本文引录至《手把手教你RISC-V CPU(上)》一书第13章中断与异常处理机制。


引言

中断和异常虽说本身不是一种指令,但却是处理器指令集架构中非常重要的一环,任何一种指令集架构都会安排专门的章节定义和详解其中断和异常的行为,可以说中断和异常是不得不说的故事。


中断和异常概述

中断概述

中断(Interrupt)机制,即处理器核在顺序执行程序指令流的过程中突然被别的请求打 断而中止执行当前的程序,转而去处理别的事情,待其处理完了别的事情,然后重新回到之 前程序中断的点继续执行之前的程序指令流,其要点如下。

  • 打断处理器执行程序指令流的“别的请求”便称之为中断请求(Interrupt Request),“别 的请求”的来源便称之为中断源(Interrupt Source)。中断源通常来自于外围硬件设备。
  • 处理器转而去处理的“别的事情”便称之为中断服务程序(Interrupt Service Routine, ISR)。
  • 中断处理是一种正常的机制,而非一种错误情形。处理器收到中断请求之后,需要 保存当前程序的现场,简称为保存现场。等到处理完中断服务程序后,处理器需要 恢复之前的现场,从而继续执行之前被打断的程序,简称为“恢复现场”。
  • 可能存在多个中断源同时向处理器发起请求的情形,因此需要对这些中断源进行仲 裁,从而选择哪个中断源被优先处理。此种情况称为“中断仲裁”,同时可以给不同 的中断分配优先级以便于仲裁,因此中断存在着“中断优先级”的概念。
  • 还有一种可能是处理器已经在处理某个中断过程中(执行该中断的 ISR 之中),此时 有一个优先级更高的新中断请求到来,此时处理器该如何是好呢?有如下两种可能。
    • 第一种可能是处理器并不响应新的中断,而是继续执行当前正在处理的中断服务程序, 待到彻底完成之后才响应新的中断请求,这种称为处理器“不支持中断嵌套”。
    • 第二种可能是处理器中止当前的中断服务程序,转而开始响应新的中断,并执行其“中 断服务程序”,如此便形成了中断嵌套(即前一个中断还没响应完,又开始响应新的中断), 并且嵌套的层次可以有很多层。

注意:假设新来的中断请求的优先级比正在处理的中断优先级低(或者相同),则不管处理器是否能支持“中断嵌套”,都不应该响应这个新的中断请求,处理器必须完成当前中断服务程序之后才考虑响应新的中断请求(因为新中断请求的优先级并不比当前正在处理 的中断优先级高)。


异常概述

异常(Exception)机制,即处理器核在顺序执行程序指令流的过程中突然遇到了异常的事情而中止执行当前的程序,转而去处理该异常,其要点如下。

  • 处理器遇到的“异常的事情”称为异常(Exception)。异常与中断的最大区别在于中断往往是一种外因,而异常是由处理器内部事件或程序执行中的事件引起的,譬如本身硬件故障、程序故障,或者执行特殊的系统服务指令而引起的,简而言之是一种内因。
  • 与中断服务程序类似,处理器也会进入异常服务处理程序。
  • 与中断类似,可能存在多个异常同时发生的情形,因此异常也有优先级,并且也可能发生多重异常的嵌套

广义上的异常

如上一节所述,中断和异常最大的区别是起因内外有别。除此之外,从本质上来讲,中断和异常对于处理器而言基本上是一个概念。中断和异常发生时, 处理器将暂停当前正在执行的程序,转而执行中断和异常处理程序;返回时,处理器恢复执行之前被暂停的程序。

因此中断和异常的划分是一种狭义的划分。从广义上来讲,中断和异常都被认为是一种广义上的异常。处理器广义上的异常,通常只分为同步异常(Synchronous Exception)和异步异常(Asynchronous Exception)。

1.同步异常

同步异常是指由于执行程序指令流或者试图执行程序指令流而造成的异常。这种异常的原因能够被精确定位于某一条执行的指令。同步异常的另外一个通俗的表象便是,无论程序在同样的环境下执行多少遍,每一次都能精确地重现出来。

