工程文件下载:
链接: https://pan.baidu.com/s/1LeeIV4nVPEQRLQqsdRvNxg
提取码: 1ywb
代码存放在\project中,\project\prj 存放Vivado工程。
仿真工程存放在\testbench中,打开Modelsim运行do tb_sim.do即可。
前言
通过Verilog代码,在FPGA上搭建单片机执行汇编程序实现LED闪烁。
实现结果:
1.CPU
1.指令系统cpu_instr_dec.sv
目前只支持8种指令。
localparam [`CPU_DATA_WIDTH-1:0] MOV = 'b0001_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] SETB = 'b0010_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] CLR = 'b0011_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] AJMP = 'b0100_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] DJNZ = 'b0101_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] RET = 'b0111_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] LCALL = 'b1000_xxxx;
localparam [`CPU_DATA_WIDTH-1:0] NOP = 'b1111_xxxx;
CPU的指令系统为单片机最重要的部分,程序指令从ROM中读取出来后,CPU指令系统需要解析并判断进入下一状态。
状态机跳转如下图:
2.运算器cpu_arithmetic.sv
运算器主要执行+ - * /等运算操作,需要指令系统识别到指令后,将相应数据写入ACC或TMP,并操作ALU寄存器执行相应运算操作。
localparam [`CPU_DATA_WIDTH-1:0] ARITH_ADD = 'b0000_0001;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_SUB = 'b0000_0010;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_MUL = 'b0000_0011;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_DIV = 'b0000_0100;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_PLUS = 'b0000_1000;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_DEC = 'b0000_1001;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_AND = 'b0001_0000;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_OR = 'b0001_0001;
localparam [`CPU_DATA_WIDTH-1:0] ARITH_XOR = 'b0001_0010;
3.IO控制cpu_io.sv
IO控制主要控制P0,P1,P2,P3寄存器。
4.程序计数器cpu_pc.sv
程序计数器根据指令系统读取1条指令后发起的PC_PLUS信号进行+1操作,指令系统可对PC的值进行赋值,实现跳转,在LCALL、RET等指令是常用到。
5.片内存储器cpu_ram.sv
片内存储器主要存储IO、PC、ACC、TMP等以外的寄存器,用于指令系统操作。
2.程序
1.汇编程序
主要实现了LED闪烁的功能,如下是实现的汇编指令,通过延时180ms进行点亮和熄灭LED从而实现LED闪烁。LED接在P0.0端。
ORG 0000H
AJMP MAIN
ORG 0050H
MAIN :CLR P0.0
LCALL DELAY
AJMP L1
L1 :SETB P0.0
LCALL DELAY
AJMP MAIN
;delay 180ms in 10M clock
DELAY :MOV R0, #240
L3 :MOV R1, #240
L4 :NOP
DJNZ R1, L4
DJNZ R0, L3
RET
2.指令内存映射qwic51_rom.sv
该部分代码主要存放机器能识别的代码,如MOV首先需要翻译成0b00010000,并在接下来的1个地址空间内存放操作的地址,再接下来的1个地址空间内存放操作的数据,这样才能被指令系统cpu_instr_dec正确识别。
翻译指令需要手动翻译,如MOV R0 #2需要被翻译成:
00H:0b00010000
01H:R0地址
02H:2
如下是将上述汇编代码翻译成机器能识别的数据完整内存映射,程序计数器PC即CPU_PC_ADDR,通过PC的值来得到程序指令。
localparam MAIN = 8'h50;
localparam L1 = 8'h56;
localparam DELAY = 8'h60;
localparam L3 = 8'h63;
localparam L4 = 8'h66;
always @(posedge CPU_CLK)
begin
case(CPU_PC_ADDR)
8'h00 : cpu_ir_reg_r <= RESET_CTRL;
8'h01 : cpu_ir_reg_r <= AJMP_CTRL;
8'h02 : cpu_ir_reg_r <= MAIN;
MAIN : cpu_ir_reg_r <= CLR_CTRL + 'b0000;
MAIN + 1 : cpu_ir_reg_r <= P0;
MAIN + 2 : cpu_ir_reg_r <= LCALL_CTRL;
MAIN + 3 : cpu_ir_reg_r <= DELAY;
MAIN + 4 : cpu_ir_reg_r <= AJMP_CTRL;
MAIN + 5 : cpu_ir_reg_r <= L1;
L1 : cpu_ir_reg_r <= SETB_CTRL + 'b0000;
L1 + 1 : cpu_ir_reg_r <= P0;
L1 + 2 : cpu_ir_reg_r <= LCALL_CTRL;
L1 + 3 : cpu_ir_reg_r <= DELAY;
L1 + 4 : cpu_ir_reg_r <= AJMP_CTRL;
L1 + 5 : cpu_ir_reg_r <= MAIN;
DELAY : cpu_ir_reg_r <= MOV_CTRL;
DELAY + 1 : cpu_ir_reg_r <= R0;
DELAY + 2 : cpu_ir_reg_r <= 240;
L3 : cpu_ir_reg_r <= MOV_CTRL;
L3 + 1 : cpu_ir_reg_r <= R1;
L3 + 2 : cpu_ir_reg_r <= 240;
L4 : cpu_ir_reg_r <= NOP_CTRL;
L4 + 1 : cpu_ir_reg_r <= DJNZ_CTRL;
L4 + 2 : cpu_ir_reg_r <= R1;
L4 + 3 : cpu_ir_reg_r <= L4;
L4 + 4 : cpu_ir_reg_r <= DJNZ_CTRL;
L4 + 5 : cpu_ir_reg_r <= R0;
L4 + 6 : cpu_ir_reg_r <= L3;
L4 + 7 : cpu_ir_reg_r <= RET_CTRL;
default : cpu_ir_reg_r <= NOP_CTRL;
endcase
end
3.系统时钟
系统时钟为10MHz,通过50MHz分频得到。
未完待续
更新日志:
- 2019年12月4日:1.0
新建目录。