Compare commits

3 Commits
Author SHA1 Message Date
Anccend ccd1b917d0 新增 ISA 验证资产:迷你汇编器 + 指令测试程序
tools/rv_asm.py
- 极简 RISC-V 汇编器(Python 3,无第三方依赖),支持
  add/sub/and/or/xor/sll/srl/sra/slt/sltu、mul/mulh/mulhsu/mulhu、
  addi/slti/sltiu/xori/ori/andi、slli/srli/srai、lw/sw、
  beq/bne/blt/bge/bltu/bgeu、lui、jal/jalr、ecall、标签与 # 注释;
- 提供 print / halt 两条双字伪指令(分别等价于 addi a7,zero,34 + ecall、
  addi a7,zero,10 + ecall);
- I 型与访存立即数做 12 位有符号范围检查,越界直接报错,避免静默截断;
- 除 hex 外同时输出 .lst 编码清单,便于与 .asm 逐行核对。

testbench/programs/isa_test.asm + isa_test.expected
- 指令扩展验证程序:sltiu(两个方向)、xor/sll/srl/sra、mul/mulh/mulhu/mulhsu、
  bltu/bge/bgeu 的有符号与无符号区分,以及 bge/bgeu/bltu 三个循环,
  共 24 项 LED 输出,期望值逐行保存在 isa_test.expected。

testbench/programs/lui_test.asm
- LUI 验证程序:0x12345 / 0xfffff / 0x80000 与 lui+addi 组合,共 4 项输出。

另外包含编辑器/格式化工具对 cpu21_riscv_alu.v、cpu21_riscv_branch_unit.v、
cpu21_riscv_decoder.v 的重排(纯格式,语义等价)。

