tinyriscv学习记录之五 二十六、clint.v的学习阅读代码熟悉的感觉又出现了反复咀嚼难以下咽。和阅读div.v的代码时候一样看代码找不到突破口的话那么突破口就是不熟悉试商法的原理我想在clint.v也是一样。如果清楚中断处理的过程那么clint.v的代码也就能看明白。所以还是先搞懂中断处理的流程弄清需要中断需要做哪些事。二十七、先知道csr寄存器在RISC-V 这种架构里中断/异常处理和 CSR 寄存器分不开的。原因很简单中断处理至少要解决 4 件事而这 4 件事恰好都要靠 CSR 来完成记录从哪里回来→mepc记录为什么进中断→mcause记录/修改中断使能状态→mstatus决定中断处理程序入口地址→mtvec也就是说CPU 一旦进入中断不能只是“跳走”这么简单还必须保存现场记录原因关闭或管理中断使能跳到统一的处理入口处理完再按保存的信息返回而这些信息的标准存放位置就是 CSR。二十八、重要的CSR寄存器CSR 名称全称功能mtvecMachine Trap-Vector Base Address保存中断/异常入口地址进入 trap 后跳到这里mepcMachine Exception Program Counter保存异常/中断发生时的返回地址mret时跳回这里mstatusMachine Status Register控制和保存中断使能状态进入 trap 时关闭中断返回时恢复mcauseMachine Cause Register记录本次异常/中断的原因二十九、clint.v的中断处理过程就是先识别 trap 类型暂停流水线分几个时钟周期依次写mepc/mstatus/mcause保存现场之后再让ex跳到mtvec执行mret时则恢复mstatus并让ex跳回mepc。三十、clint.v代码分成4步1. 先判断“是不是要处理 trap”clint先根据当前输入判断int_stateecall / ebreak→ 同步异常S_INT_SYNC_ASSERTint_flag_i ! INT_NONE且global_int_en_i True→ 异步中断S_INT_ASYNC_ASSERTmret→ 中断返回S_INT_MRET否则 →S_INT_IDLE也就是说第一步只是分类现在是同步异常异步中断还是从中断返回2. 一旦开始处理就先暂停流水线assign hold_flag_o ((int_state ! S_INT_IDLE) | (csr_state ! S_CSR_IDLE)) ? HoldEnable : HoldDisable;意思是只要检测到中断/异常或者 CSR 处理流程还没结束就拉高hold_flag_o作用防止流水线继续往前跑给clint腾出几个时钟周期依次写mepc/mstatus/mcause所以clint不是一发现中断就马上跳走而是先冻结现场。3. 进入中断时分几拍保存现场这部分由csr_state控制。情况 A同步异常或异步中断当csr_state S_CSR_IDLE时如果发现要进中断第 1 拍准备异常原因和返回地址对同步异常cause设成对应异常码比如ecall→11ebreak→3对异步中断cause 32h80000004同时算好inst_addr也就是将来写进mepc的返回地址这里为什么要算inst_addr 因为中断发生时当前流水线可能正遇到普通顺序执行跳转指令多周期除法所以作者要修正“真正该保存哪个 PC”。第 2 拍写mepc状态转到S_CSR_MEPC这时输出we_o WriteEnablewaddr_o CSR_MEPCdata_o inst_addr作用把将来返回时要继续执行的位置保存到mepc第 3 拍写mstatus状态转到S_CSR_MSTATUS这时写mstatus把全局中断关掉data_o {csr_mstatus[31:4], 1b0, csr_mstatus[2:0]};意思是清掉中断使能位防止处理当前 trap 时又被新的中断打断第 4 拍写mcause状态转到S_CSR_MCAUSE这时输出waddr_o CSR_MCAUSEdata_o cause作用记录这次 trap 的原因是ecall是ebreak还是异步中断第 5 拍正式通知 EX 跳转到中断入口还是在S_CSR_MCAUSE这个阶段clint会同时发int_assert_o INT_ASSERTint_addr_o csr_mtvec意思是现在现场已经保存完了可以让ex去把 PC 改到mtvec开始执行中断/异常处理程序这点很关键不是刚检测到中断就跳而是先写完关键 CSR再跳到mtvec。这样 trap 语义才完整。4. 中断返回 mret 的过程如果当前指令是 mretint_state S_INT_MRET。这时不会走 mepc - mstatus - mcause 这一套进入中断流程 而是进入S_CSR_MSTATUS_MRET返回时做两件事第 1 件恢复 mstatusdata_o {csr_mstatus[31:4], csr_mstatus[7], csr_mstatus[2:0]};意思是把中断前保存/对应的使能状态恢复回来第 2 件通知 EX 跳回 mepc同一阶段输出int_assert_o INT_ASSERTint_addr_o csr_mepc意思是不再跳去 mtvec而是跳回之前保存的返回地址 mepc这样中断处理程序执行完后CPU 就能回到原程序继续运行。三十、csr_state的状态推进顺序为什么是这样的状态流转是S_CSR_IDLE - S_CSR_MEPC - S_CSR_MSTATUS - S_CSR_MCAUSE - S_CSR_IDLE对应的 CSR 操作顺序是写mepc保存中断/异常发生时的返回地址写mstatus关闭全局中断防止处理中再被打断写mcause记录本次 trap 的原因然后才通知跳转到mtvec也就是说实际顺序是先保存返回地址 - 再关中断 - 再记录原因 - 最后跳去中断入口三十一、顺序是先保存返回地址 - 再关中断 - 再记录原因 - 最后跳去中断入口。操作系统中是先关中断为什么这里不是呢不能先关中断再保存返回地址再记录原因先关中断这种思路的重点是先把门关上再整理现场适合这种情况硬件能自动抓住异常现场或设计上最怕 trap 期间再次被打断或有更底层机制保证返回地址不会丢然而“先保存返回地址再关中断”之所以在clint.v里更自然是因为mepc是最关键的现场关系到能不能正确返回mepc还需要根据当前流水线状态修正计算不是现成值这个实现已经用hold_flag_o先稳住了流程所以不必把“立刻关中断”放在第一位因此作者就按先保现场、再改状态、再记原因的顺序来写