From abb2e161afa722ec154cd816e25ea32eb7abde82 Mon Sep 17 00:00:00 2001 From: ANCCEND <3064429095@qq.com> Date: Sat, 12 Sep 2026 16:34:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=B7=BB=E5=8A=A0=E5=92=8C?= =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpu21_riscv_redirect_int_bpb.v | 189 ++++++++++++------- testbench/programs/risc-v-benchmark_ccab.hex | 1 - testbench/programs/risc-v-分支预测测试.hex | 67 +++++++ testbench/risc-v-branch-predict.hex | 67 +++++++ testbench/tb_cpu21_riscv_redirect_int_bpb.v | 1 + 5 files changed, 256 insertions(+), 69 deletions(-) create mode 100644 testbench/programs/risc-v-分支预测测试.hex create mode 100644 testbench/risc-v-branch-predict.hex diff --git a/cpu21_riscv_redirect_int_bpb.v b/cpu21_riscv_redirect_int_bpb.v index 1254e0b..bb44b20 100644 --- a/cpu21_riscv_redirect_int_bpb.v +++ b/cpu21_riscv_redirect_int_bpb.v @@ -2,23 +2,24 @@ `default_nettype none // ----------------------------------------------------------------------------- -// cpu21-riscv-4.circ -> synthesizable Verilog +// cpu21-riscv-4.circ -> 可综合 Verilog 实现 // -// This file keeps the instruction encodings used by the Logisim design. The -// processor is a five-stage IF/ID/EX/MEM/WB pipeline with: -// * EX-stage forwarding and load-use interlock; -// * EX-stage control-flow resolution and pipeline redirect; -// * an eight-entry fully-associative branch target buffer; -// * three sampled interrupt sources with priority and nested URET return; -// * the original ecall convention: a7==34 writes the LED value, otherwise -// the pipeline waits for go_i. +// 本文件保留 Logisim 原设计所使用的指令编码。处理器为五级流水线 +// IF/ID/EX/MEM/WB,主要特性: +// * EX 级前递(forwarding)与 load-use 冒险互锁; +// * EX 级控制流裁决与流水线重定向; +// * 八表项全相联分支目标缓冲(BPB); +// * 三路采样的中断源,带优先级与可嵌套的 URET 返回; +// * 沿用原 ecall 约定:a7==34 时写出 LED 值,否则流水线等待 go_i。 // -// Memory interface convention: -// instr_addr_o and data_addr_o are byte addresses. data_wstrb_o is a byte -// write mask, so an external word RAM can use data_addr_o[11:2] as its word -// index and merge bytes according to data_wstrb_o. +// 存储器接口约定: +// instr_addr_o 与 data_addr_o 均为字节地址。data_wstrb_o 是字节写掩码, +// 因此外部字宽 RAM 可用 data_addr_o[11:2] 作为字索引,再依据 +// data_wstrb_o 完成字节合并。 // ----------------------------------------------------------------------------- +// ALU:纯组合运算单元。result2 作为第二结果输出,用于 MUL 的高 32 位 +// 以及 DIVU 的余数;除法则以 b==0 作为除零保护。 module cpu21_riscv_alu ( input wire [ 3:0] op, input wire [31:0] a, @@ -70,9 +71,9 @@ module cpu21_riscv_alu ( endmodule -// Eight-entry fully-associative branch target buffer. -// tag = PC[11:2], matching the ten-bit tag splitter in the Logisim BPB. -// count is a two-bit saturating predictor: 00/01 not-taken, 10/11 taken. +// 八表项全相联分支目标缓冲(BPB/BTB)。 +// tag = PC[11:2],与 Logisim 版 BPB 中的十位 tag 拆分保持一致。 +// count 为两位饱和计数器:00/01 表示不跳转,10/11 表示跳转。 module cpu21_bpb_8 ( input wire clk, input wire reset, @@ -103,6 +104,7 @@ module cpu21_bpb_8 ( integer k; integer selected_index; + // 预测命中查找:按 tag 全相联匹配,命中则给出目标地址与方向预测。 always @* begin predict_hit = 1'b0; predict_taken = 1'b0; @@ -116,8 +118,8 @@ module cpu21_bpb_8 ( end end - // Find a matching entry, otherwise use the first invalid entry, otherwise - // the oldest entry. The age update below implements an LRU-like policy. + // 更新时先找匹配表项;若无匹配则优先使用无效表项,否则淘汰最旧表项。 + // 下方的 age 自增实现了类似 LRU 的替换策略。 always @* begin update_found = 1'b0; update_index = 3'd0; @@ -142,6 +144,7 @@ module cpu21_bpb_8 ( end end + // 表项状态更新(含替换与两位饱和计数器更新)。 always @(posedge clk or posedge reset) begin if (reset) begin for (k = 0; k < 8; k = k + 1) begin @@ -160,11 +163,13 @@ module cpu21_bpb_8 ( age[selected_index] <= 3'b0; if (!update_found) begin - // Weak initial state avoids an immediate false prediction. + // 新表项采用较弱的初始状态,避免立刻产生错误预测。 count[selected_index] <= update_taken ? 2'b10 : 2'b01; end else if (update_taken) begin + // 实际跳转:计数器加一(饱和到 11)。 if (count[selected_index] != 2'b11) count[selected_index] <= count[selected_index] + 2'b01; end else begin + // 实际不跳转:计数器减一(饱和到 00)。 if (count[selected_index] != 2'b00) count[selected_index] <= count[selected_index] - 2'b01; end @@ -176,15 +181,16 @@ module cpu21_bpb_8 ( endmodule +// 顶层模块:五级流水线 RISC-V 处理器,内置分支预测与多级中断支持。 module cpu21_riscv_redirect_int_bpb #( parameter RESET_PC = 32'h0000_0000, parameter IRQ1_VECTOR = 32'h0000_30ac, parameter IRQ2_VECTOR = 32'h0000_31e4, parameter IRQ3_VECTOR = 32'h0000_3310, parameter IRQ_STACK_DEPTH = 4, - // ustatus reset value. Bit 0 is MIE. The course interrupt test program - // only sets MIE inside its interrupt handlers, so the mainline would never - // acknowledge the first interrupt unless MIE is enabled right after reset. + // ustatus 复位值。bit0 为 MIE。课程的中断测试程序只会在中断处理 + // 程序中设置 MIE,若复位后不立即置位 MIE,主程序将永远无法响应 + // 第一次中断,因此这里默认打开 MIE。 parameter [31:0] USTATUS_INIT = 32'h0000_0001 ) ( input wire clk, @@ -234,7 +240,7 @@ module cpu21_riscv_redirect_int_bpb #( output wire [15:0] prediction_success_count_o, output wire [15:0] prediction_failure_count_o ); - // Custom CPU21 opcodes from the supplied truth table. + // CPU21 自定义操作码(取自所提供的真值表)。 localparam OP_LOAD = 5'h00; localparam OP_R = 5'h0c; localparam OP_I = 5'h04; @@ -257,14 +263,17 @@ module cpu21_riscv_redirect_int_bpb #( localparam ALU_SLT = 4'd11; localparam ALU_SLTU = 4'd12; + // 当前取指 PC。 reg [31:0] pc_q; + // IF/ID 流水寄存器(含本条指令所携带的分支预测信息)。 reg ifid_valid_q; reg [31:0] ifid_pc_q; reg [31:0] ifid_ir_q; reg ifid_pred_taken_q; reg [31:0] ifid_pred_target_q; + // ID/EX 流水寄存器:保存译码结果与操作数。 reg idex_valid_q; reg [31:0] idex_pc_q; reg [31:0] idex_ir_q; @@ -296,6 +305,7 @@ module cpu21_riscv_redirect_int_bpb #( reg idex_pred_taken_q; reg [31:0] idex_pred_target_q; + // EX/MEM 流水寄存器:保存 ALU/存储结果与写回控制。 reg exmem_valid_q; reg [31:0] exmem_pc_q; reg [31:0] exmem_ir_q; @@ -310,6 +320,7 @@ module cpu21_riscv_redirect_int_bpb #( reg [ 3:0] exmem_wstrb_q; reg [ 2:0] exmem_wb_sel_q; + // MEM/WB 流水寄存器:保存访存数据与最终写回信息。 reg memwb_valid_q; reg [31:0] memwb_pc_q; reg [31:0] memwb_ir_q; @@ -322,13 +333,14 @@ module cpu21_riscv_redirect_int_bpb #( reg memwb_mem_to_reg_q; reg [ 2:0] memwb_wb_sel_q; + // 寄存器堆、LED 输出、停机标志。 reg [31:0] regfile [ 0:31]; reg [31:0] led_data_q; reg led_valid_q; reg halted_q; - // Interrupt state. ustatus[0] is the circuit's MIE bit. A small stack - // keeps EPC/status/priority so nested interrupts can return correctly. + // 中断相关状态。ustatus[0] 即电路中的 MIE 位。用一个小的硬件栈 + // 保存 EPC/状态/优先级,使嵌套中断能够正确返回。 reg [31:0] ustatus_q; reg [31:0] uepc_q; reg [ 1:0] irq_current_q; @@ -341,6 +353,7 @@ module cpu21_riscv_redirect_int_bpb #( reg [ 1:0] priority_stack [0:IRQ_STACK_DEPTH-1]; reg [ 2:0] irq_depth_q; + // 性能计数器,用于统计周期数、停顿、气泡与分支预测效果。 reg [15:0] cycle_count_q; reg [15:0] stall_count_q; reg [15:0] bubble_count_q; @@ -350,10 +363,10 @@ module cpu21_riscv_redirect_int_bpb #( reg [15:0] prediction_failure_count_q; // ------------------------------------------------------------------------- - // Decode in ID. + // ID 级译码。 // ------------------------------------------------------------------------- - // The Logisim controller receives the standard RISC-V opcode field - // IR[6:2] (the table stores this five-bit field in hexadecimal). + // Logisim 控制器接收的是标准 RISC-V 操作码字段 IR[6:2](真值表中 + // 以十六进制形式存储该五位字段)。 wire [ 4:0] d_opcode = ifid_ir_q[6:2]; wire [ 2:0] d_funct3 = ifid_ir_q[14:12]; wire [ 6:0] d_funct7 = ifid_ir_q[31:25]; @@ -384,6 +397,7 @@ module cpu21_riscv_redirect_int_bpb #( reg [31:0] d_imm; wire [11:0] d_csr_addr = ifid_ir_q[31:20]; + // 默认控制信号:全部为"无操作/不写回",再由下面的 case 覆盖。 always @* begin d_reg_write = 1'b0; d_mem_to_reg = 1'b0; @@ -411,18 +425,20 @@ module cpu21_riscv_redirect_int_bpb #( d_imm = {{20{ifid_ir_q[31]}}, ifid_ir_q[31:20]}; case (d_opcode) + // R 型运算:由 funct3/funct7 决定具体操作。 OP_R: begin d_uses_rs1 = 1'b1; d_uses_rs2 = 1'b1; d_reg_write = 1'b1; - // The circuit's extra REMU control is the standard R-type - // funct7=1/funct3=111 form; ALU result2 is the remainder. + // 电路中额外的 REMU 控制对应标准 R 型的 funct7=1/funct3=111 + // 形式;此时 ALU 的 result2 即为余数。 if ((d_funct7 == 7'b0000001) && (d_funct3 == 3'b111)) begin d_aluop = ALU_DIVU; d_wb_sel = 3'd3; end else if ((d_funct7 == 7'b0000001) && (d_funct3 == 3'b000)) begin d_aluop = ALU_MUL; end else begin + // 标准 R 型 funct3 译码;add/sub 与 sra/srl 由 funct7[5] 区分。 case (d_funct3) 3'b000: d_aluop = (d_funct7[5] ? ALU_SUB : ALU_ADD); 3'b001: d_aluop = ALU_SLL; @@ -437,22 +453,25 @@ module cpu21_riscv_redirect_int_bpb #( end end + // I 型运算(addi/slli/slti/xori/srai/srli/ori/andi)。 OP_I: begin d_uses_rs1 = 1'b1; d_alu_src = 1'b1; d_reg_write = 1'b1; case (d_funct3) - 3'b000: d_aluop = ALU_ADD; // addi - 3'b001: d_aluop = ALU_SLL; // slli - 3'b010: d_aluop = ALU_SLT; // slti - 3'b100: d_aluop = ALU_XOR; // xori - 3'b101: d_aluop = (d_funct7[5] ? ALU_SRA : ALU_SRL); - 3'b110: d_aluop = ALU_OR; // ori - 3'b111: d_aluop = ALU_AND; // andi + 3'b000: d_aluop = ALU_ADD; // addi:立即数加 + 3'b001: d_aluop = ALU_SLL; // slli:立即数逻辑左移 + 3'b010: d_aluop = ALU_SLT; // slti:有符号小于置 1 + 3'b100: d_aluop = ALU_XOR; // xori:立即数异或 + 3'b101: + d_aluop = (d_funct7[5] ? ALU_SRA : ALU_SRL); // srai/srli:立即数算术/逻辑右移 + 3'b110: d_aluop = ALU_OR; // ori:立即数或 + 3'b111: d_aluop = ALU_AND; // andi:立即数与 default: d_reg_write = 1'b0; endcase end + // 加载指令,当前仅支持 lw(funct3=010)。 OP_LOAD: begin if (d_funct3 == 3'b010) begin d_uses_rs1 = 1'b1; @@ -464,6 +483,7 @@ module cpu21_riscv_redirect_int_bpb #( end end + // 存储指令:sw(funct3=010)与 sb(funct3=000)。 OP_STORE: begin if ((d_funct3 == 3'b010) || (d_funct3 == 3'b000)) begin d_uses_rs1 = 1'b1; @@ -471,11 +491,12 @@ module cpu21_riscv_redirect_int_bpb #( d_alu_src = 1'b1; d_aluop = ALU_ADD; d_mem_write = 1'b1; - d_mem_byte = (d_funct3 == 3'b000); // sb + d_mem_byte = (d_funct3 == 3'b000); // sb:字节存储 d_imm = {{20{ifid_ir_q[31]}}, ifid_ir_q[31:25], ifid_ir_q[11:7]}; end end + // 条件分支:beq(000)/bne(001)/bltu(110)。 OP_BRANCH: begin if ((d_funct3 == 3'b000) || (d_funct3 == 3'b001) || (d_funct3 == 3'b110)) begin d_uses_rs1 = 1'b1; @@ -496,6 +517,7 @@ module cpu21_riscv_redirect_int_bpb #( end end + // 无条件跳转并链接:jal(写回 PC+4)。 OP_JAL: begin d_jal = 1'b1; d_reg_write = 1'b1; @@ -510,6 +532,7 @@ module cpu21_riscv_redirect_int_bpb #( }; end + // 寄存器间接跳转并链接:jalr。 OP_JALR: begin if (d_funct3 == 3'b000) begin d_jalr = 1'b1; @@ -521,33 +544,34 @@ module cpu21_riscv_redirect_int_bpb #( end end + // 系统指令:ecall / uret / CSR 读写。 OP_SYS: begin - // The circuit uses IR[21] to distinguish URET from ecall. + // 电路用 IR[21] 区分 URET 与 ecall。 if (d_funct3 == 3'b000) begin if (ifid_ir_q[21]) begin d_uret = 1'b1; end else begin d_ecall = 1'b1; - // ecall reads a7 and a0, as documented in the sheet. + // 按文档说明,ecall 读取 a7(rs17) 与 a0(rs10)。 d_uses_rs1 = 1'b1; d_uses_rs2 = 1'b1; d_src1_idx = 5'd17; d_src2_idx = 5'd10; end end else if (d_funct3 == 3'b001) begin - d_csr_write = 1'b1; // CSRRW + d_csr_write = 1'b1; // CSRRW:写 CSR,并把旧值写回 rd d_uses_rs1 = 1'b1; d_alu_src = 1'b1; d_imm = {27'b0, ifid_ir_q[19:15]}; d_reg_write = (d_rd != 5'd0); d_wb_sel = 3'd4; end else if (d_funct3 == 3'b110) begin - d_csr_set = 1'b1; // CSRRSI + d_csr_set = 1'b1; // CSRRSI:置位 CSR 中的指定位 d_imm = {27'b0, ifid_ir_q[19:15]}; d_reg_write = (d_rd != 5'd0); d_wb_sel = 3'd4; end else if (d_funct3 == 3'b111) begin - d_csr_clear = 1'b1; // CSRRCI + d_csr_clear = 1'b1; // CSRRCI:清除 CSR 中的指定位 d_imm = {27'b0, ifid_ir_q[19:15]}; d_reg_write = (d_rd != 5'd0); d_wb_sel = 3'd4; @@ -559,13 +583,15 @@ module cpu21_riscv_redirect_int_bpb #( endcase end - // Register-file read with a write-first bypass for the same cycle's WB. + // 写回值选择:wb_sel 0=ALU 结果,1=访存数据,2=PC+4,3=MUL 高位/余数, + // 4=CSR 旧值。 wire [31:0] wb_value; assign wb_value = (memwb_wb_sel_q == 3'd1) ? memwb_mem_data_q : (memwb_wb_sel_q == 3'd2) ? (memwb_pc_q + 32'd4) : (memwb_wb_sel_q == 3'd3) ? memwb_alu_result2_q : (memwb_wb_sel_q == 3'd4) ? memwb_csr_old_q : memwb_alu_result_q; + // ID 级读寄存器:x0 恒为 0,并对同周期 WB 的结果做写优先旁路。 wire [31:0] d_rs1_value = (d_src1_idx == 5'd0) ? 32'b0 : ((memwb_valid_q && memwb_reg_write_q && (memwb_rd_q == d_src1_idx)) ? wb_value : @@ -576,8 +602,8 @@ module cpu21_riscv_redirect_int_bpb #( regfile[d_src2_idx]); // ------------------------------------------------------------------------- - // IF branch prediction. JAL/JALR are resolved in EX, while conditional - // branches may redirect the fetch PC immediately from a BPB target. + // IF 级分支预测。JAL/JALR 在 EX 级裁决,条件分支则可以直接依据 BPB + // 给出的目标地址重定向取指 PC。 // ------------------------------------------------------------------------- wire [4:0] f_opcode = instr_i[6:2]; wire [2:0] f_funct3 = instr_i[14:12]; @@ -589,10 +615,12 @@ module cpu21_riscv_redirect_int_bpb #( wire bpb_predict_taken; wire ex_branch_taken; wire [31:0] ex_target; + // BPB 更新端口:在 EX 级用条件分支的实际结果训练预测器。 wire bpb_update_enable = idex_valid_q && idex_branch_q; wire bpb_update_taken = ex_branch_taken; wire [31:0] bpb_update_target = ex_target; + // 分支目标缓冲实例。 cpu21_bpb_8 u_bpb ( .clk (clk), .reset (reset), @@ -608,12 +636,13 @@ module cpu21_riscv_redirect_int_bpb #( .update_taken (bpb_update_taken) ); + // 下一取指 PC:预测跳转命中时使用 BPB 目标地址,否则顺序加 4。 wire [31:0] fetch_next_pc = (f_is_branch && bpb_predict_hit && bpb_predict_taken) ? bpb_predict_target : (pc_q + 32'd4); // ------------------------------------------------------------------------- - // EX forwarding, ALU, and control-flow resolution. + // EX 级:前递网络、ALU 运算与控制流裁决。 // ------------------------------------------------------------------------- reg [31:0] ex_src1; reg [31:0] ex_src2; @@ -626,6 +655,8 @@ module cpu21_riscv_redirect_int_bpb #( (exmem_wb_sel_q == 3'd4) ? exmem_csr_old_q : exmem_alu_result_q; + // 前递优先级:EX/MEM 的结果最新,其次为 MEM/WB 写回值,最后才是 + // 译码时读到的寄存器值。 always @* begin ex_src1 = idex_rs1_value_q; ex_src2 = idex_rs2_value_q; @@ -636,12 +667,15 @@ module cpu21_riscv_redirect_int_bpb #( else if (wb_forward_valid && (memwb_rd_q == idex_rs2_idx_q)) ex_src2 = wb_value; end + // ALU 的 B 端:立即数或 rs2。 wire [31:0] ex_alu_b = idex_alu_src_q ? idex_imm_q : ex_src2; wire [31:0] ex_alu_result; wire [31:0] ex_alu_result2; + // 读取 CSR 旧值(ustatus=0x004、uepc=0x041),供 CSR 指令回写 rd。 wire [31:0] ex_csr_old_value = (idex_csr_addr_q == 12'h004) ? ustatus_q : (idex_csr_addr_q == 12'h041) ? uepc_q : 32'b0; + // ALU 实例(纯组合逻辑)。 cpu21_riscv_alu u_ex_alu ( .op (idex_aluop_q), .a (ex_src1), @@ -650,14 +684,19 @@ module cpu21_riscv_redirect_int_bpb #( .result2(ex_alu_result2) ); + // 条件分支裁决:beq 相等、bne 不等、bltu 无符号小于。 assign ex_branch_taken = idex_branch_q && ((idex_beq_q && (ex_src1 == ex_src2)) || (idex_bne_q && (ex_src1 != ex_src2)) || (idex_bltu_q && (ex_src1 < ex_src2))); + // 跳转目标:jalr 为 (rs1+imm) 且最低位清零;jal 为 PC+imm。 assign ex_target = idex_jalr_q ? ((ex_src1 + idex_imm_q) & 32'hffff_fffe) : (idex_pc_q + idex_imm_q); + // EX 级存在需要裁决的控制流指令。 wire ex_controlflow = idex_valid_q && (idex_branch_q || idex_jal_q || idex_jalr_q); + // 需要重定向的条件:条件分支的预测方向/目标与实际不符;JAL/JALR 则 + // 一定在 EX 级才解析出目标,因此总是需要重定向。 wire ex_redirect_valid = ex_controlflow && (idex_branch_q ? ((idex_pred_taken_q != ex_branch_taken) || @@ -666,6 +705,7 @@ module cpu21_riscv_redirect_int_bpb #( wire [31:0] ex_redirect_pc = (idex_branch_q && !ex_branch_taken) ? (idex_pc_q + 32'd4) : ex_target; + // 存储数据对齐:依据字节地址低两位生成写数据与字节写掩码。 reg [31:0] ex_store_data; reg [3:0] ex_store_wstrb; always @* begin @@ -698,24 +738,27 @@ module cpu21_riscv_redirect_int_bpb #( end end + // ecall 约定:a7==34 时输出 LED;否则令处理器停机,等待 go_i 重新启动。 wire ex_led_event = idex_valid_q && idex_ecall_q && (ex_src1 == 32'd34); wire ex_halt_event = idex_valid_q && idex_ecall_q && (ex_src1 != 32'd34); - // A load in EX followed by a consumer in ID needs one bubble. ALU and - // branch dependencies are handled by the forwarding network above. + // EX 级为 load、而 ID 级指令立即使用其结果时,需要插入一个气泡。 + // ALU 与分支的数据相关由上面的前递网络解决。 wire load_use_hazard = ifid_valid_q && idex_valid_q && idex_mem_to_reg_q && (idex_rd_q != 5'd0) && ((d_uses_rs1 && (d_src1_idx == idex_rd_q)) || (d_uses_rs2 && (d_src2_idx == idex_rd_q))); // ------------------------------------------------------------------------- - // Interrupt sampling and priority selection. - // IRQ3 has the highest priority, then IRQ2, then IRQ1. A nested request - // is admitted only when its priority is higher than the current level. + // 中断采样与优先级选择。 + // IRQ3 优先级最高,其次 IRQ2,最后 IRQ1。嵌套请求只有在优先级高于 + // 当前级别时才会被接纳。 // ------------------------------------------------------------------------- + // 上升沿检测:只在 irq_i 由 0 变 1 的那一拍产生中断事件。 wire [2:0] irq_event = irq_sync2_q & ~irq_prev_q; reg irq_selected_valid; reg [1:0] irq_selected_level; + // 优先级仲裁:在当前级别允许的条件下选出优先级最高的中断源。 always @* begin irq_selected_valid = 1'b0; irq_selected_level = 2'd0; @@ -732,20 +775,22 @@ module cpu21_riscv_redirect_int_bpb #( end wire ex_uret_redirect = idex_valid_q && idex_uret_q; - // uepc_q is the active context's architectural return PC. The stack - // stores the interrupted outer contexts; using uepc_q here preserves the - // course-book sequence that saves/restores uepc with CSRRW. + // uepc_q 是当前运行上下文的架构返回 PC,栈中缓存的是被中断的外层 + // 上下文。这里使用 uepc_q,与课程讲义中用 CSRRW 保存/恢复 uepc 的 + // 流程保持一致。 wire [31:0] ex_return_pc = uepc_q; + // 响应中断的条件:有已选中中断、MIE 使能、栈未溢出、未停机,且没有 + // 正在处理的重定向/URET/停机事件。 wire take_irq = irq_selected_valid && ustatus_q[0] && (irq_depth_q < IRQ_STACK_DEPTH) && !halted_q && !ex_redirect_valid && !ex_uret_redirect && !ex_halt_event; - // The EX instruction is allowed to complete when an interrupt is taken. - // Resume at the next architectural PC; if EX is a correctly predicted - // taken branch, that PC is its target. If EX is empty, IF/ID is the - // oldest instruction that has not executed yet. + // 响应中断时允许 EX 级指令正常完成。恢复地址取"下一个架构 PC":若 + // EX 是一条预测正确且已跳转的分支,则该 PC 就是它的目标地址;若 EX + // 为空,则 IF/ID 中保存的就是尚未执行的最老指令。 wire [31:0] irq_save_pc = idex_valid_q ? ((idex_branch_q && ex_branch_taken) ? ex_target : (idex_pc_q + 32'd4)) : (ifid_valid_q ? ifid_pc_q : pc_q); + // 依据被选中的优先级选择对应的中断入口地址。 reg [31:0] irq_vector; always @* begin case (irq_selected_level) @@ -756,6 +801,7 @@ module cpu21_riscv_redirect_int_bpb #( endcase end + // 组合逻辑计算下一拍的挂起位与 CSR(ustatus/uepc)取值。 reg [ 2:0] irq_pending_d; reg [31:0] ustatus_d; reg [31:0] uepc_d; @@ -792,14 +838,16 @@ module cpu21_riscv_redirect_int_bpb #( end // ------------------------------------------------------------------------- - // External/debug outputs. + // 对外输出与调试信号。 // ------------------------------------------------------------------------- + // 存储器接口:指令地址来自取指 PC;数据地址/写数据/写掩码来自 MEM 级。 assign instr_addr_o = pc_q; assign data_addr_o = exmem_alu_result_q; assign data_wdata_o = exmem_write_data_q; assign data_wstrb_o = (exmem_valid_q && exmem_mem_write_q) ? exmem_wstrb_q : 4'b0; assign data_we_o = |data_wstrb_o; + // LED 输出与流水线各级 PC/指令回显(用于调试与波形观察)。 assign led_data_o = led_data_q; assign led_valid_o = led_valid_q; assign if_pc_o = pc_q; @@ -832,15 +880,18 @@ module cpu21_riscv_redirect_int_bpb #( assign prediction_success_count_o = prediction_success_count_q; assign prediction_failure_count_o = prediction_failure_count_q; + // 需要冲刷年轻指令/气泡的情况:中断、URET、ecall 停机、分支重定向。 wire flush_younger = take_irq || ex_uret_redirect || ex_halt_event || ex_redirect_valid; + // load-use 冒险且无需冲刷时,插入一个气泡(停顿一拍)。 wire pipeline_stall = load_use_hazard && !flush_younger && !halted_q; // ------------------------------------------------------------------------- - // State update. Ordering follows the five-stage pipeline: WB commit, - // MEM/WB capture, EX/MEM capture, then ID/EX and IF/ID/PC control. + // 状态更新。顺序遵循五级流水线:先 WB 写回,再捕获 MEM/WB、EX/MEM, + // 最后处理 ID/EX 与 IF/ID/PC 的控制。 // ------------------------------------------------------------------------- integer r; integer s; + // 复位时清空所有流水寄存器、寄存器堆、中断状态与性能计数器。 always @(posedge clk or posedge reset) begin if (reset) begin pc_q <= RESET_PC; @@ -935,12 +986,12 @@ module cpu21_riscv_redirect_int_bpb #( if (!halted_q) cycle_count_q <= cycle_count_q + 16'd1; led_valid_q <= 1'b0; - // WB + // WB 级:写回寄存器堆(x0 恒为 0)。 if (memwb_valid_q && memwb_reg_write_q && (memwb_rd_q != 5'd0)) regfile[memwb_rd_q] <= wb_value; regfile[0] <= 32'b0; - // Interrupt synchronizers and pending latches. + // 中断输入打两拍同步,并锁存挂起状态与 CSR。 irq_sync1_q <= irq_i; irq_sync2_q <= irq_sync1_q; irq_prev_q <= irq_sync2_q; @@ -970,7 +1021,7 @@ module cpu21_riscv_redirect_int_bpb #( end end - // MEM/WB + // MEM/WB 流水寄存器捕获。 memwb_valid_q <= exmem_valid_q; memwb_pc_q <= exmem_pc_q; memwb_ir_q <= exmem_ir_q; @@ -983,7 +1034,7 @@ module cpu21_riscv_redirect_int_bpb #( memwb_mem_to_reg_q <= exmem_mem_to_reg_q; memwb_wb_sel_q <= exmem_wb_sel_q; - // EX/MEM + // EX/MEM 流水寄存器捕获。 exmem_valid_q <= idex_valid_q; exmem_pc_q <= idex_pc_q; exmem_ir_q <= idex_ir_q; @@ -998,8 +1049,8 @@ module cpu21_riscv_redirect_int_bpb #( exmem_wstrb_q <= ex_store_wstrb; exmem_wb_sel_q <= idex_wb_sel_q; - // Control priority: interrupt, URET, ecall halt, branch redirect, - // then load-use stall, then ordinary pipeline advance. + // 控制优先级:中断 > URET > ecall 停机 > 分支重定向 > + // load-use 停顿 > 正常流水推进。 if (flush_younger || pipeline_stall || halted_q) begin idex_valid_q <= 1'b0; idex_pred_taken_q <= 1'b0; @@ -1076,6 +1127,8 @@ module cpu21_riscv_redirect_int_bpb #( ifid_pred_target_q <= bpb_predict_target; end + // 性能计数器:停顿/气泡、条件分支实际跳转数、无条件跳转数, + // 以及分支预测的成功/失败次数。 if (pipeline_stall) begin stall_count_q <= stall_count_q + 16'd1; bubble_count_q <= bubble_count_q + 16'd1; diff --git a/testbench/programs/risc-v-benchmark_ccab.hex b/testbench/programs/risc-v-benchmark_ccab.hex index 2131925..1ac26ce 100644 --- a/testbench/programs/risc-v-benchmark_ccab.hex +++ b/testbench/programs/risc-v-benchmark_ccab.hex @@ -1,4 +1,3 @@ -v2.0 raw 00100493 0100006f 00100493 diff --git a/testbench/programs/risc-v-分支预测测试.hex b/testbench/programs/risc-v-分支预测测试.hex new file mode 100644 index 0000000..59ab946 --- /dev/null +++ b/testbench/programs/risc-v-分支预测测试.hex @@ -0,0 +1,67 @@ +00500493 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +fff48493 +f80492e3 +00100413 +0ff00913 +00100493 +00300993 +01240c63 +00840a63 +00100493 +00200913 +00300993 +00400a13 +00949a63 +01249863 +00100493 +00200913 +00300993 +00c000ef +00a00893 +00000073 +00000413 +00140413 +00800533 +02200893 +00000073 +00008067 diff --git a/testbench/risc-v-branch-predict.hex b/testbench/risc-v-branch-predict.hex new file mode 100644 index 0000000..59ab946 --- /dev/null +++ b/testbench/risc-v-branch-predict.hex @@ -0,0 +1,67 @@ +00500493 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +0140006f +00100493 +00200913 +00300993 +00400a13 +fff48493 +f80492e3 +00100413 +0ff00913 +00100493 +00300993 +01240c63 +00840a63 +00100493 +00200913 +00300993 +00400a13 +00949a63 +01249863 +00100493 +00200913 +00300993 +00c000ef +00a00893 +00000073 +00000413 +00140413 +00800533 +02200893 +00000073 +00008067 diff --git a/testbench/tb_cpu21_riscv_redirect_int_bpb.v b/testbench/tb_cpu21_riscv_redirect_int_bpb.v index c1940bf..16acc68 100644 --- a/testbench/tb_cpu21_riscv_redirect_int_bpb.v +++ b/testbench/tb_cpu21_riscv_redirect_int_bpb.v @@ -17,6 +17,7 @@ module tb_cpu21_riscv_redirect_int_bpb #( parameter ROM_FILE = "risc-v-benchmark_ccab.hex" // risc-v-benchmark_ccab.hex // cpu21_riscv_redirect_int_bpb_rom.hex + // risc-v-branch-predict.hex ); localparam integer ROM_WORDS = 1024; localparam integer RAM_WORDS = 1024;