譬如,程序流中有一条非法的指令,那么处理器执行到该非法指令便会产生非法指令异常(Illegal Instruction Exception),能被精确地定位于这一条非法指令,并且能够被反复重现。


2.异步异常

异步异常是指那些产生原因不能够被精确定位于某条指令的异常。异步异常的另外一个通俗的表象便是,程序在同样的环境下执行很多遍,每一次发生异常的指令 PC 都可能会不一样。

最常见的异步异常是“外部中断”。外部中断的发生是由外围设备驱动的,一方面外部中断的发生带有偶然性,另一方面中断请求抵达于处理器核之时,处理器的程序指令流执行到具体的哪一条指令更带有偶然性。

因此一次中断的到来可能会巧遇到某一条“正在执行的不幸指令”,而该指令便成了“背锅侠”。在它的指令 PC 所在之处,程序便停止执行,并转而响应中断去执行中断服务程序。但是当程序重复执行时,却很难会出现同一条指令反复“背锅”的精确情形。

对于异步异常,根据其响应异常后的处理器状态,又可以分为两种。

  • 精确异步异常(Precise Asynchronous Exception):指响应异常后的处理器状态能够
    精确反映为某一条指令的边界,即某一条指令执行完之后的处理器状态。
  • 非精确异步异常(Imprecise Asynchronous Exception):指响应异常后的处理器状态无法精确反映为某一条指令的边界,即可能是某一条指令执行了一半然后被打断的结果,或者是其他模糊的状态。

常见的典型同步异常和异步异常如表 13-1 所示,此表可以帮助读者更加理解同步异常和异步异常的区别。

在这里插入图片描述

在这里插入图片描述


RISC-V 架构异常处理机制

本节将介绍 RISC-V 架构的异常处理机制。当前 RISC-V 架构文档主要分为“指令集文档”和“特权架构文档”。RISC-V 架构的异常处理机制定义在“特权架构文档”中。

狭义的中断和异常均可以被归于广义的异常范畴,因此本文自此将用“异常”作为统一概念进行论述,其包含了狭义上的“中断”和“异常”。

RISC-V 的架构不仅可以有机器模式(Machine Mode)的工作模式,还可以有用户模式(User Mode)、监督模式(Supervisor Mode)等工作模式。在不同的模式下均可以产生异常,并且有的模式也可以响应中断。

RISC-V 架构要求机器模式是必须具备的模式,其他的模式均是可选而非必选的模式。本文仅介绍基于机器模式的异常处理机制。

进入异常

进入异常时,RISC-V 架构规定的硬件行为可以简述如下。

  • 停止执行当前程序流,转而从 CSR 寄存器 mtvec 定义的 PC 地址开始执行。
  • 进入异常不仅会让处理器跳转到上述的 PC 地址开始执行,还会让硬件同时更新其他几个 CSR 寄存器,分别是以下 4 个寄存器。
    • 机器模式异常原因寄存器 mcause(Machine Cause Register)
    • 机器模式异常 PC 寄存器 mepc(Machine Exception Program Counter)
    • 机器模式异常值寄存器 mtval(Machine Trap Value Register )
    • 机器模式状态寄存器 mstatus(Machine Status Register)

下文将分别予以详述。

1.从 mtvec 定义的 PC 地址开始执行

RISC-V 架构规定,在处理器的程序执行过程中,一旦遇到异常发生,则终止当前的程序流,处理器被强行跳转到一个新的 PC 地址。该过程在 RISC-V 的架构中定义为“陷阱(trap)”,字面含义为“跳入陷阱”,更加准确的意译为“进入异常”。

RISC-V 处理器 trap 后跳入的 PC 地址由一个叫做机器模式异常入口基地址寄存器 mtvec(Machine Trap-Vector Base-Address Register)的 CSR 寄存器指定,其要点如下。

  • mtvec 寄存器是一个可读可写的 CSR 寄存器,因此软件可以编程更改其中的值。
  • mtvec 寄存器的详细格式如图 13-1 所示,其中的最低 2 位是 MODE 域,高 30 位是BASE 域。

