Files
risc-v-cpu/cpu21_riscv_decoder.v
T
Anccend 266ec6a049 重构:按功能拆分流水线模块、修复 BLT 指令、注释中文化
1) 模块拆分(便于单独仿真调试)
   把原来单文件的三模块结构按功能拆成 10 个独立文件,顶层只保留五级流水
   寄存器、子模块例化、PC/IF-ID/ID-EX 控制优先级与对外输出:
     cpu21_riscv_alu.v            组合 ALU(result2 用于 MUL 高位/余数)
     cpu21_bpb_8.v                8 项全相联分支目标缓冲
     cpu21_riscv_decoder.v        ID 级组合译码器
     cpu21_riscv_regfile.v        寄存器堆(写优先旁路)
     cpu21_riscv_forward_unit.v   EX 级前递网络
     cpu21_riscv_store_unit.v     EX 级存储数据对齐(sw/sb)
     cpu21_riscv_branch_unit.v    EX 级控制流裁决
     cpu21_riscv_hazard_unit.v    load-use 冒险与停顿/冲刷判定
     cpu21_riscv_irq_ctrl.v       中断优先级、ustatus/uepc、嵌套返回栈
     cpu21_riscv_perf_counters.v  性能计数器
   已把上述文件加入 Vivado 工程 sources_1;端口、时序与行为经 xsim 对同一
   ROM 逐周期回归验证,统计量完全一致。

2) 修复 BLT 指令(真值表第 28 行:opcode IR[6:2]=0x18、funct3=100、ALU_OP=SLT)
   此前 funct3=100 未译码,blt 被当作空指令执行,于是
   `blt s1,zero,loop` 的循环只执行一次就顺序落到退出代码并停机
   (现象:只输出第一个值后就不再运行)。
     - cpu21_riscv_decoder.v:新增 blt_o,OP_BRANCH 接受 funct3=100,
       并按真值表给出 ALU_SLT(beq/bne 仍为 SUB,bltu 仍为 SLTU)
     - cpu21_riscv_branch_unit.v:新增 blt_i,用有符号比较
       $signed(src1) < $signed(src2) 裁决
     - cpu21_riscv_redirect_int_bpb.v:新增 idex_blt_q 流水寄存器,
       IF 级 f_is_branch 纳入 funct3=100,使 blt 也参与 BPB 预测
   验证:blt 小程序正确输出 -3/-2/-1 后停机(cond_taken=2);
   benchmark 第 [4] 段完整输出 fffffff1..ffffffff;
   中断测试程序的中断入口与停留周期与改动前一致。

3) 注释中文化
   - cpu21_riscv_redirect_int_bpb.v 及全部新增子模块使用中文注释;
   - testbench/tb_cpu21_riscv_redirect_int_bpb.v 与新增的 tb_no_intr.v
     注释全部译为中文。
   - testbench/tb_no_intr.v:与中断测试平台同框架但不注入 irq 脉冲的对照
     测试平台(修复其 $dumpvars 引用了不存在的模块名,并改用 tb_no_intr.vcd
     避免与另一个 TB 的波形文件互相覆盖)。

4) 其他
   - testbench/tb_cpu21_riscv_redirect_int_bpb.v 的默认 ROM_FILE 改为
     cpu21_riscv_redirect_int_bpb_rom.hex(中断测试程序);
   - cpu21_riscv_redirect_int_bpb说明.md 增补"文件与模块划分"章节与 BLT 说明;
   - 真值表.txt 移动到 testbench/programs/ 下。
2026-09-12 18:48:31 +08:00

