RISC-V特权架构 - 时钟中断处理
- 1 MTI中断处理
- 1.1 触发中断
- 1.2 查询mie.MTIE与mip.MTIE
- 1.3 若运行在M模式下
- 1.4 若运行在S模式下
- 1.5 若运行在U模式下
- 2 STI中断处理
- 2.1 触发中断
- 2.2 查询mie.STIE与mip.STIE
- 2.3 若运行在M模式下
- 2.4 若运行在S模式下
- 2.5 若运行在U模式下
- 3 知识总结
- 3.1 中断处理逻辑
- 3.2 中断处理规则
本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。
RISC-V 架构定义了,CSR 寄存器机器模式中断等待寄存器mip(Machine Interrupt Pending Registers),可以用于查询中断的等待状态。
支持的中断类型,主要有以下几种:
- 外部中断:MEIP/SEIP/UEIP
- 时钟中断:MTIP/STIP/UTIP
- 软件中断:MSIP/SSIP/USIP
RISC-V 架构中,在机器模式、监督模式和用户模式下,均有对应的时钟中断,分别为MTIP、STIP、UTIP。
对于初学者来讲,涉及到的mie,mstatus,midelege等寄存器,是如何使用的,可能理解的比较模糊,希望经过本节的介绍,能够拨开迷雾。
本文主要,以时钟中断为例,对中断的触发、查询、委托、处理,整个流程进行介绍。
1 MTI中断处理
MTI(Machine Timer Interrupt),即机器模式时钟中断。
时钟中断,是由本地中断控制器(CLINT)管理的,属于本地中断的一种。
1.1 触发中断
MTI中断的触发,是依靠一个计时器,以及一对寄存器来完成的,即mtime和mtimecmp。
- mtime 用于反映当前计时器的计数值
- mtimecmp 用于设置计时器的比较值
当mtime 中的计数值,大于或者等于mtimecmp 中设置的比较值时,计时器便会产生时钟中断。产生中断后,需要固件/软件,重新写mtimecmp 寄存器的值,使其大于mtime 中的值,从而将计时器中断清除。
RISC-V 架构,并没有将这一对寄存器,定义为CSR寄存器。而是定义为存储器地址映射(Memory Address Mapped)的系统寄存器,具体的存储器映射(Memory Mapped)地址RISC-V 架构并没有规定,而是交由SoC系统集成者实现。
以上描述的是单核时的情形,若为多核,原理是相同的。CLINT的时钟中断,亦可以发送给每个核,如下图所示:
CLINT中,定义了与核数相同的mtimecmp寄存器,如下图所示:
一个mtimecmp 对应一个核,计数值使用同一个mtime寄存器,mtime与所有mtimecmp比较,可以分别对每个核,触发时钟中断。
时钟中断触发后,硬件会自动将mip.MTIP=1。
MTI中断触发、查询、处理,完整流程图,如下所示:
1.2 查询mie.MTIE与mip.MTIE
mie.MTIE == 1
:表示使能MTI中断mip.MTIE == 1
:表示MTI中断处于等待响应状态
要让某中断被处理,则该中断对应的mie.XXIE和mip.XXIP必须为1,只有满足这个条件,才会进入处理逻辑。
此外,下面,还需要根据,当前运行的模式,来查询全局中断使能状态,以及委托。
1.3 若运行在M模式下
- 若
mstatus.mie == 0
:表示禁用M模式下中断;故不处理任何中断,异常可以正常处理,不受影响。 - 若
mstatus.mie == 1
:表示使能M模式下中断,故可以继续处理。
由于mideleg.MTIP始终为0,因此MTI中断,永远无法被委托给S模式。
即无论运行在何种模式下,M模式时钟中断(mip.MTIP),只能在M模式下处理。
所以,通常,会在MTIP的中断处理函数中,通过注入的方式将mip.STIP置1,以及midelege.STIP置1,以便下一次进入S模式时,以委托方式在S模式下处理mip.STIP中断。
不能忘记,每次时钟中断处理都要更新 mtimecmp 寄存器,否则时钟中断信号,就不会被清除。
1.4 若运行在S模式下
- 若
mstatus.sie == 0
:表示禁用S模式下中断;由于mideleg.MTIP为0,无法委托至S,故陷入M处理中断。 - 若
mstatus.sie == 1
:表示使能S模式下中断,由于mideleg.MTIP为0,无法委托至S,故陷入M处理中断。
若运行在M模式下,只有在全局中断使能位mstatus.mie置位时,才会处理中断。
如果在S模式下,触发了M模式的中断(MTIP),此时无视mstatus.mie直接响应,即:
运行在低权限模式下,高权限模式的全局中断使能位一直是enable状态。
1.5 若运行在U模式下
由于mideleg.MTIP为0,无法委托至S,故只能陷入M处理中断。
此处,就像是上面S模式介绍的一样,亦不关心mstatus.mie全局中断使能状态。
2 STI中断处理
STI(Supervisor Timer Interrupt),即监管模式时钟中断。
时钟中断,是由本地中断控制器(CLINT)管理的,属于本地中断的一种。
2.1 触发中断
在早期的RISC-V规范中,仅有mtime和mtimecmp寄存器,因此时钟中断触发时,默认写入mip.MTIP=1,而非mip.STIP=1。
我们知道mip.MTIP,只能在M下处理,这就导致想要在S下处理时钟中断,变得比较麻烦。
所以,这里就有2个方法:
- 软件注入mip.STIP
通常,会在mip.MTIP的中断处理函数中,通过注入的方式将mip.STIP置1,以及midelege.STIP置1,以便下一次进入S模式时,以委托方式在S模式下处理mip.STIP中断。
- 通过stimecmp,触发mip.STIP
由于 mtimecmp 只能在 M 模式下访问,对于 S/HS 模式下的内核需要通过 SBI 才能访问,会造成较大的中断延迟和性能开销。为了解决这一问题,RISC-V 新增了 Sstc 拓展支持,S模式扩展为HS模式,新增了 stimecmp 。当 time>=stimecmp (HS)时会产生 timer 中断,不再需要通过 SBI 陷入其他模式。
STI中断触发、查询、处理,完整流程图,如下所示:
2.2 查询mie.STIE与mip.STIE
mie.STIE == 1
:表示使能STI中断mip.STIE == 1
:表示STI中断处于等待响应状态
要让某中断被处理,则该中断对应的mie.XXIE和mip.XXIP必须为1,只有满足这个条件,才会进入处理逻辑。
此外,下面,还需要根据,当前运行的模式,来查询全局中断使能状态,以及委托。
2.3 若运行在M模式下
- 若
mstatus.mie == 0
:表示禁用M模式下中断;故不处理任何中断,异常可以正常处理,不受影响。 - 若
mstatus.mie == 1
:表示使能M模式下中断,故可以继续处理。- 如果
mideleg.STIP == 0
,表示不委托给S处理,故陷入M进行处理。 - 如果
mideleg.STIP == 1
,表示委托给S处理,故不在M处理,下次进入S时,尝试在S下处理。
- 如果
2.4 若运行在S模式下
- 若
mstatus.sie == 0
:表示禁用S模式下中断。- 如果
mideleg.STIP == 0
,表示不委托给S处理,故陷入M进行处理。 - 如果
mideleg.STIP == 1
,表示委托给S处理,但是这里,由于S模式全局中断被禁用,因此即便委托,也不会处理任何中断。
- 如果
- 若
mstatus.sie == 1
:表示使能S模式下中断。- 如果
mideleg.STIP == 0
,表示不委托给S处理,故陷入M进行处理。 - 如果
mideleg.STIP == 1
,表示委托给S处理,这里S模式全局中断使能,因此,可以陷入S处理中断。
- 如果
2.5 若运行在U模式下
- 如果
mideleg.STIP == 0
,表示不委托给S处理,故陷入M进行处理。 - 如果
mideleg.STIP == 1
,表示委托给S处理,这里,由于运行在低优先级U下,要切换到高优先级S处理中断,因此不关心mstatus.sie全局中断开关状态,默认为enable。所以,这里可以陷入S处理中断。
3 知识总结
3.1 中断处理逻辑
经过对MTI和STI中断的处理分析,发现两者的查询处理逻辑,其实是一样的。
对于RISC-V支持的,主要中断类型:
- 外部中断:
MEIP/SEIP/UEIP
- 时钟中断:
MTIP/STIP/UTIP
- 软件中断:
MSIP/SSIP/USIP
其处理流程,总结为以下几个步骤(以XXI
中断为例):
mie.XXIE == 1 && mip.XXIP == 1
:检查确保XXI中断使能,且该中断被触发- 根据运行模式,检查全局中断开关
- M模式: 检查mstatus.mie
mstatus.mie == 0
:禁用M全局中断,不处理mstatus.mie == 1
:使能M全局中断,继续检查委托mideleg.XXIP == 0
:不委托,陷入Mmideleg.XXIP == 1
:委托到S,陷入S
- S模式: 检查mstatus.sie
mstatus.sie == 0
:禁用S全局中断,继续检查委托mideleg.XXIP == 0
:不委托,陷入Mmideleg.XXIP == 1
:委托到S,不处理
mstatus.sie == 1
:使能S全局中断,继续检查委托mideleg.XXIP == 0
:不委托,陷入Mmideleg.XXIP == 1
:委托到S,陷入S
- U模式: 无需检查全局中断,只检查委托
mideleg.XXIP == 0
:不委托,陷入Mmideleg.XXIP == 1
:委托到S,陷入S
- M模式: 检查mstatus.mie
比如,换成机器模式外部中断MEI,也是一样的处理过程。
3.2 中断处理规则
- RISC-V架构所有模式的异常,在默认情况下,都跳转到M模式处理。
- 中断/异常委托的目的地,只能是S模式。也就是说,将mideleg中某中断置为1后,表示将该中断委托到S模式进行处理,且只能是S模式。
- 事实上,即使在mideleg中设置了,将S模式产生的时钟中断,委托给S模式,委托仍无法完成。因为,硬件产生的时钟中断,仍会发到M模式(mtime寄存器,是M模式的设备),所以,我们需要手动触发S模式下的时钟中断(注入mip.STIP)。
- 由于mideleg中MEIP、MTIP、MSIP这三类M模式中断,属于保留位域,默认值为0,也不可更改,因此对于这些中断均不能委托。
故MEIP、MTIP、MSIP
这3类中断,仅能在M模式下处理。 - 当运行在低权限模式下时,若需要陷入高权限模式处理中断,则高权限模式的全局中断使能位,默认一直是enable状态(即忽略mstatus.mie或sie的实际值)。
- 当处理中断时,mie、mip、mideleg三个寄存器中,判断的位域应该是相同的。比如:处理MTI中断时,应检查
mie.MTIE && mip.MTIP && mideleg.MTIP
,因mideleg.MTIP==0,故无法委托;而不能去检查mideleg.STIP,它是表示将STI中断,委托到S的位域。
7. 在常见模拟器中,对于影子寄存器,通常只使用mie、mip、mstatus
寄存器,而不使用uie/sie、uip/sip、sstatus。
- uie、sie均为mie的子集,为影子寄存器,对应位域偏移与值,均相同;读取 uie/sie 的任何字段或写入其任何可写字段,都会导致 mie 中同名字段的读写。
- uip、sip均为mip的子集,为影子寄存器,对应位域偏移与值,均相同;读写与上述特性相同。
- sstatus为mstatus的子集,为影子寄存器,对应位域偏移与值,均相同;读写与上述特性相同。
更多关于中断处理的文档,可参考:
- 《MIT 6.S081 教材第五章内容 – 中断与设备驱动–下》
- 《RISC-V timer 在 Linux 中的实现》
- 《详解RISC v中断》