在这里插入图片描述

  • 假设 MODE 的值为 0,则所有的异常响应时处理器均跳转到 BASE 值指示的 PC 地址。
  • 假设 MODE 的值为 1,则狭义的异常发生时,处理器跳转到 BASE 值指示的 PC 地址;狭义的中断发生时,处理器跳转到 BASE+4*CAUSE 值指示的 PC 地址。
    • CAUSE的值表示中断对应的异常编号(Exception Code),如图 13-3 所示。
    • 譬如机器计时器中断(Machine Timer Interrupt)的异常编号为 7,则其跳转的地址为 BASE+4×7=BASE+28= BASE+0x1c。
2.更新 CSR 寄存器 mcause

RISC-V 架构规定,在进入异常时,机器模式异常原因寄存器 mcause(Machine Cause Register)被同时更新,以反映当前的异常种类,软件可以通过读此寄存器查询造成异常的具体原因。

mcause 寄存器的详细格式如图 13-2 所示,其中最高 1 位为 Interrupt 域,低 31 位为异常编号域。此两个域的组合表示值如图 13-3 所示,用于指示 RISC-V 架构定义的 12 种中断类型和 16 种异常类型。
在这里插入图片描述
在这里插入图片描述

3.更新 CSR 寄存器 mepc

RISC-V 架构定义异常的返回地址由机器模式异常 PC 寄存器 mepc(Machine Exception Program Counter)保存。在进入异常时,硬件将自动更新mepc 寄存器的值为当前遇到异常的指令 PC 值(即当前程序的停止执行点)。该寄存器将作为异常的返回地址,在异常结束之后,能够使用它保存的PC 值回到之前被停止执行的程序点。

(1)值得注意的是,虽然 mepc 寄存器会在异常发生时自动被硬件更新,但是 mepc 寄存器本身也是一个可读可写的寄存器,因此软件也可以直接写该寄存器以修改其值。

(2)对于狭义的中断和狭义的异常而言,RISC-V 架构定义其返回地址(更新的 mepc 值)有些细微差别。

  • 出现中断时,中断返回地址 mepc 的值被更新为下一条尚未执行的指令。
  • 出现异常时,中断返回地址 mepc 的值被更新为当前发生异常的指令 PC。
    • 注意:如果异常由 ecall 或 ebreak 产生,由于 mepc 的值被更新为 ecall 或 ebreak 指令自己的PC。因此在异常返回时,如果直接使用 mepc 保存的 PC 值作为返回地址,则会再次跳回 ecall 或者 ebreak 指令,从而造成死循环(执行 ecall 或者 ebreak 指令导致重新进入异常)。
    • 正确的做法是在异常处理程序中软件改变 mepc 指向下一条指令,由于现在 ecall/ebreak(或 c.ebreak)是 4(或 2)字节指令,因此改写设定 mepc=mepc+4(或+2)即可。
4.更新 CSR 寄存器 mtval

RISC-V 架构规定,在进入异常时,硬件将自动更新机器模式异常值寄存器 mtval(Machine Trap Value Register ),以反映引起当前异常的存储器访问地址或者指令编码。

• 如果是由存储器访问造成的异常,譬如遭遇硬件断点、取指令、存储器读写造成的异常,则将存储器访问的地址更新到 mtval 寄存器中。

• 如果是由非法指令造成的异常,则将该指令的指令编码更新到 mtval 寄存器中。

注意:mtval 寄存器又名 mbadaddr 寄存器,在某些早期版本的 RISC-V 编译器中仅识别mbadaddr 名称。

5.更新 CSR 寄存器 mstatus

RISC-V 架构规定,在进入异常时,硬件将自动更新机器模式状态寄存器 mstatus(Machine Status Register)的某些域。

(1)mstatus 寄存器的详细格式如图 13-4 所示,其中的 MIE 域表示在 Machine Mode 下中断全局使能。

在这里插入图片描述

  • 当该 MIE 域的值为 1 时,表示 Machine Mode 下所有中断的全局打开。
  • 当该 MIE 域的值为 0 时,表示 Machine Mode 下所有中断的全局关闭。

