RV特权架构小记

发布时间 2023-06-24 15:23:03作者: NOSAE

参考链接

https://github.com/Lingrui98/RISC-V-book

https://riscv.org/technical/specifications/

异常和中断

用户模式(U)所能提供的可以看成只能完成计算和访存等功能,我们需要更多现代的特性的时候,就必须进入最高特权的机器模式(M)或提供给操作系统的监管者模式(S),并且需要csr(控制状态寄存器)来帮助完成这样的扩展。

机器模式是唯一所有标准 RISC-V 处理器都必须实现的权限模式,该模式是hart(硬件线程)可以执行的最高权限模式。

我们熟悉的计算和访存等功能都在用户模式下完成,而这个模式的最重要功能之一则是负责处理拦截和处理异常。

rv将异常分为两种:

  1. 同步异常(指令执行期间产生,比如访问无效地址、执行无效命令)
  2. 中断(与指令流异步的外部事件,比如鼠标单击)

其中第一种同步异常又分为以下五种:

  1. 访问错误异常:例如尝试写入rom
  2. 断点异常:例如ebreak
  3. 环境调用异常:ecall
  4. 非法指令异常:译码阶段发现无效操作码
  5. 非对齐地址异常

下面这张图显示了各个类型的异常/中断如何表示,并存储在mcause寄存器中

rvbook第二章说过可以支持非对齐访存,但为什么有instruction address misaligned异常呢。是因为原子访存操作必须要对齐,其次,一些处理器设计者采取将非对齐访存通过软件模拟的方式来实现,而不在硬件层面提供支持。当然,如果需要性能更好的处理器,完全可以在硬件层面提供非对齐访存的支持

机器模式

csr

如果你翻看rv的规格手册你会发现有很多不明觉厉的csr,然而此处支持m-mode下异常处理的必要csr只有八个:

csr 保存的内容
mtvec 发生异常时需要跳转的地址
mepc 发生异常的指令地址
mcause 发生异常的种类
mie 目前能处理和必须忽略的中断
mip 目前正准备处理的中断
mtval trap的附加信息:比如访存异常的出错地址、非法指令异常的指令本身
mscratch 暂存字大小数据
mstatus 许多状态

前面几个寄存器相对好理解,看一下最后一个mstatus:

mstatus也有一个MIE域,与mie寄存器不同,MIE域控制全局中断使能,mie寄存器控制每种类型中断的使能,比如mie[7]对应m-mode下的时钟中断。显然,只有MIE=1时,mie才有效。

中断处理流程举例

用m-mode时钟中断举例,当MIE=1(全局中断使能有效),mie[7]=1(时钟中断使能有效),mip[7]=1(时钟中断到来),此时处理器处理该时钟中断:

  • pc保存到mepc,pc设置为mtvec
  • 根据mcause设置mtval
  • MIE保存到MPIE,并将MIE设置为0禁用中断
  • 权限模式保存到MPP,并将权限模式设置为m-mode(如果只实现m-mode,不需要这个步骤)

中断对于正常程序来说属于另一个例程,因此还需要将中断处理程序用到的整数寄存器保存到“栈”中,具体做法是软件让mscratch包含指向附加临时内存空间的指针,中断处理程序通过该指针保存整数寄存器到内存中,并且在结束时恢复。(关于这里我在写pa的nanos-lite中断处理的时候发现处理程序并没有使用mscratch,而是像普通的函数一样使用sp来入栈和出栈,可能是为了简化实现)

最后用mret退出异常处理程序时,处理器将pc恢复为mepc,MIE恢复为MPIE,权限模式恢复为MPP。

中断嵌套

遇到中断嵌套的时候,上述的mepc,mcause等寄存器还需要保存到内存中的栈中,可抢 占的中断处理程序可以在启用中断之前把这些寄存器保存到内存中的栈,然后在退出之前,禁用中断并从栈中恢复寄存器。

还有一个特殊的指令:wfi(wait for interrupt),它通知处理处理器目前不用干活,应该进入低功耗模式,直到mie&mip≠0,具体取决于处理器的实现,可以停止时钟,或者仅仅当nop来执行。