验证(xsim + testbench/tb_no_intr.v,按提交后的正式路径生成 hex)
- isa_test:24 项 LED 输出与 isa_test.expected 逐项一致,143 周期后 halt 停机;
- lui_test:12345000 / fffff000 / 80000000 / 12345678,21 周期后停机;
- 重新编译无 error / warning。
2026-09-12 19:10:24 +08:00
Anccend 9f6f4c9ada ISA 扩展:sltiu、bge/bgeu、mulh/mulhsu/mulhu(并回归 xor/sll/srl/sra/bltu/mul)
cpu21_riscv_alu.v
- 新增 ALU_MULH(4'd13)、ALU_MULHSU(4'd14):用 64 位有符号乘积
  mult_ss = $signed(a)*$signed(b)、mult_su = $signed(a)*$signed({32'b0,b})
  取高 32 位;mulhu 复用 ALU_MUL 的 result2(无符号乘积高 32 位),
  与课程约定的 remu 共用 wb_sel=3 通道,因此 ALU 操作码仍为 4 位。

cpu21_riscv_decoder.v
- OP_I 新增 funct3=011 -> sltiu(立即数符号扩展后无符号比较);
- OP_R 的 funct7=0000001 分支改为完整 M 扩展译码:
  000=mul、001=mulh、010=mulhsu、011=mulhu,并保留 111=remu(wb_sel=3);
- OP_BRANCH 新增 funct3=101(bge)、111(bgeu),新增 bge_o/bgeu_o 输出,
  ALU_OP 仍按比较类型给出 SLT/SLTU/SUB。

cpu21_riscv_branch_unit.v
- 新增 bge_i、bgeu_i;把四种比较预计算成短 wire(有符号 <、>= 与无符号 <、>=)
  再组合出 branch_taken,顺带修掉之前被自动换行拆散的 $signed 表达式。

cpu21_riscv_redirect_int_bpb.v
- 新增 d_bge/d_bgeu 连线与 idex_bge_q/idex_bgeu_q 流水寄存器(含复位与捕获);
- IF 级 f_is_branch 纳入 funct3=101/111,使 bge/bgeu 也参与 BPB 预测。

说明文档
- 功能列表与编码表补充 sltiu、bge/bgeu 与 M 扩展乘法。

验证(xsim + tb_no_intr;测试程序由临时小汇编器生成,共 24 项 LED 输出逐项比对)
- sltiu:1 <u 0xffffffff = 00000001;0xffffffff <u 1 = 00000000
- xor:0xff ^ 0xffffffff = ffffff00
- sll:1 << 31 = 80000000;srl:0xffffffff >> 28 = 0000000f;sra:同左 = ffffffff
- mul:1234*567 = 000aad1e;mulh:(-1)*(-1) 高 = 00000000
  mulhu:0xffffffff^2 高 = fffffffe;mulhsu:(-1)*2 高 = ffffffff
- bltu:0 <u 0xffffffff 成立(取跳转分支);bge:(-1) >= 0 不成立(走 fall-through);
  bgeu:0xffffffff >=u 0 成立
- bge / bgeu / bltu 三个循环计数正确(fffffffd..00000000 / 3..1 / 4..1),最后 halt 停机
  (143 周期,halted=1)
- 回归:中断测试程序 6003 周期统计与改动前逐项一致
  (stalls=11 bubbles=297 cond_taken=648 pred_ok=663 pred_fail=102,
  IRQ1@cyc85、IRQ2@cyc266、IRQ3@cyc447);
  benchmark 的 BLT 段落仍连续输出 fffffff1..ffffffff,
  60000 周期统计与改动前完全一致(stalls=3195 bubbles=6955 cond_taken=8857
  pred_ok=9455 pred_fail=1050)。
2026-09-12 19:02:24 +08:00
Anccend 036a57d907 实现 LUI 指令(加载高位立即数)
- cpu21_riscv_alu.v:新增 ALU_PASSB(4'd15),直接把 B 端送到 result;
- cpu21_riscv_decoder.v:新增 OP_LUI(IR[6:2]=0x0d,即标准 RISC-V opcode 0x37),
  imm = {IR[31:12], 12'b0},用 ALU_SRC 选择立即数、ALU 直通输出写回 rd,
  不读源寄存器(uses_rs1/rs2 保持 0);
- 说明文档补充 lui 的功能说明与编码表行。

验证(xsim + tb_no_intr,自编的小汇编器生成测试程序):
依次输出 12345000 / fffff000 / 80000000,以及 lui+addi 组合得到 12345678,
随后 a7=10 停机(共 21 周期,halted=1),无编译错误与警告。
2026-09-12 18:59:04 +08:00
9 changed files with 492 additions and 44 deletions
+13 -2
View File
@@ -4,7 +4,8 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ALU:纯组合运算单元。 // ALU:纯组合运算单元。
// //
// result 为主结果;result2 为第二结果,用于 MUL 的高 32 位与 DIVU 的余数。 // result 为主结果;result2 为第二结果(乘法的无符号高 32 位 / DIVU 的余数),
// 由译码器用 wb_sel=3 选取,用于无符号高位乘法 mulhu 与课程约定的 remu。
// 除法做了除零保护:b==0 时商为 32'hffff_ffff、余数为被除数。 // 除法做了除零保护:b==0 时商为 32'hffff_ffff、余数为被除数。
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
module cpu21_riscv_alu ( module cpu21_riscv_alu (
@@ -27,10 +28,17 @@ module cpu21_riscv_alu (
localparam ALU_NOR = 4'd10; localparam ALU_NOR = 4'd10;
localparam ALU_SLT = 4'd11; localparam ALU_SLT = 4'd11;
localparam ALU_SLTU = 4'd12; localparam ALU_SLTU = 4'd12;
reg [63:0] mult_result; localparam ALU_MULH = 4'd13;
localparam ALU_MULHSU = 4'd14;
localparam ALU_PASSB = 4'd15; // 直接输出 B 端(供 LUI 使用)
reg [63:0] mult_result; // 无符号 32x32 乘积
reg signed [63:0] mult_ss; // 有符号 x 有符号
reg signed [63:0] mult_su; // 有符号 x 无符号
always @* begin always @* begin
mult_result = a * b; mult_result = a * b;
mult_ss = $signed(a) * $signed(b);
mult_su = $signed(a) * $signed({32'b0, b});
result2 = 32'b0; result2 = 32'b0;
case (op) case (op)
ALU_SLL: result = a << b[4:0]; ALU_SLL: result = a << b[4:0];
@@ -50,8 +58,11 @@ module cpu21_riscv_alu (
ALU_OR: result = a | b; ALU_OR: result = a | b;
ALU_XOR: result = a ^ b; ALU_XOR: result = a ^ b;
ALU_NOR: result = ~(a | b); ALU_NOR: result = ~(a | b);
ALU_MULH: result = mult_ss[63:32];
ALU_MULHSU: result = mult_su[63:32];
ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0; ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0;
ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0; ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0;
ALU_PASSB: result = b;
default: result = 32'b0; default: result = 32'b0;
endcase endcase
end end
+13 -6
View File
@@ -18,7 +18,9 @@ module cpu21_riscv_branch_unit (
input wire beq_i, input wire beq_i,
input wire bne_i, input wire bne_i,
input wire blt_i, input wire blt_i,
input wire bge_i,
input wire bltu_i, input wire bltu_i,
input wire bgeu_i,
input wire jal_i, input wire jal_i,
input wire jalr_i, input wire jalr_i,
// IF 级携带过来的预测信息 // IF 级携带过来的预测信息
@@ -31,15 +33,20 @@ module cpu21_riscv_branch_unit (
output wire redirect_valid_o, output wire redirect_valid_o,
output wire [31:0] redirect_pc_o output wire [31:0] redirect_pc_o
); );
// 条件分支裁决:beq 相等、bne 不等、blt 有符号小于、bltu 无符号小于。 // 比较结果预先算好,避免表达式过长被自动换行拆散。
wire lt_signed = $signed(src1_i) < $signed(src2_i);
wire ge_signed = $signed(src1_i) >= $signed(src2_i);
wire lt_unsigned = src1_i < src2_i;
wire ge_unsigned = src1_i >= src2_i;
// 条件分支裁决:beq 相等、bne 不等、blt/bge 有符号比较、bltu/bgeu 无符号比较。
assign branch_taken_o = branch_i && assign branch_taken_o = branch_i &&
((beq_i && (src1_i == src2_i)) || ((beq_i && (src1_i == src2_i)) ||
(bne_i && (src1_i != src2_i)) || (bne_i && (src1_i != src2_i)) ||
(blt_i && ($signed( (blt_i && lt_signed) ||
src1_i (bge_i && ge_signed) ||
) < $signed( (bltu_i && lt_unsigned) ||
src2_i (bgeu_i && ge_unsigned));
))) || (bltu_i && (src1_i < src2_i)));
// 跳转目标:jalr 为 (rs1+imm) 且最低位清零;jal 为 PC+imm。 // 跳转目标:jalr 为 (rs1+imm) 且最低位清零;jal 为 PC+imm。
assign target_o = jalr_i ? ((src1_i + imm_i) & 32'hffff_fffe) : (pc_i + imm_i); assign target_o = jalr_i ? ((src1_i + imm_i) & 32'hffff_fffe) : (pc_i + imm_i);
+44 -11
View File
@@ -29,7 +29,9 @@ module cpu21_riscv_decoder (
output reg beq_o, output reg beq_o,
output reg bne_o, output reg bne_o,
output reg blt_o, output reg blt_o,
output reg bge_o,
output reg bltu_o, output reg bltu_o,
output reg bgeu_o,
output reg jal_o, output reg jal_o,
output reg jalr_o, output reg jalr_o,
output reg ecall_o, output reg ecall_o,
@@ -47,6 +49,8 @@ module cpu21_riscv_decoder (
localparam OP_BRANCH = 5'h18; localparam OP_BRANCH = 5'h18;
localparam OP_JAL = 5'h1b; localparam OP_JAL = 5'h1b;
localparam OP_SYS = 5'h1c; localparam OP_SYS = 5'h1c;
// LUI 为扩展指令(标准 RISC-V opcode 0x37 -> IR[6:2]=0x0d)。
localparam OP_LUI = 5'h0d;
localparam ALU_SLL = 4'd0; localparam ALU_SLL = 4'd0;
localparam ALU_SRA = 4'd1; localparam ALU_SRA = 4'd1;
@@ -60,6 +64,9 @@ module cpu21_riscv_decoder (
localparam ALU_XOR = 4'd9; localparam ALU_XOR = 4'd9;
localparam ALU_SLT = 4'd11; localparam ALU_SLT = 4'd11;
localparam ALU_SLTU = 4'd12; localparam ALU_SLTU = 4'd12;
localparam ALU_MULH = 4'd13;
localparam ALU_MULHSU = 4'd14;
localparam ALU_PASSB = 4'd15; // LUI:直接输出 B 端(立即数)
wire [4:0] opcode = ir_i[6:2]; wire [4:0] opcode = ir_i[6:2];
wire [2:0] funct3 = ir_i[14:12]; wire [2:0] funct3 = ir_i[14:12];
@@ -87,7 +94,9 @@ module cpu21_riscv_decoder (
beq_o = 1'b0; beq_o = 1'b0;
bne_o = 1'b0; bne_o = 1'b0;
blt_o = 1'b0; blt_o = 1'b0;
bge_o = 1'b0;
bltu_o = 1'b0; bltu_o = 1'b0;
bgeu_o = 1'b0;
jal_o = 1'b0; jal_o = 1'b0;
jalr_o = 1'b0; jalr_o = 1'b0;
ecall_o = 1'b0; ecall_o = 1'b0;
@@ -102,13 +111,23 @@ module cpu21_riscv_decoder (
uses_rs1_o = 1'b1; uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1; uses_rs2_o = 1'b1;
reg_write_o = 1'b1; reg_write_o = 1'b1;
// 电路中额外的 REMU 控制对应标准 R 型的 funct7=1/funct3=111 // M 扩展(funct7=0000001):mul/mulh/mulhsu/mulhu,外加课程约定的 remu
// 形式;此时 ALU 的 result2 即为余数。 // (funct3=111 形式)。mulhu 与 remu 都取 ALU 的 result2(wb_sel=3)。
if ((funct7 == 7'b0000001) && (funct3 == 3'b111)) begin if (funct7 == 7'b0000001) begin
case (funct3)
3'b000: alu_op_o = ALU_MUL; // mul:无符号乘积低 32 位
3'b001: alu_op_o = ALU_MULH; // mulh:有符号乘积高 32 位
3'b010: alu_op_o = ALU_MULHSU; // mulhsu:有符号 x 无符号高 32 位
3'b011: begin // mulhu:无符号乘积高 32 位
alu_op_o = ALU_MUL;
wb_sel_o = 3'd3;
end
3'b111: begin // remu(课程约定):取余数
alu_op_o = ALU_DIVU; alu_op_o = ALU_DIVU;
wb_sel_o = 3'd3; wb_sel_o = 3'd3;
end else if ((funct7 == 7'b0000001) && (funct3 == 3'b000)) begin end
alu_op_o = ALU_MUL; default: reg_write_o = 1'b0;
endcase
end else begin end else begin
// 标准 R 型 funct3 译码;add/sub 与 sra/srl 由 funct7[5] 区分。 // 标准 R 型 funct3 译码;add/sub 与 sra/srl 由 funct7[5] 区分。
case (funct3) case (funct3)
@@ -125,7 +144,7 @@ module cpu21_riscv_decoder (
end end
end end
// I 型运算(addi/slli/slti/xori/srai/srli/ori/andi)。 // I 型运算(addi/slli/slti/sltiu/xori/srai/srli/ori/andi)。
OP_I: begin OP_I: begin
uses_rs1_o = 1'b1; uses_rs1_o = 1'b1;
alu_src_o = 1'b1; alu_src_o = 1'b1;
@@ -134,6 +153,7 @@ module cpu21_riscv_decoder (
3'b000: alu_op_o = ALU_ADD; // addi:立即数加 3'b000: alu_op_o = ALU_ADD; // addi:立即数加
3'b001: alu_op_o = ALU_SLL; // slli:立即数逻辑左移 3'b001: alu_op_o = ALU_SLL; // slli:立即数逻辑左移
3'b010: alu_op_o = ALU_SLT; // slti:有符号小于置 1 3'b010: alu_op_o = ALU_SLT; // slti:有符号小于置 1
3'b011: alu_op_o = ALU_SLTU; // sltiu:无符号小于置 1(立即数符号扩展)
3'b100: alu_op_o = ALU_XOR; // xori:立即数异或 3'b100: alu_op_o = ALU_XOR; // xori:立即数异或
3'b101: 3'b101:
alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL); // srai/srli:立即数算术/逻辑右移 alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL); // srai/srli:立即数算术/逻辑右移
@@ -143,6 +163,16 @@ module cpu21_riscv_decoder (
endcase endcase
end end
// 加载高位立即数 lui:rd = imm[31:12] << 12。
// 不读寄存器;用 ALU_SRC 选立即数,ALU 直通 B 端输出。
OP_LUI: begin
reg_write_o = 1'b1;
alu_src_o = 1'b1;
alu_op_o = ALU_PASSB;
imm_o = {ir_i[31:12], 12'b0};
wb_sel_o = 3'd0;
end
// 加载指令,当前仅支持 lw(funct3=010)。 // 加载指令,当前仅支持 lw(funct3=010)。
OP_LOAD: begin OP_LOAD: begin
if (funct3 == 3'b010) begin if (funct3 == 3'b010) begin
@@ -168,19 +198,22 @@ module cpu21_riscv_decoder (
end end
end end
// 条件分支:beq(000)/bne(001)/blt(100,有符号小于)/bltu(110,无符号小于)。 // 条件分支:beq(000)/bne(001)/blt(100)/bge(101)/bltu(110)/bgeu(111)。
OP_BRANCH: begin OP_BRANCH: begin
if ((funct3 == 3'b000) || (funct3 == 3'b001) || if ((funct3 == 3'b000) || (funct3 == 3'b001) || (funct3 == 3'b100) ||
(funct3 == 3'b100) || (funct3 == 3'b110)) begin (funct3 == 3'b101) || (funct3 == 3'b110) || (funct3 == 3'b111)) begin
uses_rs1_o = 1'b1; uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1; uses_rs2_o = 1'b1;
branch_o = 1'b1; branch_o = 1'b1;
beq_o = (funct3 == 3'b000); beq_o = (funct3 == 3'b000);
bne_o = (funct3 == 3'b001); bne_o = (funct3 == 3'b001);
blt_o = (funct3 == 3'b100); blt_o = (funct3 == 3'b100);
bge_o = (funct3 == 3'b101);
bltu_o = (funct3 == 3'b110); bltu_o = (funct3 == 3'b110);
// 真值表:beq/bne 用减法比较,blt 用 ALU_SLT,bltu 用 ALU_SLTU。 bgeu_o = (funct3 == 3'b111);
alu_op_o = blt_o ? ALU_SLT : (bltu_o ? ALU_SLTU : ALU_SUB); // 有符号比较用 ALU_SLT、无符号比较用 ALU_SLTU,相等/不等用减法;
// 分支是否成立由 EX 级的分支裁决单元决定,这里只与真值表保持一致。
alu_op_o = (blt_o || bge_o) ? ALU_SLT : ((bltu_o || bgeu_o) ? ALU_SLTU : ALU_SUB);
imm_o = {{19{ir_i[31]}}, ir_i[31], ir_i[7], ir_i[30:25], ir_i[11:8], 1'b0}; imm_o = {{19{ir_i[31]}}, ir_i[31], ir_i[7], ir_i[30:25], ir_i[11:8], 1'b0};
end end
end end
+14 -1
View File
@@ -130,7 +130,9 @@ module cpu21_riscv_redirect_int_bpb #(
reg idex_beq_q; reg idex_beq_q;
reg idex_bne_q; reg idex_bne_q;
reg idex_blt_q; reg idex_blt_q;
reg idex_bge_q;
reg idex_bltu_q; reg idex_bltu_q;
reg idex_bgeu_q;
reg idex_jal_q; reg idex_jal_q;
reg idex_jalr_q; reg idex_jalr_q;
reg idex_ecall_q; reg idex_ecall_q;
@@ -182,7 +184,8 @@ module cpu21_riscv_redirect_int_bpb #(
wire [2:0] f_funct3 = instr_i[14:12]; wire [2:0] f_funct3 = instr_i[14:12];
wire f_is_branch = (f_opcode == OP_BRANCH) && wire f_is_branch = (f_opcode == OP_BRANCH) &&
((f_funct3 == 3'b000) || (f_funct3 == 3'b001) || ((f_funct3 == 3'b000) || (f_funct3 == 3'b001) ||
(f_funct3 == 3'b100) || (f_funct3 == 3'b110)); (f_funct3 == 3'b100) || (f_funct3 == 3'b101) ||
(f_funct3 == 3'b110) || (f_funct3 == 3'b111));
wire [31:0] bpb_predict_target; wire [31:0] bpb_predict_target;
wire bpb_predict_hit; wire bpb_predict_hit;
wire bpb_predict_taken; wire bpb_predict_taken;
@@ -239,7 +242,9 @@ module cpu21_riscv_redirect_int_bpb #(
wire d_beq; wire d_beq;
wire d_bne; wire d_bne;
wire d_blt; wire d_blt;
wire d_bge;
wire d_bltu; wire d_bltu;
wire d_bgeu;
wire d_jal; wire d_jal;
wire d_jalr; wire d_jalr;
wire d_ecall; wire d_ecall;
@@ -268,7 +273,9 @@ module cpu21_riscv_redirect_int_bpb #(
.beq_o (d_beq), .beq_o (d_beq),
.bne_o (d_bne), .bne_o (d_bne),
.blt_o (d_blt), .blt_o (d_blt),
.bge_o (d_bge),
.bltu_o (d_bltu), .bltu_o (d_bltu),
.bgeu_o (d_bgeu),
.jal_o (d_jal), .jal_o (d_jal),
.jalr_o (d_jalr), .jalr_o (d_jalr),
.ecall_o (d_ecall), .ecall_o (d_ecall),
@@ -377,7 +384,9 @@ module cpu21_riscv_redirect_int_bpb #(
.beq_i (idex_beq_q), .beq_i (idex_beq_q),
.bne_i (idex_bne_q), .bne_i (idex_bne_q),
.blt_i (idex_blt_q), .blt_i (idex_blt_q),
.bge_i (idex_bge_q),
.bltu_i (idex_bltu_q), .bltu_i (idex_bltu_q),
.bgeu_i (idex_bgeu_q),
.jal_i (idex_jal_q), .jal_i (idex_jal_q),
.jalr_i (idex_jalr_q), .jalr_i (idex_jalr_q),
.pred_taken_i (idex_pred_taken_q), .pred_taken_i (idex_pred_taken_q),
@@ -548,7 +557,9 @@ module cpu21_riscv_redirect_int_bpb #(
idex_beq_q <= 1'b0; idex_beq_q <= 1'b0;
idex_bne_q <= 1'b0; idex_bne_q <= 1'b0;
idex_blt_q <= 1'b0; idex_blt_q <= 1'b0;
idex_bge_q <= 1'b0;
idex_bltu_q <= 1'b0; idex_bltu_q <= 1'b0;
idex_bgeu_q <= 1'b0;
idex_jal_q <= 1'b0; idex_jal_q <= 1'b0;
idex_jalr_q <= 1'b0; idex_jalr_q <= 1'b0;
idex_ecall_q <= 1'b0; idex_ecall_q <= 1'b0;
@@ -654,7 +665,9 @@ module cpu21_riscv_redirect_int_bpb #(
idex_beq_q <= d_beq; idex_beq_q <= d_beq;
idex_bne_q <= d_bne; idex_bne_q <= d_bne;
idex_blt_q <= d_blt; idex_blt_q <= d_blt;
idex_bge_q <= d_bge;
idex_bltu_q <= d_bltu; idex_bltu_q <= d_bltu;
idex_bgeu_q <= d_bgeu;
idex_jal_q <= d_jal; idex_jal_q <= d_jal;
idex_jalr_q <= d_jalr; idex_jalr_q <= d_jalr;
idex_ecall_q <= d_ecall; idex_ecall_q <= d_ecall;
+9 -4
View File
@@ -10,7 +10,7 @@
- 五级流水:IF、ID、EX、MEM、WB。 - 五级流水:IF、ID、EX、MEM、WB。
- EX/MEM 与 MEM/WB 前递,以及 load-use 一拍停顿。 - EX/MEM 与 MEM/WB 前递,以及 load-use 一拍停顿。
- 条件分支 `beq`、`bne`、`blt`(有符号小于)、`bltu`(无符号小于)在 EX 段判定,错误预测时清空 IF/ID、ID/EX 并重定向 PC。真值表中 BLT 的 `ALU_OP` 为 SLT,与实现一致。 - 条件分支 `beq`、`bne`、`blt`/`bge`(有符号)、`bltu`/`bgeu`(无符号)在 EX 段判定,错误预测时清空 IF/ID、ID/EX 并重定向 PC。真值表中 BLT 的 `ALU_OP` 为 SLT,与实现一致。
- `jal`、`jalr` 在 EX 段重定向,返回地址写回 `rd`。 - `jal`、`jalr` 在 EX 段重定向,返回地址写回 `rd`。
- 8 项全相联 BPB:标签 `PC[11:2]`,每项包含 valid、目标 PC、2 位饱和计数器和 3 位年龄字段。BPB 只由条件分支更新,避免 `jal`、`jalr` 占用不会被查询的表项;未命中时优先使用无效项,否则替换年龄最大的项。 - 8 项全相联 BPB:标签 `PC[11:2]`,每项包含 valid、目标 PC、2 位饱和计数器和 3 位年龄字段。BPB 只由条件分支更新,避免 `jal`、`jalr` 占用不会被查询的表项;未命中时优先使用无效项,否则替换年龄最大的项。
- 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。 - 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。
@@ -18,6 +18,9 @@
- CSRRSI、CSRRCI,以及 CSRRW:支持 CSR 地址 `0x004`(`ustatus`)和 `0x041`(`uepc`)。立即数为 `IR[19:15]` 的零扩展值,并按 RISC-V 规则把旧 CSR 值写回 `rd`;这覆盖任务书中的 `csrrsi s6,0x41,0` 和 `csrrw zero,0x41,s6` 用法。 - CSRRSI、CSRRCI,以及 CSRRW:支持 CSR 地址 `0x004`(`ustatus`)和 `0x041`(`uepc`)。立即数为 `IR[19:15]` 的零扩展值,并按 RISC-V 规则把旧 CSR 值写回 `rd`;这覆盖任务书中的 `csrrsi s6,0x41,0` 和 `csrrw zero,0x41,s6` 用法。
- `ecall`:`a7 == 34` 时把 `a0` 锁存到 `led_data_o`;否则置 `halted_o`,直到 `go_i` 为高。 - `ecall`:`a7 == 34` 时把 `a0` 锁存到 `led_data_o`;否则置 `halted_o`,直到 `go_i` 为高。
- `sw` 和 `sb`:输出 `data_wstrb_o`,支持按字节写入。 - `sw` 和 `sb`:输出 `data_wstrb_o`,支持按字节写入。
- `lui`:加载高位立即数,`rd = imm[31:12] << 12`(按需求补充的扩展指令,标准 opcode `0x37`)。
- `sltiu`:I 型 `funct3=011`,立即数符号扩展后按无符号与 rs1 比较。
- M 扩展乘法(`funct7=0000001`):`mul`(乘积低 32 位)、`mulh`(有符号×有符号高 32 位)、`mulhsu`(有符号×无符号高 32 位)、`mulhu`(无符号×无符号高 32 位)。
## 指令编码 ## 指令编码
@@ -25,12 +28,14 @@
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。 因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
| 指令类别 | opcode | 说明 | | 指令类别 | opcode | 说明 |
| ------------------------------ | -----: | --------------------------------------- | | ----------------------------------------------- | -----: | ---------------------------------------------------------------------------- |
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra | | R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/slli/srli/srai | | M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu(另有课程约定的 remu,`funct3=111`) |
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai |
| `lui`(扩展) | `0x0d` | 加载高位立即数,`rd = imm[31:12] << 12` |
| `lw` | `0x00` | `funct3=010` | | `lw` | `0x00` | `funct3=010` |
| `sw` / `sb` | `0x08` | `funct3=010` / `000` | | `sw` / `sb` | `0x08` | `funct3=010` / `000` |
| `beq` / `bne` / `blt` / `bltu` | `0x18` | `funct3=000` / `001` / `100` / `110` | | `beq` / `bne` / `blt` / `bge` / `bltu` / `bgeu` | `0x18` | `funct3=000` / `001` / `100` / `101` / `110` / `111` |
| `jal` | `0x1b` | J 型立即数 | | `jal` | `0x1b` | J 型立即数 |
| `jalr` | `0x19` | `funct3=000` | | `jalr` | `0x19` | `funct3=000` |
| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI | | 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI |
+109
View File
@@ -0,0 +1,109 @@
# 指令扩展验证程序:每条被测指令的结果都通过 ecall(a7=34) 输出,最后用 halt 停机。
# 期望输出顺序见每行注释(共 24 次输出),同时保存在同目录 isa_test.expected(每行一项 LED 值)。
#
# 用法(在仓库根目录执行):
# python tools/rv_asm.py testbench/programs/isa_test.asm <仿真工作目录>/risc-v-benchmark_ccab.hex
# 再用 testbench/tb_no_intr.v 仿真;hex 命名为 risc-v-benchmark_ccab.hex 时会被测试
# 平台优先从仿真工作目录载入(参见 tb_no_intr.v 的 ROM 探测顺序)。
# print / halt 是 tools/rv_asm.py 提供的双字伪指令:
# print = addi a7,zero,34 + ecall;halt = addi a7,zero,10 + ecall。
# ---- sltiu:立即数符号扩展后做无符号比较 ----
addi t0, zero, 1
addi t1, zero, -1 # t1 = 0xffffffff
sltiu a0, t0, -1 # 1 <u 0xffffffff -> 00000001
print
sltiu a0, t1, 1 # 0xffffffff <u 1 -> 00000000
print
# ---- xor / sll / srl / sra(原有指令回归)----
addi t0, zero, 0xff
addi t1, zero, -1
xor a0, t0, t1 # 0xff ^ 0xffffffff -> ffffff00
print
addi t0, zero, 1
addi t1, zero, 31
sll a0, t0, t1 # 1 << 31 -> 80000000
print
addi t0, zero, -1
addi t1, zero, 28
srl a0, t0, t1 # 逻辑右移 -> 0000000f
print
sra a0, t0, t1 # 算术右移 -> ffffffff
print
# ---- mul:无符号乘积低 32 位 ----
addi t0, zero, 1234
addi t1, zero, 567
mul a0, t0, t1 # 1234*567 = 0x000aad1e
print
# ---- mulh / mulhu / mulhsu:64 位乘积的高 32 位 ----
addi t0, zero, -1 # 有符号 -1 / 无符号 0xffffffff
addi t1, zero, -1
mulh a0, t0, t1 # (-1)*(-1)=1 -> 00000000
print
mulhu a0, t0, t1 # 0xffffffff^2 -> fffffffe
print
addi t0, zero, -1
addi t1, zero, 2
mulhsu a0, t0, t1 # (-1)*2=-2 -> ffffffff
print
# ---- bltu:0 <u 0xffffffff 成立(按有符号比较则不成立)----
addi t0, zero, -1
bltu zero, t0, bltu_ok
addi a0, zero, 0x101 # 不应执行
print
bltu_ok:
addi a0, zero, 0x601 # 00000601
print
# ---- bge:有符号 >= ,(-1) >= 0 不成立,应走 fall-through ----
addi t0, zero, -1
bge t0, zero, bge_bad
addi a0, zero, 0x602 # 00000602
print
jal zero, bge_done
bge_bad:
addi a0, zero, 0x102 # 不应执行
print
bge_done:
# ---- bgeu:无符号 >= ,0xffffffff >=u 0 成立 ----
addi t0, zero, -1
bgeu t0, zero, bgeu_ok
addi a0, zero, 0x103 # 不应执行
print
bgeu_ok:
addi a0, zero, 0x603 # 00000603
print
# ---- bge 循环:从 -3 递增到 0(共 4 次输出)----
addi s1, zero, -3
bge_loop:
add a0, zero, s1
print # fffffffd / fffffffe / ffffffff / 00000000
addi s1, s1, 1
bge zero, s1, bge_loop
# ---- bgeu 循环:3 -> 1(共 3 次输出)----
addi t0, zero, 3
bgeu_loop:
add a0, zero, t0
print # 00000003 / 00000002 / 00000001
addi t0, t0, -1
addi t2, zero, 1
bgeu t0, t2, bgeu_loop
# ---- bltu 循环:4 -> 1(共 4 次输出)----
addi s1, zero, 4
bltu_loop:
add a0, zero, s1
print # 00000004 / 00000003 / 00000002 / 00000001
addi s1, s1, -1
bltu zero, s1, bltu_loop
halt
+24
View File
@@ -0,0 +1,24 @@
00000001
00000000
ffffff00
80000000
0000000f
ffffffff
000aad1e
00000000
fffffffe
ffffffff
00000601
00000602
00000603
fffffffd
fffffffe
ffffffff
00000000
00000003
00000002
00000001
00000004
00000003
00000002
00000001
+26
View File
@@ -0,0 +1,26 @@
# LUI 指令测试:依次打印 lui 的结果,最后停机。
# 期望输出:12345000 / fffff000 / 80000000 / 12345678,随后停机(halted=1)。
# 用法:python tools/rv_asm.py testbench/programs/lui_test.asm <仿真工作目录>/risc-v-benchmark_ccab.hex
lui t0, 0x12345 # t0 = 0x12345000
add a0, zero, t0
addi a7, zero, 34
ecall
lui t0, 0xfffff # t0 = 0xfffff000
add a0, zero, t0
addi a7, zero, 34
ecall
lui t0, 0x80000 # t0 = 0x80000000
add a0, zero, t0
addi a7, zero, 34
ecall
lui t0, 0x12345 # 常见用法 lui+addi:0x12345678
addi t0, t0, 0x678
add a0, zero, t0
addi a7, zero, 34
ecall
addi a7, zero, 10 # a7=10 -> 停机
ecall
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""极简 RISC-V 汇编器:仅支持本项目测试所需的指令子集。
用法: python tools/rv_asm.py <input.asm> <output.hex>
同时生成 <output.hex>.lst 反汇编/编码清单,便于核对。
支持:add/sub/and/or/xor/sll/srl/sra/slt/sltu、
mul/mulh/mulhsu/mulhu、
addi/slti/sltiu/xori/ori/andi、slli/srli/srai、
lw/sw、beq/bne/blt/bge/bltu/bgeu、lui、jal/jalr、ecall、标签、# 注释
伪指令:print(= addi a7,zero,34 + ecall)、halt(= addi a7,zero,10 + ecall),各占两个字。
示例:python tools/rv_asm.py testbench/programs/isa_test.asm out.hex
"""
import re
import sys
REGS = {
"zero": 0,
"ra": 1,
"sp": 2,
"gp": 3,
"tp": 4,
"t0": 5,
"t1": 6,
"t2": 7,
"s0": 8,
"s1": 9,
"a0": 10,
"a1": 11,
"a2": 12,
"a3": 13,
"a4": 14,
"a5": 15,
"a6": 16,
"a7": 17,
"s2": 18,
"s3": 19,
"s4": 20,
"s5": 21,
"s6": 22,
"s7": 23,
"s8": 24,
"s9": 25,
"s10": 26,
"s11": 27,
"t3": 28,
"t4": 29,
"t5": 30,
"t6": 31,
}
def reg(t):
t = t.strip()
if t in REGS:
return REGS[t]
m = re.fullmatch(r"x(\d+)", t)
if m:
return int(m.group(1))
raise SystemExit("未知寄存器: " + t)
def num(t):
return int(t.strip(), 0)
def enc_r(f7, rs2, rs1, f3, rd):
return (f7 << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x33
def enc_i(imm, rs1, f3, rd, op=0x13):
return ((imm & 0xFFF) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | op
def enc_s(imm, rs2, rs1, f3):
imm &= 0xFFF
return (
((imm >> 5) << 25)
| (rs2 << 20)
| (rs1 << 15)
| (f3 << 12)
| ((imm & 0x1F) << 7)
| 0x23
)
def enc_b(imm, rs2, rs1, f3):
imm &= 0x1FFF
return (
(((imm >> 12) & 1) << 31)
| (((imm >> 5) & 0x3F) << 25)
| (rs2 << 20)
| (rs1 << 15)
| (f3 << 12)
| (((imm >> 1) & 0xF) << 8)
| (((imm >> 11) & 1) << 7)
| 0x63
)
def enc_u(imm, rd):
return ((imm & 0xFFFFF) << 12) | (rd << 7) | 0x37
def enc_j(imm, rd):
imm &= 0x1FFFFF
return (
(((imm >> 20) & 1) << 31)
| (((imm >> 1) & 0x3FF) << 21)
| (((imm >> 11) & 1) << 20)
| (((imm >> 12) & 0xFF) << 12)
| (rd << 7)
| 0x6F
)
RTYPE = {
"add": (0x00, 0),
"sub": (0x20, 0),
"sll": (0x00, 1),
"slt": (0x00, 2),
"sltu": (0x00, 3),
"xor": (0x00, 4),
"srl": (0x00, 5),
"sra": (0x20, 5),
"or": (0x00, 6),
"and": (0x00, 7),
"mul": (0x01, 0),
"mulh": (0x01, 1),
"mulhsu": (0x01, 2),
"mulhu": (0x01, 3),
}
ITYPE = {"addi": 0, "slti": 2, "sltiu": 3, "xori": 4, "ori": 6, "andi": 7}
SHIFT = {"slli": 1, "srli": 5, "srai": 5}
BTYPE = {"beq": 0, "bne": 1, "blt": 4, "bge": 5, "bltu": 6, "bgeu": 7}
# 双字伪指令:print = addi a7,zero,34 + ecall;halt = addi a7,zero,10 + ecall
PSEUDO_SIZE = {"print": 2, "halt": 2}
def assemble(lines):
labels, items, addr = {}, [], 0
for raw in lines:
line = raw.split("#")[0].strip()
if not line:
continue
while True:
m = re.match(r"^([A-Za-z_.$][\w.$]*)\s*:\s*(.*)$", line)
if not m:
break
labels[m.group(1)] = addr
line = m.group(2).strip()
if not line:
break
if not line:
continue
items.append((addr, line))
addr += 4 * PSEUDO_SIZE.get(line.split()[0].lower(), 1)
out = []
for addr, line in items:
parts = re.split(r"[\s,]+", line)
mn, a = parts[0].lower(), parts[1:]
if mn in RTYPE:
f7, f3 = RTYPE[mn]
w = enc_r(f7, reg(a[2]), reg(a[1]), f3, reg(a[0]))
elif mn in ITYPE:
im = num(a[2])
if not (-2048 <= im <= 2047):
raise SystemExit("I 型立即数超出 12 位有符号范围: " + line)
w = enc_i(im, reg(a[1]), ITYPE[mn], reg(a[0]))
elif mn in SHIFT:
w = enc_i(num(a[2]) & 0x1F, reg(a[1]), SHIFT[mn], reg(a[0]))
elif mn == "lui":
w = enc_u(num(a[1]), reg(a[0]))
elif mn in ("lw", "sw"):
m = re.fullmatch(r"(-?\w+)\((\w+)\)", a[1])
if not m:
raise SystemExit("内存操作数格式应为 imm(rs1): " + line)
im = num(m.group(1))
if not (-2048 <= im <= 2047):
raise SystemExit("访存偏移超出 12 位有符号范围: " + line)
if mn == "lw":
w = enc_i(im, reg(m.group(2)), 2, reg(a[0]), 0x03)
else:
w = enc_s(im, reg(a[0]), reg(m.group(2)), 2)
elif mn in BTYPE:
tgt = labels[a[2]] if a[2] in labels else num(a[2])
w = enc_b(tgt - addr, reg(a[1]), reg(a[0]), BTYPE[mn])
elif mn == "jal":
tgt = labels[a[1]] if a[1] in labels else num(a[1])
w = enc_j(tgt - addr, reg(a[0]))
elif mn == "jalr":
w = enc_i(num(a[2]), reg(a[1]), 0, reg(a[0]), 0x67)
elif mn == "ecall":
w = 0x00000073
elif mn == "print":
w = [enc_i(34, 0, 0, 17), 0x00000073]
elif mn == "halt":
w = [enc_i(10, 0, 0, 17), 0x00000073]
else:
raise SystemExit("未知助记符: " + mn)
out.append((addr, line, w if isinstance(w, list) else [w]))
return out
def main():
src, dst = sys.argv[1], sys.argv[2]
with open(src, encoding="utf-8") as f:
prog = assemble(f.readlines())
with open(dst, "w", encoding="ascii") as f:
for _, _, ws in prog:
for w in ws:
f.write("%08X\n" % w)
with open(dst + ".lst", "w", encoding="utf-8") as f:
for addr, line, ws in prog:
f.write("%04X: %s %s\n" % (addr, " ".join("%08X" % w for w in ws), line))
print("已生成 %s(%d 个字)" % (dst, sum(len(ws) for _, _, ws in prog)))
main()