(2)RISC-V 架构规定,异常发生时有如下情况。

  • MPIE 域的值被更新为异常发生前 MIE 域的值。MPIE 域的作用是在异常结束之后,能够使用 MPIE 的值恢复出异常发生之前的 MIE 值。
  • MIE 的值则被更新成为 0(意味着进入异常服务程序后中断被全局关闭,所有的中断都将被屏蔽不响应)。
  • MPP 的值被更新为异常发生前的模式。MPP 域的作用是在异常结束之后,能够使用MPP 的值恢复出异常发生之前的工作模式。

退出异常

当程序完成异常处理之后,最终需要从异常服务程序中退出,并返回主程序。RISC-V架构定义了一组专门的退出异常指令(Trap-Return Instructions),包括 MRET、SRET、和URET。其中 MRET 指令是必备的,而 SRET 和 URET 指令仅在支持监督模式和用户模式的处理器中使用。

注意:由于本书为简化知识模型,在此仅介绍“只支持机器模式”的架构,对 SRET 和URET 指令不做赘述。对其感兴趣的读者请参见 RISC-V“特权架构文档”原文。

在机器模式下退出异常时,软件必须使用 MRET 指令。RISC-V 架构规定,处理器执行MRET 指令后的硬件行为如下。

  • 停止执行当前程序流,转而从 CSR 寄存器 mepc 定义的 PC 地址开始执行。
  • 执行 MRET 指令不仅会让处理器跳转到上述的 PC 地址开始执行,还会让硬件同时更新 CSR 寄存器机器模式状态寄存器 mstatus(Machine Status Register)。

下文将分别予以详述。

1.从 mepc 定义的 PC 地址开始执行
  • 在进入异常时,mepc 寄存器被同时更新,以反映当时遇到异常的指令的 PC 值。通过这个机制,意味着 MRET 指令执行后处理器回到了当时遇到异常的指令的 PC 地址,从而可以继续执行之前被中止的程序流。
2.更新 CSR 寄存器 mstatus

mstatus 寄存器的详细格式如图 13-4 所示。RISC-V 架构规定,在执行 MRET 指令后,硬件将自动更新机器模式状态寄存器 mstatus(Machine Status Register)的某些域。

RISC-V 架构规定,执行 MRET 指令退出异常时有如下情况。

  • mstatus 寄存器 MIE 域的值被更新为当前 MPIE 的值。
  • mstatus 寄存器 MPIE 域的值则被更新为 1。

在进入异常时,MPIE 的值曾经被更新为异常发生前的 MIE 值。而 MRET 指令执行后,再次将 MIE 域的值更新为 MPIE 的值。通过这个机制,则意味着 MRET指令执行后,处理器的 MIE 值被恢复成异常发生之前的值(假设之前的 MIE 值为 1,则意味着中断被重新全局打开)。


异常服务程序

当处理器进入异常后,即开始从 mtvec 寄存器定义的 PC 地址执行新的程序。该程序通常为异常服务程序,并且程序还可以通过查询 mcause 中的异常编号(Exception Code)决定进一步跳转到更具体的异常服务程序。

譬如当程序查询 mcause 中的值为 0x2,则得知该异常是非法指令错误(Illegal Instructions)引起的,因此可以进一步跳
转到非法指令错误异常服务子程序中去。

图 13-5 所示为一异常入口程序实例片段,程序通过读取 mcause 的值,进而判断异常的类型,从而进入不同的异常服务子程序。

在这里插入图片描述

注意:由于 RISC-V 架构规定的进入异常和退出异常机制中没有硬件自动保存和恢复上下文的操作,因此需要软件明确地使用指令进行上下文的保存和恢复。


RISC-V 架构中断定义

中断类型

RISC-V 架构定义的中断类型分为 4 种。

  • 外部中断(External Interrupt)
  • 计时器中断(Timer Interrupt)
  • 软件中断(Software Interrupt)
  • 调试中断(Debug Interrupt)

下文将分别予以详述:

1.外部中断

RISC-V 架构定义的外部中断要点如下。

(1)外部中断是指来自于处理器核外部的中断,譬如外部设备 UART、GPIO 等产生的中断。

(2)RISC-V 架构在机器模式、监督模式和用户模式下均有对应的外部中断。由于本文为简化知识模型,在此仅介绍“只支持机器模式”的架构,因此仅介绍机器模式外部中断。

