尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
异常响应机制
【一生一芯 / PA】异常响应机制从ecall到mtvec再到mret的完整代码解析代码环境NEMU~/ysyx-workbench/nemuAM~/ysyx-workbench/abstract-machineISAriscv32一、异常响应机制到底在解决什么问题在 PA3 中我们要让程序之间能切换执行流。但讲义特别强调不能用 call / jal 随便跳转 必须提供一种“限制入口”的执行流切换方式这种机制就是异常响应机制它的核心流程是异常 / 中断发生 - 硬件保存返回地址 - 硬件保存异常原因 - 硬件跳转到固定入口 mtvec - 软件保存上下文 - 软件处理异常 - 软件通过 mret 返回在 RISC-V 中这三个关键 CSR 是mtvec - 异常入口 mepc - 异常返回地址 mcause - 异常原因二、NEMU 硬件侧isa_raise_intr()文件nemu/src/isa/riscv32/system/intr.cword_t isa_raise_intr(word_t NO, vaddr_t epc) { // 1. 保存返回地址 csr_write(MEPC_ADDR, epc); // 2. 保存异常原因 csr_write(MCAUSE_ADDR, NO); // 3. 特殊处理 ebreak if (NO 3) { nemu_state.state NEMU_END; nemu_state.halt_pc epc; nemu_state.halt_ret 0; return epc; } // 4. 读取异常入口 word_t mtvec csr_read(MTVEC_ADDR); // 5. 返回 mtvec并保证 4 字节对齐 return mtvec ~0x3; }逐行解释csr_write(MEPC_ADDR, epc);保存异常发生时的 PC将来mret要返回这里。csr_write(MCAUSE_ADDR, NO);保存异常原因。例如11 - ecall 3 - ebreak 0x80000007 - 机器定时器中断if (NO 3)你的代码把ebreak特殊处理为 NEMU 正常结束。return mtvec ~0x3;把 PC 修改为mtvec这就是异常入口跳转。三、AM 软件侧cte_init()设置异常入口文件abstract-machine/am/src/riscv/nemu/cte.cbool cte_init(Context*(*handler)(Event, Context*)) { asm volatile(csrw mtvec, %0 : : r(__am_asm_trap)); user_handler handler; return true; }解释csrw mtvec, __am_asm_trap把异常入口设置为汇编函数__am_asm_trap。这样无论用户程序如何触发异常CPU 都只能跳转到mtvec __am_asm_trap这就是“限制入口”的具体实现。四、汇编异常入口保存和恢复上下文文件abstract-machine/am/src/riscv/nemu/trap.S__am_asm_trap: addi sp, sp, -CONTEXT_SIZE MAP(REGS, PUSH) csrr t0, mcause csrr t1, mstatus csrr t2, mepc STORE t2, OFFSET_EPC(sp) STORE t0, OFFSET_CAUSE(sp) STORE t1, OFFSET_STATUS(sp) mv a0, sp call __am_irq_handle mv sp, a0 LOAD t1, OFFSET_STATUS(sp) LOAD t2, OFFSET_EPC(sp) csrw mstatus, t1 csrw mepc, t2 MAP(REGS, POP) addi sp, sp, CONTEXT_SIZE mret逐段解释1. 保存通用寄存器addi sp, sp, -CONTEXT_SIZE MAP(REGS, PUSH)在栈上开辟上下文空间保存所有通用寄存器。2. 读取 CSRcsrr t0, mcause csrr t1, mstatus csrr t2, mepc把异常原因、状态和返回地址读出来。3. 保存到 ContextSTORE t2, OFFSET_EPC(sp) STORE t0, OFFSET_CAUSE(sp) STORE t1, OFFSET_STATUS(sp)写入 C 结构体Context。4. 调用 C 处理函数mv a0, sp call __am_irq_handle把Context *传给 C 函数。5. 恢复上下文csrw mstatus, t1 csrw mepc, t2 MAP(REGS, POP) mret恢复寄存器和 CSR最后mret返回。五、C 语言异常分发文件abstract-machine/am/src/riscv/nemu/cte.cContext* __am_irq_handle(Context *c) { if (user_handler) { Event ev {0}; switch (c-mcause) { case 11: if (c-gpr[17] -1) { ev.event EVENT_YIELD; } else { ev.event EVENT_SYSCALL; } c-mepc 4; break; case 0x80000007: ev.event EVENT_IRQ_TIMER; break; default: ev.event EVENT_ERROR; break; } c user_handler(ev, c); assert(c ! NULL); } return c; }逐行解释case 11:mcause 11表示ecall。if (c-gpr[17] -1)如果a7 -1说明是yield()否则是系统调用。c-mepc 4;跳过ecall指令返回后从下一条指令继续。case 0x80000007:mcause 0x80000007表示机器定时器中断。ev.event EVENT_IRQ_TIMER;交给用户注册的 handler 处理。六、用户侧事件处理函数修改后的trm.cstatic Context *event_handler(Event ev, Context *ctx) { switch (ev.event) { case EVENT_YIELD: break; case EVENT_SYSCALL: if (ctx-GPRx 0) { halt(ctx-GPR1); } break; case EVENT_IRQ_TIMER: break; default: panic(Unhandled event); break; } return ctx; }这个event_handler就是未来操作系统的入口雏形。七、定时器中断如何触发文件nemu/src/device/intr.cvoid dev_raise_intr() { cpu.pc isa_raise_intr(0x80000007, cpu.pc); }解释isa_raise_intr(0x80000007, cpu.pc)表示异常原因 机器定时器中断 返回地址 当前 PC然后pc mtvec进入__am_asm_trap。八、完整异常响应链路1.yield()链路用户程序执行 ecall ↓ NEMU isa_raise_intr(11, pc) ↓ mcause 11 mepc pc pc mtvec ↓ __am_asm_trap ↓ 保存 Context ↓ __am_irq_handle ↓ mcause 11 a7 -1 - EVENT_YIELD ↓ event_handler ↓ 返回 Context ↓ mret ↓ pc mepc2. 定时器中断链路Linux 虚拟定时器 ↓ alarm_sig_handler ↓ timer_intr ↓ dev_raise_intr ↓ isa_raise_intr(0x80000007, pc) ↓ mcause 0x80000007 mepc pc pc mtvec ↓ __am_asm_trap ↓ __am_irq_handle ↓ EVENT_IRQ_TIMER ↓ event_handler ↓ mret九、测试验证1. 编译 NEMUcd ~/ysyx-workbench/nemu make -j$(nproc)2. 运行中断测试export NEMU_HOME~/ysyx-workbench/nemu export AM_HOME~/ysyx-workbench/abstract-machine cd ~/ysyx-workbench/am-kernels/tests/am-tests make ARCHriscv32-nemu run mainargsi3. 成功标志会看到异常处理链反复执行hello_intr - __am_asm_trap - __am_irq_handle - simple_trap - putch最终输出ttttttttttt其中t 定时器中断 d 设备中断 y yield因为hello_intr()是死循环所以测试会一直运行timeout是正常的。十、代码和讲义对照异常响应机制对应代码保存返回地址csr_write(MEPC_ADDR, epc)保存异常原因csr_write(MCAUSE_ADDR, NO)跳转异常入口return mtvec ~0x3设置异常入口cte_init - csrw mtvec保存上下文trap.S异常分发__am_irq_handle用户处理event_handler异常返回mret十一、总结异常响应机制就是一套标准流程硬件保存 mepc / mcause跳转 mtvec 软件保存上下文 软件根据 mcause 分发事件 软件处理事件 软件mret 返回你的代码现在已经具备这条完整链路。接下来 PA3 要做的就是在这个机制之上实现1. 操作系统入口 event_handler 2. 用户程序加载器 3. 批处理调度
RELATED

相关推荐

Sentinel HASP HL 3.5 驱动安装全指南:老加密狗在 Win10/11 上的识别与修复

Sentinel HASP HL 3.5 驱动安装全指南:老加密狗在 Win10/11 上的识别与修复

简介:面向软件授权与版权保护场景的Sentinel硬件安全模块USB驱动程序包,专门解决Windows系统下Sentinel设备无法被识别、频繁报错或与操作系统通信异常的问题,适合IT运维及软件管理人员快速恢复加密狗等设备的正常连接,在软件授权…

📅 2026/10/4 22:23:30
用Cursor和ChatGPT大半年上线3个小程序:我的AI赚钱之路与TaoToken配置复盘

用Cursor和ChatGPT大半年上线3个小程序:我的AI赚钱之路与TaoToken配置复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/4 22:23:30
api-ms-win-core-sysinfo-l1-2-0.dll缺失修复详解

api-ms-win-core-sysinfo-l1-2-0.dll缺失修复详解

简介:当Windows 7系统报出“无法启动程序,因为计算机中丢失api-ms-win-core-sysinfo-l1-2-0.dll”时,依赖该组件的软件通常无法正常运行。该dll隶属系统核心信息库,负责向应用程序传递处理器类型、内存配置、操作系统版本等关键底…

📅 2026/10/4 22:23:30
MORE NEWS

更多资讯

📰

C#调用USB摄像头实战:DirectShow/AForge/OpenCvSharp选型与避坑指南

简介:面向在.NET平台使用C#操作USB摄像头的开发者,这份资源提供一套可直接运行的完整示例,覆盖摄像头枚举、连接、视频流启停、拍照抓帧与图片保存等关键环节。压缩包内共38个文件,包括6个C#源文件、10个动态库、3个可执行程序以及…

📰

中控Java二次开发demo实战:跑通、避坑与封装指南

简介:面向企业级考勤系统的开发者,中控Java二次开发demo.zip提供了一套直接可用的对接方案,适用于需要读取考勤记录、维护人员信息或集成考勤数据到业务系统的场景。资源以Java源码与配套文档为核心,压缩包整体约37.77MB&#xff…

📰

C# TCP/IP最简例程:TcpClient与TcpListener服务端客户端互通指南

简介:面向C#初学者的TCP/IP通信例程包,内含服务端与客户端两个独立完整模块,清晰演示了传输控制协议下如何通过TcpListener、TcpClient和Socket类完成建立连接、发送数据与接收响应的全过程,适合刚刚接触网络编程、希望快速跑通首…

📰

插件加载与激活失败:IAR、web boot与MusicFree排查指南

如果你最近在搜索引擎里只敲了 plugins 这一个词,大概率正面对下面三个场景之一:刚装好的嵌入式开发环境里多了一个 plugins 目录,不知道它到底是干嘛的;某个 Web 类应用启动时刷出一条以 "failed to load plugins web boot:…

📰

Chrome DevTools MCP 实战完整教程:把 MCP 配置改到 TaoToken 的调试链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

openrig 配置编排指南:Claude Code 与 Codex 多模型环境装配实践

1. 从零认识 openrig:它到底解决什么问题第一次看到 openrig 这个名字,很多人会以为是某个硬件支架项目,毕竟 rig 在英文里有“装配、支架”的意思。但结合 Claude Code、Codex、YAML、Node.js 这一串关键词,答案就清晰了&#xf…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