RISC-V 的中断与异常
本文是我对阅读《手把手教你设计 CPU RISC-V 处理器》中断与异常相关章节的知识整理。本文的内容均以机器模式为例。
在学习 RISC-V 中断与异常的处理机制之前,我们需要先对这两个名词要有一个明确的理解。
中断与异常,站在处理器处理的过程来说,其实并没有区别。当中断与异常发生时,处理器的表现形式就是,暂停当前执行的程序,转而执行处理中断或异常的处理程序,处理完后视情况恢复执行之前被暂停的程序。
通常我们所理解的中断与异常都可以被统称为广义上的异常。
广义上的异常被分为两种:
- 同步异常:执行某个程序流,能稳定复现的的异常,能比较精确的确定是那条指令引发的异常。(例如程序流里有一条非法指令。属于内因)
- 异步异常:异常产生的原因与当前的程序流无关,与外部的中断事件有关。(由外部事件引起的。属于外因)
RISC-V 架构异常处理机制
RISC-V 定义的三种模式(U;S;M),均可发生异常。但是只有特权模式(S;M)才能处理异常,因为处理异常需要 CSR 寄存器。
当处理器的程序在正常执行当中发生了异常,处理器就会强行跳转到一个指定的 PC 地址。这个过程定义为“陷阱(trap)”。
1. RISC-V 进入异常处理流程
(机器模式下)当异常发生后,处理器会做如下处理:
- 处理器停止执行当前的程序流,转而跳转 mtvec 寄存器定义的 PC 地址开始执行。
- 将异常原因记录到 mcause 寄存器中。
- 将异常的返回地址保存到 mepc 寄存器中。
- 将异常发生时的存储器访问地址或者指令编码保存到 mtval 寄存器中。
- 更新 mstatus 状态寄存器。
2. RISC-V 异常服务程序
(机器模式下)处理器处理异常,相关知识点:
- 当处理器进入异常后,就会从 mtvec 寄存器定义的 PC 地址执行新的程序。
- mcause 寄存器用于判断是那种异常类型。(其中包含了 12 种中断以及 16 种异常。)
- RISC-V 架构在进入退出异常机制中没有硬件自动保存和恢复上下文的操作。(所以需要软件上实现上下文切换的机制;mtvec 寄存器的 MODE 域需要设置为 0)
- mtvec 寄存器的 MODE 域设置为 1 后,异常服务程序的跳转会更加高效,但是对于操作系统来说,一般都是需要设置为 0 ,这样可以让处理器发生异常时可以进入一个统一的异常服务程序入口。
3. RISC-V 退出异常处理流程
(机器模式下)当处理器处理完异常之后,软件上要执行 MRET 指令,然后处理器会做如下处理:
- 处理器停止执行当前的程序流,转而跳转 mepc 寄存器保存的 PC 地址开始执行。
- 更新 mstatus 状态寄存器。
RISC-V 中断类型
RISC-V 架构一共定义了 4 种中断类型:
- 外部中断:外部中断是处理器核的一个单比特输入信号,如果要支持更多的外部中断,那么就需要在这个单比特信号的基础之上引入一个 PLIC 中断控制器。
- 计时器中断:RISC-V 架构定义了两个 64 位的寄存器 mtime 与 mtimecmp 。mtime 寄存器里的值会恒定频率递增,当 mtime 的值大于等于 mtimecmp 的值时就会产生计时器中断。
- 软件中断:软件自己触发的中断。(通过程序上写 1 到 msio 寄存器,即可触发软件中断。)
- 调试中断:用于实现调试器。
RISC-V 中断屏蔽与等待
- RISC-V 架构中 MIE 寄存器可以使能或屏蔽 外部中断;计时器中断;软件中断;但是调试中断无法屏蔽。
- RISC-V 架构中 MIP 寄存器可以用来查询 外部中断;计时器中断;软件中断;是否处于等待状态。(无论中断是否被 MIE 屏蔽,都可以查询这个寄存器来判断是否产生相应的中断。只有中断源被清除后,硬件才自动清零。)
RISC-V 没有定义硬件的中断嵌套机制
RISC-V 架构,在硬件上没有中断嵌套机制,需要软件上来实现。因为在进入异常之后,mstatus 寄存器的 MIE 全局中断使能位会被置 0(也就是中断被全局关闭,再也无法响应新的中断。)
如果硬要有中断嵌套的机制,那么就需要软件上在异常服务程序里将 MIE 全局中断使能位打开,以及嵌套保存上下文。
这一设计让处理器硬件设计变得简单了,但是软件的设计就变得复杂。
RISC-V 异常委托机制
只有在特权模式下才能处理异常。目前 RISC-V 一共只定义了两个特权模式。(S 模式;M 模式)
RISC-V 有两个寄存器 medeleg 和 mideleg ,分别用于委托异常和中断。
默认情况下,任何特权级别的所有陷阱都在机器模式下处理,但是在机器模式的异常服务程序里可以通过 MRET 指令将陷阱重定向回我们想要的特权模式。
为了提高性能,可以在 medeleg 和 mideleg 对单个位的写入来指定特定的异常和中断应该由 RISC-V 那种特权模式来处理。(通常就是 M 模式委托给 S 模式)