(3)机器模式外部中断(Machine External Interrupt)的屏蔽由 CSR 寄存器 mie 中的 MEIE域控制,等待(Pending)标志则反映在 CSR 寄存器 mip 中的 MEIP 域。

(4)机器模式外部中断可以作为处理器核的一个单比特输入信号,假设处理器需要支持很多个外部中断源,RISC-V 架构定义了一个平台级别中断控制器(Platform Level Interrupt Controller,PLIC)可用于多个外部中断源的优先级仲裁和派发。

  • PLIC 可以将多个外部中断源仲裁为一个单比特的中断信号送入处理器核,处理器核收到中断进入异常服务程序后,可以通过读 PLIC 的相关寄存器查看中断源的编号和信息。
  • 处理器核在处理完相应的中断服务程序后,可以通过写 PLIC 的相关寄存器和具体的外部中断源的寄存器,从而清除中断源(假设中断来源为 GPIO,则可通过 GPIO 模块的中断相关寄存器清除该中断)。

(5)虽然 RISC-V 架构只明确定义了一个机器模式外部中断,同时明确定义可通过 PLIC在外部管理众多的外部中断源将其仲裁成为一根机器模式外部中断信号传递给处理器核。但是 RISC-V 架构也预留了大量的空间供用户扩展其他外部中断类型,如以下 3 种。

  • CSR 寄存器 mie 和 mip 的高 20 位可以用于扩展控制其他的自定义中断类型。
  • 用户甚至可以自定义若干组新的 mie<n>mip<n>寄存器以支持更多自定义中断类型。
  • CSR 寄存器 mcause 的中断异常编号域为 12 及以上的值,均可以用于其他自定义中断的异常编号(Exception Code)。因此理论上通过扩展,RISC-V 架构可以支持无数根自定义的外部中断(External Interrupt)信号直接输入给处理器核。

2.计时器中断

RISC-V 架构定义的计时器中断要点如下。

(1)计时器中断是指来自计时器的中断。

(2)RISC-V 架构在机器模式、监督模式和用户模式下均有对应的计时器中断。由于本文为简化知识模型,在此仅介绍“只支持机器模式”的架构,因此仅介绍机器模式计时器中断(Machine Timer Interrupt)。

(3)机器模式计时器中断的屏蔽由 mie 寄存器中的 MTIE 域控制,等待(Pending)标志则反映在 mip 寄存器中的 MTIP 域。

(4)RISC-V 架构定义了系统平台中必须有一个计时器,并给该计时器定义了两个 64 位宽的寄存器 mtime(如图 13-6 所示)和 mtimecmp(如图 13-7 所示)。

  • mtime 寄存器用于反映当前计时器的计数值,mtimecmp 用于设置计时器的比较值。当 mtime 中的计数值大于或者等于mtimecmp 中设置的比较值时,计时器便会产生计时器中断。计时器中断会一直拉高,直到软件重新写 mtimecmp 寄存器的值,使得其比较值大于 mtime 中的值,从而将计时器中断清除。

在这里插入图片描述

  • 值得注意的是,RISC-V 架构并没有定义 mtime 寄存器和 mtimecmp 寄存器为 CSR寄存器,而是定义其为存储器地址映射(Memory Address Mapped)的系统寄存器,具体的存储器映射(Memory Mapped)地址 RISC-V 架构并没有规定,而是交由 SoC系统集成者实现。
  • 另一点值得注意的是,RISC-V 架构定义 mtime 定时器为实时(Real-Time)计时器,系统必须以一种恒定的频率作为计时器的时钟。该恒定的时钟频率必须为低速的电源常开的(Always-on)时钟,低速是为了省电,常开是为了提供准确的计时。

3.软件中断

RISC-V 架构定义的软件中断要点如下。

(1)软件中断是指来自软件自己触发的中断。

(2)由于 RISC-V 架构在机器模式、监督模式和用户模式下均有对应的软件中断。由于本文为简化知识模型,在此仅介绍“只支持机器模式”的架构,因此仅介绍机器模式软件中断(Machine Software Interrupt)。

