Files
risc-v-cpu/rtl/cpu21_riscv_irq_ctrl.v
T
Anccend 556fbe9530 目录整理:RTL/上板/仿真分开存放,测试 ROM 镜像归入 testbench/rom
- rtl/:12 个可综合的处理器源文件(顶层 cpu21_riscv_redirect_int_bpb.v,其余为各功能单元)。
- board/:板级顶层 cpu21_board_top.v、引脚约束 cpu21_board_top.xdc、片内 ROM 初始化
  cpu21_board_rom.vh 与短延时版本 cpu21_board_rom_fast.vh。
- testbench/:保留三个测试平台;ROM 镜像统一放到 testbench/rom/,测试程序源码与期望值
  仍在 testbench/programs/(其中的 .hex 是 RARS 原始输出,与 rom/ 里改过显示方式的版本
  内容不同,两者都保留)。
- 同步更新:两个测试平台的 ROM 探测路径改为 testbench/rom/(并在头注释里补充说明:
  PowerShell 会把 -generic_top/--testplusarg 参数里的 "=" 拆开、Verilog 字符串中的
  路径要用正斜杠);tools/hex2vh.py 的用法示例指向 board/cpu21_board_top.v;
  说明文档新增“目录结构”一节并同步所有路径引用;.gitignore 补充 .Xil/。
- 清理:删除 *.hex.lst 中间文件与空的 .Xil/ 目录。
- 验证:isa_test 24/24、lui_test 4/4、mmio_test 5/5 三项回归通过;两个基线(5560 / 6003
  周期)与整理前一致;板级仿真(默认 ROM 首次写入 LED=0001、快速 ROM 跑马灯循环)正常;
  Vivado 工程源文件路径同步到 rtl/ 与 board/ 后综合通过(LUT 5276、23 个引脚,与整理前
  完全一致),因此已有 bit 文件仍然有效。
2026-09-13 18:06:21 +08:00

189 lines
6.8 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
// -----------------------------------------------------------------------------
// 中断控制器 + 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