LoongArch CPU设计实验_实践任务6:20条指令单周期CPU
声明
由于Loongarch交叉编译工具未能成功配置,本实验使用的是exp压缩包(压缩包下载链接见文尾参考资料)。
实验要求
本实践任务要求: 1. 结合本章中讲述的设计方案,阅读并理解mycpu_env/myCPU/目录下提供的代码。 2. 完成代码调试。我们提供的代码中加入了//TODO//处bug,请大家通过仿真波形的调试修复这些bug,使设计可以通过仿真和上板验证。
请参照第2.3.1节中介绍的方式获取本次实践任务所需的实验开发环境。具体的实验环境位于 mycpu_env/ 目录下,其组织结构和使用方式已在本章4.2节介绍过,此处不再重复。
实验环境准备就绪后,请参考下列步骤完成本实践任务:
- 修改func配置文件——mycpu_env/func/include/test_config.h,选择exp6的配置,编译。(如果是通过压缩包exp6.zip获取实验开发环境的,请跳过该步骤。)
- 打开gettrace工程——mycpu_env/gettrace/gettrace.xpr。(该Vivado工程中的IP核是使用Vivado2019.2创建的,如果使用更高版本的Vivado打开,请参考附录D.4节进行IP核升级。)运行gettrace工程的仿真(进入仿真界面后,直接点击run all等待仿真运行完成),生成新的参考trace文件golden_trace.txt(mycpu_env/gettrace/golden_trace.txt)。要等仿真运行完成,golden_trace.txt才有完整的内容。(如果是通过压缩包exp6.zip获取实验开发环境的,请跳过该步骤。)
- 进入 mycpu_env/soc_verify/soc_dram/run_vivado/ 目录下启动验证myCPU的工程。如果该目录下尚未创建工程,请参照附录D.2节介绍的步骤,利用该目录下的 create_project.tcl 文件创建工程。如需要,请参考附录D.4节进行IP核升级。
- 参考本章4.2.5.2小节,对工程中的inst_ram重新定制。(如果是通过压缩包exp6.zip获取实验开发环境的,请跳过该步骤。)
- 在验证myCPU的工程中运行仿真(进入仿真界面后,直接点击run all),进行功能验证与调试,直至仿真测试通过。
- 在验证myCPU的工程中综合实现后生成bit流文件,进行上板验证。(如果无硬件实验平台,请跳过该步骤。)
代码修改
错误一
发现无法抓取错误,检查后发现是端口写错了
mycpu_top.v
// debug info generate
assign debug_wb_pc = pc;
assign debug_wb_rf_we = {4{rf_we}};
assign debug_wb_rf_wnum = dest;
assign debug_wb_rf_wdata = final_result;
错误二
mycpu_top.v
alu操作数1错误
alu u_alu(
.alu_op (alu_op ),
.alu_src1 (alu_src1 ),
.alu_src2 (alu_src2 ),
.alu_result (alu_result)
);
final_result没有定义变量
wire [31:0] final_result;
错误三
寄存器堆写回使能信号错误,bl指令需要写回
mycpu_top.v
assign gr_we = ~inst_st_w & ~inst_beq & ~inst_bne & ~inst_b;
错误四
mycpu_top.v
ui5是rk
assign imm = src2_is_4 ? 32'h4 :
need_si20 ? {i20[19:0], 12'b0} :
need_ui5 ? rk :
/*need_si12*/{{20{i12[11]}}, i12[11:0]} ;
SLL计算错误
alu.v
// SLL result
assign sll_result = alu_src1 << alu_src2; //rj << ui5
错误五
alu.v
assign or_result = alu_src1 | alu_src2;
错误六
alu.v
// SRL, SRA result
assign sr64_result = {{32{op_sra & alu_src1[31]}}, alu_src1[31:0]} >> alu_src2[4:0]; //rj >> i5
assign sr_result = sr64_result[31:0];
补充
算术右移是在左端补k个最高有效位的值。
逻辑右移在左端补k个0。
完整代码
mycpu_top.v
module mycpu_top(
input wire clk,
input wire resetn,
// inst sram interface
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,
// data sram interface
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,
// trace debug interface
output wire [31:0] debug_wb_pc,
output wire [ 3:0] debug_wb_rf_we,
output wire [ 4:0] debug_wb_rf_wnum,
output wire [31:0] debug_wb_rf_wdata
);
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
wire [31:0] seq_pc;
wire [31:0] nextpc;
wire br_taken;
wire [31:0] br_target;
wire [31:0] inst;
reg [31:0] pc;
wire [11:0] alu_op;
wire load_op;
wire src1_is_pc;
wire src2_is_imm;
wire res_from_mem;
wire dst_is_r1;
wire gr_we;
wire mem_we;
wire src_reg_is_rd;
wire [4: 0] dest;
wire [31:0] rj_value;
wire [31:0] rkd_value;
wire [31:0] imm;
wire [31:0] br_offs;
wire [31:0] jirl_offs;
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 [ 4:0] rd;
wire [ 4:0] rj;
wire [ 4:0] rk;
wire [11:0] i12;
wire [19:0] i20;
wire [15:0] i16;
wire [25:0] i26;
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 inst_add_w;
wire inst_sub_w;
wire inst_slt;
wire inst_sltu;
wire inst_nor;
wire inst_and;
wire inst_or;
wire inst_xor;
wire inst_slli_w;
wire inst_srli_w;
wire inst_srai_w;
wire inst_addi_w;
wire inst_ld_w;
wire inst_st_w;
wire inst_jirl;
wire inst_b;
wire inst_bl;
wire inst_beq;
wire inst_bne;
wire inst_lu12i_w;
wire need_ui5;
wire need_si12;
wire need_si16;
wire need_si20;
wire need_si26;
wire src2_is_4;
wire [ 4:0] rf_raddr1;
wire [31:0] rf_rdata1;
wire [ 4:0] rf_raddr2;
wire [31:0] rf_rdata2;
wire rf_we ;
wire [ 4:0] rf_waddr;
wire [31:0] rf_wdata;
wire [31:0] alu_src1 ;
wire [31:0] alu_src2 ;
wire [31:0] alu_result ;
wire [31:0] mem_result;
wire [31:0] final_result;
assign seq_pc = pc + 3'h4;
assign nextpc = br_taken ? br_target : seq_pc;
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 i20 = inst[24: 5];
assign i16 = inst[25:10];
assign i26 = {inst[ 9: 0], inst[25:10]};
decoder_6_64 u_dec0(.in(op_31_26 ), .out(op_31_26_d ));
decoder_4_16 u_dec1(.in(op_25_22 ), .out(op_25_22_d ));
decoder_2_4 u_dec2(.in(op_21_20 ), .out(op_21_20_d ));
decoder_5_32 u_dec3(.in(op_19_15 ), .out(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_sub_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h02];
assign inst_slt = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h04];
assign inst_sltu = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h05];
assign inst_nor = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h08];
assign inst_and = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h09];
assign inst_or = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0a];
assign inst_xor = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0b];
assign inst_slli_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h01];
assign inst_srli_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h09];
assign inst_srai_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h11];
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_jirl = op_31_26_d[6'h13];
assign inst_b = op_31_26_d[6'h14];
assign inst_bl = op_31_26_d[6'h15];
assign inst_beq = op_31_26_d[6'h16];
assign inst_bne = op_31_26_d[6'h17];
assign inst_lu12i_w= op_31_26_d[6'h05] & ~inst[25];
assign alu_op[ 0] = inst_add_w | inst_addi_w | inst_ld_w | inst_st_w
| inst_jirl | inst_bl;
assign alu_op[ 1] = inst_sub_w;
assign alu_op[ 2] = inst_slt;
assign alu_op[ 3] = inst_sltu;
assign alu_op[ 4] = inst_and;
assign alu_op[ 5] = inst_nor;
assign alu_op[ 6] = inst_or;
assign alu_op[ 7] = inst_xor;
assign alu_op[ 8] = inst_slli_w;
assign alu_op[ 9] = inst_srli_w;
assign alu_op[10] = inst_srai_w;
assign alu_op[11] = inst_lu12i_w;
assign need_ui5 = inst_slli_w | inst_srli_w | inst_srai_w;
assign need_si12 = inst_addi_w | inst_ld_w | inst_st_w;
assign need_si16 = inst_jirl | inst_beq | inst_bne;
assign need_si20 = inst_lu12i_w;
assign need_si26 = inst_b | inst_bl;
assign src2_is_4 = inst_jirl | inst_bl;
assign imm = src2_is_4 ? 32'h4 :
need_si20 ? {i20[19:0], 12'b0} :
need_ui5 ? rk :
/*need_si12*/{{20{i12[11]}}, i12[11:0]} ;
assign br_offs = need_si26 ? {{ 4{i26[25]}}, i26[25:0], 2'b0} :
{{14{i16[15]}}, i16[15:0], 2'b0} ;
assign jirl_offs = {{14{i16[15]}}, i16[15:0], 2'b0};
assign src_reg_is_rd = inst_beq | inst_bne | inst_st_w;
assign src1_is_pc = inst_jirl | inst_bl;
assign src2_is_imm = inst_slli_w |
inst_srli_w |
inst_srai_w |
inst_addi_w |
inst_ld_w |
inst_st_w |
inst_lu12i_w|
inst_jirl |
inst_bl ;
assign res_from_mem = inst_ld_w;
assign dst_is_r1 = inst_bl;
assign gr_we = ~inst_st_w & ~inst_beq & ~inst_bne & ~inst_b;
assign mem_we = inst_st_w;
assign dest = dst_is_r1 ? 5'd1 : rd;
assign rf_raddr1 = rj;
assign rf_raddr2 = src_reg_is_rd ? rd :rk;
regfile u_regfile(
.clk (clk ),
.raddr1 (rf_raddr1),
.rdata1 (rf_rdata1),
.raddr2 (rf_raddr2),
.rdata2 (rf_rdata2),
.we (rf_we ),
.waddr (rf_waddr ),
.wdata (rf_wdata )
);
assign rj_value = rf_rdata1;
assign rkd_value = rf_rdata2;
assign rj_eq_rd = (rj_value == rkd_value);
assign br_taken = ( inst_beq && rj_eq_rd
|| inst_bne && !rj_eq_rd
|| inst_jirl
|| inst_bl
|| inst_b
) && valid;
assign br_target = (inst_beq || inst_bne || inst_bl || inst_b) ? (pc + br_offs) :
/*inst_jirl*/ (rj_value + jirl_offs);
assign alu_src1 = src1_is_pc ? pc : rj_value;
assign alu_src2 = src2_is_imm ? imm : rkd_value;
alu u_alu(
.alu_op (alu_op ),
.alu_src1 (alu_src1 ),
.alu_src2 (alu_src2 ),
.alu_result (alu_result)
);
assign data_sram_we = mem_we && valid;
assign data_sram_addr = alu_result;
assign data_sram_wdata = rkd_value;
assign mem_result = data_sram_rdata;
assign final_result = res_from_mem ? mem_result : alu_result;
assign rf_we = gr_we && valid;
assign rf_waddr = dest;
assign rf_wdata = final_result;
// debug info generate
assign debug_wb_pc = pc;
assign debug_wb_rf_we = {4{rf_we}};
assign debug_wb_rf_wnum = dest;
assign debug_wb_rf_wdata = final_result;
endmodule
alu.v
module alu(
input wire [11:0] alu_op,
input wire [31:0] alu_src1,
input wire [31:0] alu_src2,
output wire [31:0] alu_result
);
wire op_add; //add operation
wire op_sub; //sub operation
wire op_slt; //signed compared and set less than
wire op_sltu; //unsigned compared and set less than
wire op_and; //bitwise and
wire op_nor; //bitwise nor
wire op_or; //bitwise or
wire op_xor; //bitwise xor
wire op_sll; //logic left shift
wire op_srl; //logic right shift
wire op_sra; //arithmetic right shift
wire op_lui; //Load Upper Immediate
// control code decomposition
assign op_add = alu_op[ 0];
assign op_sub = alu_op[ 1];
assign op_slt = alu_op[ 2];
assign op_sltu = alu_op[ 3];
assign op_and = alu_op[ 4];
assign op_nor = alu_op[ 5];
assign op_or = alu_op[ 6];
assign op_xor = alu_op[ 7];
assign op_sll = alu_op[ 8];
assign op_srl = alu_op[ 9];
assign op_sra = alu_op[10];
assign op_lui = alu_op[11];
wire [31:0] add_sub_result;
wire [31:0] slt_result;
wire [31:0] sltu_result;
wire [31:0] and_result;
wire [31:0] nor_result;
wire [31:0] or_result;
wire [31:0] xor_result;
wire [31:0] lui_result;
wire [31:0] sll_result;
wire [63:0] sr64_result;
wire [31:0] sr_result;
// 32-bit adder
wire [31:0] adder_a;
wire [31:0] adder_b;
wire adder_cin;
wire [31:0] adder_result;
wire adder_cout;
assign adder_a = alu_src1;
assign adder_b = (op_sub | op_slt | op_sltu) ? ~alu_src2 : alu_src2; //src1 - src2 rj-rk
assign adder_cin = (op_sub | op_slt | op_sltu) ? 1'b1 : 1'b0;
assign {adder_cout, adder_result} = adder_a + adder_b + adder_cin;
// ADD, SUB result
assign add_sub_result = adder_result;
// SLT result
assign slt_result[31:1] = 31'b0; //rj < rk 1
assign slt_result[0] = (alu_src1[31] & ~alu_src2[31])
| ((alu_src1[31] ~^ alu_src2[31]) & adder_result[31]);
// SLTU result
assign sltu_result[31:1] = 31'b0;
assign sltu_result[0] = ~adder_cout;
// bitwise operation
assign and_result = alu_src1 & alu_src2;
assign or_result = alu_src1 | alu_src2;
assign nor_result = ~or_result;
assign xor_result = alu_src1 ^ alu_src2;
assign lui_result = alu_src2;
// SLL result
assign sll_result = alu_src1 << alu_src2[4:0]; //rj << ui5
// SRL, SRA result
assign sr64_result = {{32{op_sra & alu_src1[31]}}, alu_src1[31:0]} >> alu_src2[4:0]; //rj >> i5
assign sr_result = sr64_result[31:0];
// final result mux
assign alu_result = ({32{op_add|op_sub}} & add_sub_result)
| ({32{op_slt }} & slt_result)
| ({32{op_sltu }} & sltu_result)
| ({32{op_and }} & and_result)
| ({32{op_nor }} & nor_result)
| ({32{op_or }} & or_result)
| ({32{op_xor }} & xor_result)
| ({32{op_lui }} & lui_result)
| ({32{op_sll }} & sll_result)
| ({32{op_srl|op_sra}} & sr_result);
endmodule
参考资料
[1] CPU设计实战(汪文祥)第四章
[2] LoongArch CPU设计实验_实践任务6
[3] CDP_EDE_local
[4] 龙芯架构32位精简版参考手册
[5] exp实验发布包下载地址