(3)机器模式软件中断的屏蔽由 mie 寄存器中的 MSIE 域控制,等待(Pending)标志则反映在 mip 寄存器中的 MSIP 域。

(4)RISC-V 架构定义的机器模式软件中断可以通过软件写 1 至 msip 寄存器来触发。

注意:此 msip 寄存器和 mip 寄存器中的 MSIP 域命名不可混淆。且 RISC-V 架构并没有定义 msip 寄存器为 CSR 寄存器,而是定义其为存储器地址映射的系统寄存器,具体的存储器映射地址 RISC-V 架构并没有规定,而是交由 SoC 系统集成者实现。

(5)当软件写 1 至 msip 寄存器触发了软件中断之后,CSR 寄存器 mip 中的 MSIP 域便会置高,反映其等待状态。软件可通过写 0 至 msip 寄存器来清除该软件中断。


4.调试中断

除了上述 3 种中断之外,还有一种特殊的中断——调试中断(Debug Interrupt)。此中断专用于实现调试器(Debugger)。


中断屏蔽

RISC-V 架构的狭义上的异常是不可以被屏蔽的,也就是说一旦发生狭义上的异常,处理器一定会停止当前操作转而处理异常。但是狭义上的中断则可以被屏蔽掉,RISC-V 架构定义了 CSR 寄存器机器模式中断使能寄存器 mie(Machine Interrupt Enable Registers)可以用于控制中断的屏蔽。

(1)mie 寄存器的详细格式如图 13-8 所示,其中每一个比特域用于控制每个单独的中断使能。

  • MEIE 域控制机器模式(Machine Mode)下外部中断(External Interrupt)的屏蔽。

在这里插入图片描述

  • MTIE 域控制机器模式(Machine Mode)下计时器中断(Timer Interrupt)的屏蔽。
  • MSIE 域控制机器模式(Machine Mode)下软件中断(Software Interrupt)的屏蔽。

(2)软件可以通过写 mie 寄存器中的值达到屏蔽某些中断的效果。假设 MTIE 域为被设置成 0,则意味着将计时器中断屏蔽,处理器将无法响应计时器中断。

(3)如果处理器只实现了机器模式,则监督模式和用户模式对应的中断使能位(SEIE、UEIE、STIE、UTIE、SSIE 和 USIE)无任何意义。

注意:由于本文为简化知识模型,在此仅介绍“只支持机器模式”的架构,因此对 SEIE,UEIE、STIE、UTIE、SSIE 和 USIE 等不做赘述。对其感兴趣的读者请参见 RISC-V“特权架构文档”原文。

注意:除了对 3 种中断的分别屏蔽,通过 mstatus 寄存器中的 MIE 域还可以全局关闭所有中断。


中断等待

RISC-V 架构定义了 CSR 寄存器机器模式中断等待寄存器 mip(Machine Interrupt Pending Registers)可以用于查询中断的等待状态。

(1)mip 寄存器的详细格式如图 13-9 所示,其中的每一个域用于反映每个单独的中断等待状态(Pending)

在这里插入图片描述
• MEIP 域反映机器模式(Machine Mode)下的外部中断的等待(Pending)状态。

• MTIP 域反映机器模式(Machine Mode)下的计时器中断的等待(Pending)状态。

• MSIP 域反映机器模式(Machine Mode)下的软件中断的等待(Pending)状态。

(2)如果处理器(譬如蜂鸟 E200)只实现了机器模式,则 mip 寄存器中监督模式和用户模式对应的中断等待状态位(SEIP、UEIP、STIP、UTIP、SSIP 和 USIP)无任何意义。

(3)软件可以通过读 mip 寄存器中的值达到查询中断状态的效果。

  • 如果 MTIP 域的值为 1,则表示当前有计时器中断(Timer Interrupt)正在等待“Pending”。
  • 注意:即便 mie 寄存器中 MTIE 域的值为 0(被屏蔽),如果计时器中断到来,则 MTIP 域仍然能够显示为 1。
  • MSIP 和 MEIP 与 MTIP 同理。

