实践任务5:5条指令单周期CPU
本实践任务要求:
阅读并理解实验环境中提供的代码,补充代码中缺失的部分,使设计可以通过仿真和上板验证。
请参照第2.3.1节中介绍的方式获取本次实践任务所需的实验开发环境。具体的实验环境位于 mini_env/ 目录下,其组织结构和使用方式已在本章4.1节介绍过,此处不再重复。
实验环境准备就绪后,请参考下列步骤完成本实践任务:
- 在 minicpu_env/soc_verify/run_vivado/ 目录下打开miniCPU工程。如需要,请参考附录D.4节进行工程和IP核升级。
- 对miniCPU工程中的inst_ram重新定制,选择对应func的coe文件(minicpu_env/func/inst_ram.coe)。
- 运行miniCPU工程的仿真(进入仿真界面后,直接点击run all),开始调试。可以修改 minicpu_env/soc_verify/testbench/ 目录下的minicpu_tb.v文件中的switch值观察led输出值是否符合预期(每次修改switch值之后都要重新仿真)。(因为本实验的测试程序为斐波那契数程序,斐波那契数列是:0,1,1,2,3,5,……从第三项开始,每一项都等于前两项之和。规定数列第三项为f(1),即f(1)=1,f(2)=2,f(3)=3,f(4)=5, …… 。修改拨码开关switch值相当于修改n,led输出值对应f(n)。
- myCPU仿真通过后,综合实现后生成bit流文件,进行上板验证。(如果无硬件实验平台,请跳过该步骤。)
指令格式
inst_ram
1c000000: addi.w $t0,$zero,0x0 //置第1项的0
1c000004: addi.w $t1,$zero,0x1 //置第2项的1
1c000008: addi.w $s0,$zero,0x0 //循环变量i初始化为0
1c00000c: addi.w $s1,$zero,0x1 //循环的步长置为1
1c000010: ld.w $a0,$zero,1024 //读取拨码开关输入的终止值
loop:
1c000014: add.w $t2,$t0,$t1 //f(i) = f(i-2) + f(i-1)
1c000018: addi.w $t0,$t1,0x0 //记录f(i-1)
1c00001c: addi.w $t1,$t2,0x0 //记录f(i)
1c000020: add.w $s0,$s0,$s1 //i++
1c000024: bne $s0,$a0,loop //if i!=n, goto loop
1c000028: st.w $t2,$zero,1028 //将f(n)的值输出到数码管上
end:
1c00002c: bne $s1, $zero, end //测试完毕,进入死循环
代码
module minicpu_top(
input wire clk,
input wire resetn,
output wire inst_sram_we,
output wire [31:0] inst_sram_addr,
output wire [31:0] inst_sram_wdata,
input wire [31:0] inst_sram_rdata,
output wire data_sram_we,
output wire [31:0] data_sram_addr,
output wire [31:0] data_sram_wdata,
input wire [31:0] data_sram_rdata
);
reg reset;
always @(posedge clk) reset <= ~resetn;
reg valid;
always @(posedge clk) begin
if (reset) begin
valid <= 1'b0;
end
else begin
valid <= 1'b1;
end
end
reg [31:0] pc;
wire [31:0] nextpc;
wire [31:0] inst;
wire [ 5:0] op_31_26;
wire [ 3:0] op_25_22;
wire [ 1:0] op_21_20;
wire [ 4:0] op_19_15;
wire [63:0] op_31_26_d;
wire [15:0] op_25_22_d;
wire [ 3:0] op_21_20_d;
wire [31:0] op_19_15_d;
wire [ 4:0] rd;
wire [ 4:0] rj;
wire [ 4:0] rk;
wire [11:0] i12;
wire [15:0] i16;
wire [15:0] i16_2;
wire inst_add_w;
wire inst_addi_w;
wire inst_ld_w;
wire inst_st_w;
wire inst_bne;
wire src2_is_imm;
wire res_from_mem;
wire gr_we;
wire mem_we;
wire src_reg_is_rd;
wire [31:0] rj_value;
wire [31:0] rkd_value;
wire [ 4:0] rf_raddr1;
wire [ 4:0] rf_raddr2;
wire [ 4:0] rf_waddr;
wire [31:0] rf_wdata;
wire br_taken;
wire rj_eq_rd;
wire [31:0] br_offs;
wire [31:0] br_target;
wire [31:0] imm;
wire [31:0] alu_src1;
wire [31:0] alu_src2;
wire [31:0] alu_result;
always @(posedge clk) begin
if (reset) begin
pc <= 32'h1bfffffc; //trick: to make nextpc be 0x1c000000 during reset
end
else begin
pc <= nextpc;
end
end
assign inst_sram_we = 1'b0;
assign inst_sram_addr = pc;
assign inst_sram_wdata = 32'b0;
assign inst = inst_sram_rdata;
assign op_31_26 = inst[31:26];
assign op_25_22 = inst[25:22];
assign op_21_20 = inst[21:20];
assign op_19_15 = inst[19:15];
assign rd = inst[ 4: 0];
assign rj = inst[ 9: 5];
assign rk = inst[14:10];
assign i12 = inst[21:10];
assign i16 = inst[25:10];
assign i16_2 = i16 << 2;
decoder_6_64 u_dec0(.in(op_31_26 ), .co(op_31_26_d ));
decoder_4_16 u_dec1(.in(op_25_22 ), .co(op_25_22_d ));
decoder_2_4 u_dec2(.in(op_21_20 ), .co(op_21_20_d ));
decoder_5_32 u_dec3(.in(op_19_15 ), .co(op_19_15_d ));
assign inst_add_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h00];
assign inst_addi_w = op_31_26_d[6'h00] & op_25_22_d[4'ha];
assign inst_ld_w = op_31_26_d[6'h0a] & op_25_22_d[4'h2];
assign inst_st_w = op_31_26_d[6'h0a] & op_25_22_d[4'h6];
assign inst_bne = op_31_26_d[6'h17];
assign src2_is_imm = inst_addi_w | inst_ld_w | inst_st_w;//在这里实现立即数选择信号
assign res_from_mem = inst_ld_w;
assign gr_we = inst_add_w | inst_ld_w | inst_addi_w;
assign mem_we = inst_st_w;
assign src_reg_is_rd = inst_bne | inst_st_w;
assign rf_raddr1 = rj;
assign rf_raddr2 = src_reg_is_rd ? rd : rk;
regfile u_regfile(
.clk (clk ),
.raddr1 (rf_raddr1),
.rdata1 (rj_value ),
.raddr2 (rf_raddr2),
.rdata2 (rkd_value),
.we (gr_we ),
.waddr (rf_waddr ),
.wdata (rf_wdata )
);//在空出的括号里完成引脚匹配
assign br_offs = {{14{i16[15]}}, i16, 2'b00};//在这里完成br_offs信号的生成
assign br_target = pc + br_offs;
assign rj_eq_rd = (rj_value == rkd_value);
assign br_taken = valid && inst_bne && !rj_eq_rd;
assign nextpc = br_taken ? br_target : pc + 4;//在这里实现nextpc信号的生成
// se si12
assign imm = {{20{i12[11]}},i12[11:0]};
assign alu_src1 = rj_value;
assign alu_src2 = src2_is_imm ? imm : rkd_value;//在这里实现alu_src2信号
assign alu_result = alu_src1 + alu_src2;
assign data_sram_we = mem_we;
assign data_sram_addr = alu_result;
assign data_sram_wdata = rkd_value;
assign rf_waddr = rd;
assign rf_wdata = res_from_mem ? data_sram_rdata : alu_result;//在这里完成写回寄存器值的选择
endmodule
仿真
led低电平
参考资料
[1] CPU设计实战(汪文祥)第四章
[2] LoongArch CPU设计实验_实践任务5
[3] CDP_EDE_local
[4] 龙芯架构32位精简版参考手册