设计并实现8源可屏蔽中断控制器,支持优先级仲裁、中断向量化和多种EOI(End of Interrupt)模式。中断是CPU与外设高效协作的核心机制——没有中断,CPU只能忙等轮询,浪费99%的处理能力。
中断的本质是:外设有事要做时,主动打断CPU当前的工作,让CPU优先处理紧急事务。这就像你正在看书,电话响了——你记下当前页码,接电话,挂了电话后继续看。
| 特性 | 轮询(Polling) | 中断(Interrupt) |
|---|---|---|
| CPU利用率 | 极低(忙等浪费周期) | 高(无事时CPU做别的工作) |
| 响应延迟 | 不确定(取决于轮询间隔) | 确定(几个时钟周期内) |
| 编程复杂度 | 简单(死循环检查) | 复杂(需要保存/恢复上下文) |
| 多设备 | 困难(需要依次轮询所有设备) | 自然支持(各设备独立请求) |
| 典型场景 | 简单嵌入式、无OS裸机 | 操作系统、多任务系统 |
// interrupt_controller.v - 8源可屏蔽中断控制器
module interrupt_controller (
input wire clk,
input wire rst_n,
input wire cs,
input wire rw,
input wire [1:0] reg_addr,
input wire [7:0] cpu_data_in,
output reg [7:0] cpu_data_out,
input wire [7:0] irq_sources,
output reg int_request,
output reg [2:0] int_vector,
input wire int_ack
);
reg [7:0] irq_mask; // 屏蔽寄存器
reg [7:0] irq_pending; // 挂起寄存器
reg [7:0] irq_in_service; // 服务中寄存器
reg auto_eoi;
// 优先级仲裁:找到最高优先级的有效请求
reg [2:0] best_irq;
reg has_irq;
always @(*) begin
has_irq = 1'b0;
best_irq = 3'd0;
if (irq_pending[0] & irq_mask[0] & ~irq_in_service[0])
begin best_irq = 3'd0; has_irq = 1'b1; end
else if (irq_pending[1] & irq_mask[1] & ~irq_in_service[1])
begin best_irq = 3'd1; has_irq = 1'b1; end
else if (irq_pending[2] & irq_mask[2] & ~irq_in_service[2])
begin best_irq = 3'd2; has_irq = 1'b1; end
else if (irq_pending[3] & irq_mask[3] & ~irq_in_service[3])
begin best_irq = 3'd3; has_irq = 1'b1; end
else if (irq_pending[4] & irq_mask[4] & ~irq_in_service[4])
begin best_irq = 3'd4; has_irq = 1'b1; end
else if (irq_pending[5] & irq_mask[5] & ~irq_in_service[5])
begin best_irq = 3'd5; has_irq = 1'b1; end
(irq_pending[6] & irq_mask[6] & ~irq_in_service[6])
begin best_irq = 3'd6; has_irq = 1'b1; end
else if (irq_pending[7] & irq_mask[7] & ~irq_in_service[7])
begin best_irq = 3'd7; has_irq = 1'b1; end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
irq_mask <= 8'd0;
irq_pending <= 8'd0;
irq_in_service<= 8'd0;
auto_eoi <= 1'b0;
int_request <= 1'b0;
int_vector <= 3'd0;
cpu_data_out <= 8'd0;
end else begin
// 捕获中断请求
irq_pending <= irq_pending | (irq_sources & irq_mask);
// CPU应答处理
if (int_ack && has_irq) begin
int_vector <= best_irq;
irq_in_service[best_irq] <= 1'b1;
irq_pending[best_irq] <= 1'b0;
int_request <= 1'b0;
if (auto_eoi)
irq_in_service <= 8'd0;
end
// 生成中断请求
if (has_irq && !int_ack)
int_request <= 1'b1;
// CPU寄存器写入
if (cs && !rw) begin
case (reg_addr)
2'b00: irq_mask <= cpu_data_in;
2'b01: irq_pending <= irq_pending & ~cpu_data_in;
2'b10: irq_in_service <= irq_in_service & ~cpu_data_in;
2'b11: auto_eoi <= cpu_data_in[0];
endcase
end
if (cs && rw) begin
case (reg_addr)
2'b00: cpu_data_out <= irq_mask;
2'b01: cpu_data_out <= irq_pending;
2'b10: cpu_data_out <= irq_in_service;
2'b11: cpu_data_out <= {7'd0, auto_eoi};
endcase
end
end
end
endmodule
verilator --lint-only 检查,无错误无警告。
CPU收到中断请求后,根据int_vector跳转到对应的中断服务程序。向量表放在内存低地址:
; 定时器中断服务程序模板
timer_isr:
PHA ; 保存A
PHX ; 保存X
PHY ; 保存Y
; ... 处理定时器中断 ...
LDA #$01 ; 写1到bit0清除挂起
STA $FF21 ; EOI: 清除IRR
STA $FF22 ; EOI: 清除ISR
PLY ; 恢复Y
PLX ; 恢复X
PLA ; 恢复A
RTI ; 中断返回
当前设计不支持中断嵌套——一个中断正在服务时,所有同级和低级中断都被阻塞。修改设计:当高优先级中断到来时,允许打断当前低优先级中断的ISR。提示:在int_ack时只设置当前irq的ISR位,仲裁时排除ISR中优先级更高或相等的位。
实现优先级轮转(Round-Robin):每次中断服务完成后,刚服务完的中断源优先级降到最低,其他中断源优先级提升。这保证了低优先级中断不会永远被饿死。添加一个优先级基准寄存器来实现轮转。
IRQ7设计为NMI(不可屏蔽中断)。NMI的特点是:不能被屏蔽(忽略IMR),用于处理灾难性事件(掉电、看门狗超时、总线错误)。修改控制器,让IRQ7绕过屏蔽寄存器,并确保NMI不会被丢失。
当多个外设共享同一条IRQ线时(类似PCI的INTx共享),需要软件方法判断具体是哪个设备请求了中断。设计一个"共享中断"机制:ISR中依次检查每个共享设备的就绪位,找到请求者后处理之。修改硬件添加"中断确认"握手协议。
你实现了完整的可屏蔽中断控制器!这包括:
中断是操作系统的灵魂——有了它,CPU终于可以高效地同时处理多个任务了!