(4)MEIP/MTIP/MSIP 域的属性均为只读,软件无法直接写这些域改变其值。只有这些中断的源头被清除后将中断源撤销,MEIP/MTIP/MSIP 域的值才能相应地归零。譬如 MEIP对应的外部中断需要程序进入中断服务程序后配置外部中断源,将其中断撤销。MTIP 和MSIP 同理。下一节将详细介绍中断的类型和清除方法。


中断优先级与仲裁

对于中断而言,曾经提到多个中断可能存在着优先级仲裁的情况。对于RISC-V 架构而言,分为如下 3 种情况。

(1)如果 3 种中断同时发生,其响应的优先级顺序如下,mcause 寄存器中将按此优先级顺序选择更新异常编号(Exception Code)的值。

• 外部中断(External Interrupt)优先级最高。

• 软件中断(Software Interrupt)其次。

• 计时器中断(Timer Interrupt)再次。

(2)调试中断比较特殊。只有调试器(Debugger)介入调试时才发生,正常情形下不会发生,因此在此不予讨论。

(3)由于外部中断来自 PLIC,而 PLIC 可以管理数量众多的外部中断源,多个外部中断源之间的优先级和仲裁可通过配置 PLIC 的寄存器进行管理。


中断嵌套

曾经提到多个中断理论上可能存在着中断嵌套的情况。而对于 RISC-V 架构而言:

  • 进入异常之后,mstatus 寄存器中的 MIE 域将会被硬件自动更新成为 0(意味着中断被全局关闭,从而无法响应新的中断)。
  • 退出中断后,MIE 域才被硬件自动恢复成中断发生之前的值(通过 MPIE 域得到),从而再次全局打开中断。

由上可见,一旦响应中断进入异常模式后,中断被全局关闭再也无法响应新的中断,因此 RISC-V 架构定义的硬件机制默认无法支持硬件中断嵌套行为。

如果一定要支持中断嵌套,需要使用软件的方式达到中断嵌套的目的,从理论上来讲,可采用如下方法。

(1)在进入异常之后,软件通过查询 mcause 寄存器确认这是响应中断造成的异常,并跳入相应的中断服务程序中。在这期间,由于 mstatus 寄存器中的 MIE 域被硬件自动更新成为 0,因此新的中断都不会被响应。

(2)待程序跳入中断服务程序中后,软件可以强行改写 mstatus 寄存器的值,而将 MIE域的值改为 1,意味着将中断再次全局打开。从此时起,处理器将能够再次响应中断。但是在强行打开 MIE 域之前,需要注意如下事项。

  • 假设软件希望屏蔽比其优先级低的中断,而仅允许优先级比它高的新来打断当前中断,那么软件需要通过配置 mie 寄存器中的 MEIE/MTIE/MSIE 域,来有选择地屏蔽不同类型的中断。
  • 对于 PLIC 管理的众多外部中断而言,由于其优先级受 PLIC 控制,假设软件希望屏蔽比其优先级低的中断,而仅允许优先级比它高的新来中断打断当前中断,那么软件需要通过配置 PLIC 阈值(Threshold)寄存器的方式来有选择地屏蔽不同类型的中断。

(3)在中断嵌套的过程中,软件需要注意保存上下文至存储器堆栈中,或者从存储器堆栈中将上下文恢复(与函数嵌套同理)。

(4)在中断嵌套的过程中,软件还需要注意将 mepc 寄存器,以及为了实现软件中断嵌套被修改的其他 CSR 寄存器的值保存至存储器堆栈中,或者从存储器堆栈中恢复(与函数嵌套同理)

除此之外,RISC-V 架构也允许用户实现自定义的中断控制器实现硬件中断嵌套功能。


总结比较

中断和异常虽说本身不是一种指令,但却是处理器指令集架构非常重要的一环,任何一种指令集架构都会安排专门的章节去定义中断和异常的行为。同时中断和异常也往往是最复杂和难以理解的部分,可以说要了解一门处理器架构,熟悉其中断和异常的处理机制是必不可少的。


RISC-V 架构异常相关 CSR 寄存器

将 RISC-V 架构中所有中断和异常相关的寄存器加以总结,如表 13-2 所示。

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