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/ 下。
249 lines
8.3 KiB
Verilog
249 lines
8.3 KiB
Verilog
`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
|
||
|
||
// 加载指令,当前仅支持 lw(funct3=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
|
||
|
||
// 存储指令:sw(funct3=010)与 sb(funct3=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
|
||
|
||
// 条件分支:beq(000)/bne(001)/blt(100,有符号小于)/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_SLT,bltu 用 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
|