249 lines
8.3 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`timescale 1ns / 1ps
`default_nettype none
// -----------------------------------------------------------------------------
// ID 级组合译码器。
//
// 与原始 Logisim 控制器一致:操作码取标准 RISC-V 的 IR[6:2] 五位字段,
// 真值表中以十六进制形式存储该字段。
// 所有输出在无匹配时保持"无操作/不写回"的默认值。
// -----------------------------------------------------------------------------
module cpu21_riscv_decoder (
input wire [31:0] ir_i,
output reg [ 4:0] rs1_idx_o,
output reg [ 4:0] rs2_idx_o,
output reg [ 4:0] rd_o,
output reg [31:0] imm_o,
output reg [ 3:0] alu_op_o,
output reg [ 2:0] wb_sel_o,
output wire [11:0] csr_addr_o,
output reg uses_rs1_o,
output reg uses_rs2_o,
output reg reg_write_o,
output reg mem_to_reg_o,
output reg mem_write_o,
output reg mem_byte_o,
output reg alu_src_o,
output reg branch_o,
output reg beq_o,
output reg bne_o,
output reg blt_o,
output reg bltu_o,
output reg jal_o,
output reg jalr_o,
output reg ecall_o,
output reg uret_o,
output reg csr_set_o,
output reg csr_clear_o,
output reg csr_write_o
);
// CPU21 自定义操作码(取自所提供的真值表)。
localparam OP_LOAD = 5'h00;
localparam OP_R = 5'h0c;
localparam OP_I = 5'h04;
localparam OP_STORE = 5'h08;
localparam OP_JALR = 5'h19;
localparam OP_BRANCH = 5'h18;
localparam OP_JAL = 5'h1b;
localparam OP_SYS = 5'h1c;
localparam ALU_SLL = 4'd0;
localparam ALU_SRA = 4'd1;
localparam ALU_SRL = 4'd2;
localparam ALU_MUL = 4'd3;
localparam ALU_DIVU = 4'd4;
localparam ALU_ADD = 4'd5;
localparam ALU_SUB = 4'd6;
localparam ALU_AND = 4'd7;
localparam ALU_OR = 4'd8;
localparam ALU_XOR = 4'd9;
localparam ALU_SLT = 4'd11;
localparam ALU_SLTU = 4'd12;
wire [4:0] opcode = ir_i[6:2];
wire [2:0] funct3 = ir_i[14:12];
wire [6:0] funct7 = ir_i[31:25];
// CSR 地址字段在译码时直接旁路输出(供 EX 级读取 CSR)。
assign csr_addr_o = ir_i[31:20];
// 默认控制信号:全部为"无操作/不写回",再由下面的 case 覆盖。
always @* begin
rs1_idx_o = ir_i[19:15];
rs2_idx_o = ir_i[24:20];
rd_o = ir_i[11:7];
imm_o = {{20{ir_i[31]}}, ir_i[31:20]};
alu_op_o = ALU_ADD;
wb_sel_o = 3'd0;
uses_rs1_o = 1'b0;
uses_rs2_o = 1'b0;
reg_write_o = 1'b0;
mem_to_reg_o = 1'b0;
mem_write_o = 1'b0;
mem_byte_o = 1'b0;
alu_src_o = 1'b0;
branch_o = 1'b0;
beq_o = 1'b0;
bne_o = 1'b0;
blt_o = 1'b0;
bltu_o = 1'b0;
jal_o = 1'b0;
jalr_o = 1'b0;
ecall_o = 1'b0;
uret_o = 1'b0;
csr_set_o = 1'b0;
csr_clear_o = 1'b0;
csr_write_o = 1'b0;
case (opcode)
// R 型运算:由 funct3/funct7 决定具体操作。
OP_R: begin
uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1;
reg_write_o = 1'b1;
// 电路中额外的 REMU 控制对应标准 R 型的 funct7=1/funct3=111
// 形式;此时 ALU 的 result2 即为余数。
if ((funct7 == 7'b0000001) && (funct3 == 3'b111)) begin
alu_op_o = ALU_DIVU;
wb_sel_o = 3'd3;
end else if ((funct7 == 7'b0000001) && (funct3 == 3'b000)) begin
alu_op_o = ALU_MUL;
end else begin
// 标准 R 型 funct3 译码;add/sub 与 sra/srl 由 funct7[5] 区分。
case (funct3)
3'b000: alu_op_o = (funct7[5] ? ALU_SUB : ALU_ADD);
3'b001: alu_op_o = ALU_SLL;
3'b010: alu_op_o = ALU_SLT;
3'b011: alu_op_o = ALU_SLTU;
3'b100: alu_op_o = ALU_XOR;
3'b101: alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL);
3'b110: alu_op_o = ALU_OR;
3'b111: alu_op_o = ALU_AND;
default: reg_write_o = 1'b0;
endcase
end
end
// I 型运算(addi/slli/slti/xori/srai/srli/ori/andi)。
OP_I: begin
uses_rs1_o = 1'b1;
alu_src_o = 1'b1;
reg_write_o = 1'b1;
case (funct3)
3'b000: alu_op_o = ALU_ADD; // addi:立即数加
3'b001: alu_op_o = ALU_SLL; // slli:立即数逻辑左移
3'b010: alu_op_o = ALU_SLT; // slti:有符号小于置 1
3'b100: alu_op_o = ALU_XOR; // xori:立即数异或
3'b101:
alu_op_o = (funct7[5] ? ALU_SRA : ALU_SRL); // srai/srli:立即数算术/逻辑右移
3'b110: alu_op_o = ALU_OR; // ori:立即数或
3'b111: alu_op_o = ALU_AND; // andi:立即数与
default: reg_write_o = 1'b0;
endcase
end
// 加载指令,当前仅支持 lwfunct3=010)。
OP_LOAD: begin
if (funct3 == 3'b010) begin
uses_rs1_o = 1'b1;
alu_src_o = 1'b1;
alu_op_o = ALU_ADD;
mem_to_reg_o = 1'b1;
reg_write_o = 1'b1;
wb_sel_o = 3'd1;
end
end
// 存储指令:swfunct3=010)与 sbfunct3=000)。
OP_STORE: begin
if ((funct3 == 3'b010) || (funct3 == 3'b000)) begin
uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1;
alu_src_o = 1'b1;
alu_op_o = ALU_ADD;
mem_write_o = 1'b1;
mem_byte_o = (funct3 == 3'b000); // sb:字节存储
imm_o = {{20{ir_i[31]}}, ir_i[31:25], ir_i[11:7]};
end
end
// 条件分支:beq000/bne001/blt100,有符号小于)/bltu(110,无符号小于)。
OP_BRANCH: begin
if ((funct3 == 3'b000) || (funct3 == 3'b001) ||
(funct3 == 3'b100) || (funct3 == 3'b110)) begin
uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1;
branch_o = 1'b1;
beq_o = (funct3 == 3'b000);
bne_o = (funct3 == 3'b001);
blt_o = (funct3 == 3'b100);
bltu_o = (funct3 == 3'b110);
// 真值表:beq/bne 用减法比较,blt 用 ALU_SLTbltu 用 ALU_SLTU。
alu_op_o = blt_o ? ALU_SLT : (bltu_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};
end
end
// 无条件跳转并链接:jal(写回 PC+4)。
OP_JAL: begin
jal_o = 1'b1;
reg_write_o = 1'b1;
wb_sel_o = 3'd2;
imm_o = {{11{ir_i[31]}}, ir_i[31], ir_i[19:12], ir_i[20], ir_i[30:21], 1'b0};
end
// 寄存器间接跳转并链接:jalr。
OP_JALR: begin
if (funct3 == 3'b000) begin
jalr_o = 1'b1;
uses_rs1_o = 1'b1;
alu_src_o = 1'b1;
alu_op_o = ALU_ADD;
reg_write_o = 1'b1;
wb_sel_o = 3'd2;
end
end
// 系统指令:ecall / uret / CSR 读写。
OP_SYS: begin
// 电路用 IR[21] 区分 URET 与 ecall。
if (funct3 == 3'b000) begin
if (ir_i[21]) begin
uret_o = 1'b1;
end else begin
ecall_o = 1'b1;
// 按文档说明,ecall 读取 a7(rs17) 与 a0(rs10)。
uses_rs1_o = 1'b1;
uses_rs2_o = 1'b1;
rs1_idx_o = 5'd17;
rs2_idx_o = 5'd10;
end
end else if (funct3 == 3'b001) begin
csr_write_o = 1'b1; // CSRRW:写 CSR,并把旧值写回 rd
uses_rs1_o = 1'b1;
alu_src_o = 1'b1;
imm_o = {27'b0, ir_i[19:15]};
reg_write_o = (rd_o != 5'd0);
wb_sel_o = 3'd4;
end else if (funct3 == 3'b110) begin
csr_set_o = 1'b1; // CSRRSI:置位 CSR 中的指定位
imm_o = {27'b0, ir_i[19:15]};
reg_write_o = (rd_o != 5'd0);
wb_sel_o = 3'd4;
end else if (funct3 == 3'b111) begin
csr_clear_o = 1'b1; // CSRRCI:清除 CSR 中的指定位
imm_o = {27'b0, ir_i[19:15]};
reg_write_o = (rd_o != 5'd0);
wb_sel_o = 3'd4;
end
end
default: begin
end
endcase
end
endmodule
`default_nettype wire