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/ 下。
189 lines
6.8 KiB
Verilog
189 lines
6.8 KiB
Verilog
`timescale 1ns / 1ps
|
||
`default_nettype none
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// 中断控制器 + ustatus/uepc CSR + 嵌套返回栈。
|
||
//
|
||
// IRQ3 优先级最高,其次 IRQ2,最后 IRQ1;嵌套请求只有优先级高于当前级别
|
||
// 才会被接纳。中断入口地址由优先级选出,返回地址保存在 epc_stack 中,
|
||
// 当前上下文的活动返回 PC 放在 uepc_q(与课程讲义用 CSRRW 保存/恢复
|
||
// uepc 的流程一致)。
|
||
// -----------------------------------------------------------------------------
|
||
module cpu21_riscv_irq_ctrl #(
|
||
parameter [31:0] IRQ1_VECTOR = 32'h0000_30ac,
|
||
parameter [31:0] IRQ2_VECTOR = 32'h0000_31e4,
|
||
parameter [31:0] IRQ3_VECTOR = 32'h0000_3310,
|
||
parameter IRQ_STACK_DEPTH = 4,
|
||
// ustatus 复位值。bit0 为 MIE。课程的中断测试程序只会在中断处理
|
||
// 程序中设置 MIE,若复位后不立即置位 MIE,主程序将永远无法响应
|
||
// 第一次中断,因此这里默认打开 MIE。
|
||
parameter [31:0] USTATUS_INIT = 32'h0000_0001
|
||
) (
|
||
input wire clk,
|
||
input wire reset,
|
||
input wire [ 2:0] irq_i,
|
||
input wire halted_i,
|
||
// EX 级正在处理重定向/URET/停机,此时不允许响应中断
|
||
input wire ex_block_i,
|
||
// 中断发生时要保存的返回地址(由顶层根据流水线状态给出)
|
||
input wire [31:0] save_pc_i,
|
||
// EX 级的 URET
|
||
input wire uret_i,
|
||
// EX 级 CSR 指令(CSRRW/CSRRSI/CSRRCI)
|
||
input wire csr_valid_i,
|
||
input wire csr_set_i,
|
||
input wire csr_clear_i,
|
||
input wire csr_write_i,
|
||
input wire [11:0] csr_addr_i,
|
||
input wire [31:0] csr_imm_i,
|
||
input wire [31:0] csr_wdata_i,
|
||
// 控制结果
|
||
output wire take_irq_o,
|
||
output wire [31:0] vector_o,
|
||
output wire [31:0] return_pc_o,
|
||
output wire [ 2:0] pending_o,
|
||
output wire [ 1:0] level_o,
|
||
output wire [31:0] uepc_o,
|
||
output wire [31:0] ustatus_o
|
||
);
|
||
reg [31:0] ustatus_q;
|
||
reg [31:0] uepc_q;
|
||
reg [ 1:0] irq_current_q;
|
||
reg [ 2:0] irq_pending_q;
|
||
reg [ 2:0] irq_sync1_q;
|
||
reg [ 2:0] irq_sync2_q;
|
||
reg [ 2:0] irq_prev_q;
|
||
reg [31:0] epc_stack [0:IRQ_STACK_DEPTH-1];
|
||
reg [31:0] status_stack [0:IRQ_STACK_DEPTH-1];
|
||
reg [ 1:0] priority_stack [0:IRQ_STACK_DEPTH-1];
|
||
reg [ 2:0] irq_depth_q;
|
||
|
||
reg irq_selected_valid;
|
||
reg [ 1:0] irq_selected_level;
|
||
reg [31:0] irq_vector;
|
||
reg [ 2:0] irq_pending_d;
|
||
reg [31:0] ustatus_d;
|
||
reg [31:0] uepc_d;
|
||
|
||
integer s;
|
||
|
||
// 上升沿检测:只在 irq_i 由 0 变 1 的那一拍产生中断事件。
|
||
wire [ 2:0] irq_event = irq_sync2_q & ~irq_prev_q;
|
||
|
||
// 优先级仲裁:在当前级别允许的条件下选出优先级最高的中断源。
|
||
always @* begin
|
||
irq_selected_valid = 1'b0;
|
||
irq_selected_level = 2'd0;
|
||
if (irq_pending_q[2] && (irq_current_q < 2'd3)) begin
|
||
irq_selected_valid = 1'b1;
|
||
irq_selected_level = 2'd3;
|
||
end else if (irq_pending_q[1] && (irq_current_q < 2'd2)) begin
|
||
irq_selected_valid = 1'b1;
|
||
irq_selected_level = 2'd2;
|
||
end else if (irq_pending_q[0] && (irq_current_q < 2'd1)) begin
|
||
irq_selected_valid = 1'b1;
|
||
irq_selected_level = 2'd1;
|
||
end
|
||
end
|
||
|
||
// 依据被选中的优先级选择对应的中断入口地址。
|
||
always @* begin
|
||
case (irq_selected_level)
|
||
2'd1: irq_vector = IRQ1_VECTOR;
|
||
2'd2: irq_vector = IRQ2_VECTOR;
|
||
2'd3: irq_vector = IRQ3_VECTOR;
|
||
default: irq_vector = IRQ1_VECTOR;
|
||
endcase
|
||
end
|
||
|
||
// 响应中断的条件:有已选中中断、MIE 使能、栈未溢出、未停机,且没有
|
||
// 正在处理的重定向/URET/停机事件。
|
||
assign take_irq_o = irq_selected_valid && ustatus_q[0] &&
|
||
(irq_depth_q < IRQ_STACK_DEPTH) && !halted_i &&
|
||
!ex_block_i;
|
||
|
||
assign vector_o = irq_vector;
|
||
assign return_pc_o = uepc_q;
|
||
assign pending_o = irq_pending_q;
|
||
assign level_o = irq_current_q;
|
||
assign uepc_o = uepc_q;
|
||
assign ustatus_o = ustatus_q;
|
||
|
||
// 组合逻辑计算下一拍的挂起位与 CSR(ustatus/uepc)取值。
|
||
always @* begin
|
||
irq_pending_d = irq_pending_q | irq_event;
|
||
if (take_irq_o) begin
|
||
case (irq_selected_level)
|
||
2'd1: irq_pending_d[0] = 1'b0;
|
||
2'd2: irq_pending_d[1] = 1'b0;
|
||
2'd3: irq_pending_d[2] = 1'b0;
|
||
default: irq_pending_d = irq_pending_d;
|
||
endcase
|
||
end
|
||
|
||
ustatus_d = ustatus_q;
|
||
uepc_d = uepc_q;
|
||
if (csr_valid_i && csr_set_i) begin
|
||
if (csr_addr_i == 12'h004) ustatus_d = ustatus_q | csr_imm_i;
|
||
else if (csr_addr_i == 12'h041) uepc_d = uepc_q | csr_imm_i;
|
||
end else if (csr_valid_i && csr_clear_i) begin
|
||
if (csr_addr_i == 12'h004) ustatus_d = ustatus_q & ~csr_imm_i;
|
||
else if (csr_addr_i == 12'h041) uepc_d = uepc_q & ~csr_imm_i;
|
||
end else if (csr_valid_i && csr_write_i) begin
|
||
if (csr_addr_i == 12'h004) ustatus_d = csr_wdata_i;
|
||
else if (csr_addr_i == 12'h041) uepc_d = csr_wdata_i;
|
||
end
|
||
|
||
if (take_irq_o) ustatus_d[0] = 1'b0;
|
||
else if (uret_i && (irq_depth_q != 0)) ustatus_d = status_stack[irq_depth_q-1'b1];
|
||
|
||
if (take_irq_o) uepc_d = save_pc_i;
|
||
else if (uret_i && (irq_depth_q > 1)) uepc_d = epc_stack[irq_depth_q-2];
|
||
else if (uret_i && (irq_depth_q == 1)) uepc_d = 32'b0;
|
||
end
|
||
|
||
// 复位时清空中断状态与嵌套栈。
|
||
always @(posedge clk or posedge reset) begin
|
||
if (reset) begin
|
||
ustatus_q <= USTATUS_INIT;
|
||
uepc_q <= 32'b0;
|
||
irq_current_q <= 2'b0;
|
||
irq_pending_q <= 3'b0;
|
||
irq_sync1_q <= 3'b0;
|
||
irq_sync2_q <= 3'b0;
|
||
irq_prev_q <= 3'b0;
|
||
irq_depth_q <= 3'b0;
|
||
for (s = 0; s < IRQ_STACK_DEPTH; s = s + 1) begin
|
||
epc_stack[s] <= 32'b0;
|
||
status_stack[s] <= 32'b0;
|
||
priority_stack[s] <= 2'b0;
|
||
end
|
||
end else begin
|
||
// 中断输入打两拍同步,并锁存挂起状态与 CSR。
|
||
irq_sync1_q <= irq_i;
|
||
irq_sync2_q <= irq_sync1_q;
|
||
irq_prev_q <= irq_sync2_q;
|
||
irq_pending_q <= irq_pending_d;
|
||
ustatus_q <= ustatus_d;
|
||
uepc_q <= uepc_d;
|
||
|
||
if (take_irq_o) begin
|
||
if (irq_depth_q < IRQ_STACK_DEPTH) begin
|
||
epc_stack[irq_depth_q] <= save_pc_i;
|
||
status_stack[irq_depth_q] <= ustatus_q;
|
||
priority_stack[irq_depth_q] <= irq_current_q;
|
||
irq_depth_q <= irq_depth_q + 3'd1;
|
||
end
|
||
irq_current_q <= irq_selected_level;
|
||
end else if (uret_i) begin
|
||
if (irq_depth_q != 0) begin
|
||
irq_depth_q <= irq_depth_q - 3'd1;
|
||
irq_current_q <= priority_stack[irq_depth_q-1'b1];
|
||
end
|
||
end
|
||
end
|
||
end
|
||
endmodule
|
||
|
||
`default_nettype wire
|