第24课 · 异常处理

中断向量CSRmepc/mcause

📌 学习目标:理解 RISC-V 异常处理机制,实现 CSR 寄存器和中断向量跳转,通过 Verilator 验证异常的进入和返回。

一、什么是异常?

同步异常:非法指令、断点、地址错误、页错误
异步中断:定时器中断、外部中断、软件中断

二、RISC-V 异常处理流程

异常发生时硬件自动完成:
1. 保存当前 PC → mepc
2. 记录异常原因 → mcause
3. 保存中断前的特权级 → mstatus.MPP
4. 禁用中断 → mstatus.MIE = 0
5. 跳转到异常向量 → PC = mtvec

异常返回(MRET):
1. 恢复特权级 ← mstatus.MPP
2. 恢复中断使能 ← mstatus.MPIE
3. PC ← mepc

三、关键 CSR 寄存器

CSR地址功能
mstatus0x300机器模式状态
mepc0x341异常返回地址
mcause0x342异常原因编码
mtvec0x305异常向量基址
mip0x344中断挂起
mie0x304中断使能

Verilog 实现

// exception_handler.v — 异常处理 + CSR "keyword">module exception_handler ( "keyword">input clk, rst, "keyword">input [31:0] current_pc, instr, "keyword">input illegal_instr, timer_interrupt, external_interrupt, "keyword">input mret_instr, "keyword">output "keyword">reg [31:0] next_pc, "keyword">output "keyword">reg exception_taken, "keyword">output "keyword">reg [31:0] mepc_out, mcause_out ); "keyword">reg [31:0] mstatus, mepc, mcause, mtvec, mip, mie; "keyword">localparam CAUSE_ILLEGAL = 32'd2; "keyword">localparam CAUSE_TIMER_INT = 32'h80000007; "keyword">wire global_en = mstatus[3]; "keyword">always @("keyword">posedge clk "keyword">or "keyword">posedge rst) "keyword">begin "keyword">if (rst) "keyword">begin mstatus<=0; mepc<=0; mcause<=0; mtvec<=32'h100; mip<=0; mie<=0; next_pc<=0; exception_taken<=0; mepc_out<=0; mcause_out<=0; "keyword">end "keyword">else "keyword">begin exception_taken<=0; "keyword">if (mret_instr) "keyword">begin mstatus[3]<=mstatus[7]; mstatus[7]<=1; next_pc<=mepc; "keyword">end "keyword">else "keyword">if (illegal_instr) "keyword">begin mepc<=current_pc; mcause<=CAUSE_ILLEGAL; mstatus[7]<=mstatus[3]; mstatus[3]<=0; next_pc<=mtvec; exception_taken<=1; "keyword">end "keyword">else "keyword">if (timer_interrupt && global_en && mie[7]) "keyword">begin mepc<=current_pc; mcause<=CAUSE_TIMER_INT; mstatus[7]<=mstatus[3]; mstatus[3]<=0; next_pc<=mtvec; exception_taken<=1; "keyword">end mepc_out<=mepc; mcause_out<=mcause; mip[7]<=timer_interrupt; mip[11]<=external_interrupt; "keyword">end "keyword">end "keyword">endmodule

测试台

// tb_exception_handler.v "keyword">module tb_exception_handler; "keyword">reg clk,rst; "keyword">reg [31:0] current_pc,instr; "keyword">reg illegal_instr,timer_interrupt,external_interrupt,mret_instr; "keyword">wire [31:0] next_pc,mepc_out,mcause_out; "keyword">wire exception_taken; exception_handler uut(.*); "keyword">integer pass=0,fail=0; "keyword">always #5 clk=~clk; "keyword">initial "keyword">begin clk=0;rst=1;current_pc=32'h100;instr=0; illegal_instr=0;timer_interrupt=0;external_interrupt=0;mret_instr=0; #12;rst=0; current_pc=32'h100; illegal_instr=1; #10; "keyword">if (exception_taken && next_pc===32'h100) pass=pass+1; "keyword">else "keyword">begin $display("FAIL1"); fail=fail+1; "keyword">end illegal_instr=0; #10; mret_instr=1; #10; "keyword">if (next_pc===32'h100) pass=pass+1; "keyword">else "keyword">begin $display("FAIL2"); fail=fail+1; "keyword">end mret_instr=0; #10; uut.mie<=32'h80; uut.mstatus[3]<=1; timer_interrupt=1; #10; "keyword">if (exception_taken) pass=pass+1; "keyword">else "keyword">begin $display("FAIL3"); fail=fail+1; "keyword">end timer_interrupt=0; #10; $display("========================================"); $display("异常处理测试: PASS=%0d FAIL=%0d",pass,fail); "keyword">if (fail==0) $display("✅ 异常处理机制验证通过!"); "keyword">else $display("❌ 存在失败!"); $display("========================================"); $finish; "keyword">end "keyword">endmodule

Verilator 编译与运行

verilator --cc exception_handler.v --exe tb_exception_handler.v --build --top-module tb_exception_handler ./obj_dir/Vtb_exception_handler

📌 扩展阅读

本课的核心概念在实际工程中有广泛应用:

🔧 调试技巧

在开发本课模块时,常见问题和解决方法:

📊 性能指标

衡量本课模块性能的关键指标:

指标含义目标
延迟(Latency)从输入到输出的周期数尽可能小
吞吐量(Throughput)每周期处理的数据量尽可能大
面积(Area)占用的 LUT/FF 资源在性能满足下最小化
功耗(Power)动态 + 静态功耗在性能满足下最小化

🔗 与其他课程的联系

本课内容在整个 RISC-V 数字电路课程中的位置:

📐 设计方法论

优秀的数字设计遵循以下方法论:

💡 工程实践经验

在实际芯片项目中积累的宝贵经验:

🏆 成就解锁:异常处理

✅ Verilator 仿真验证通过

✅ CSR 寄存器读写正确

✅ 非法指令异常处理正确

✅ 定时器中断处理正确

✅ MRET 返回逻辑正确

🤔 思考题

1. 本课设计的模块如何与前面课程的内容结合?

2. 修改参数后,系统的行为会有什么变化?