RISC-V架构的平台级中断控制器(PLIC:platform-level interrupt controller)详解
英文缩写
英文缩写 | 中文含义 |
---|---|
PLIC | platform-level interrupt controller,平台级中断控制器 |
SMT | simultaneous multi-threading,并发多线程 |
HART | RISC-V架构中的硬件线程 |
SMT | simultaneous multi-threading,多线程执行 |
M-MODE | machine mode,机器模式 |
S-MODE | supervisor mode,管理员模式 |
U-MODE | user mode,用户模式 |
MSI | message-signalled interrupts,用发送数据包的方式来触发中断 |
EIP bit | external interrupt pending bit,外部中断挂起位 |
IP bit | interrupt pending bit,中断挂起位 |
ID | identification,编号 |
WARL | Write-Any-Read-Legal:该字段可以写入任意值,在读取时必须具有合法值 |
context/target | 都是指能够处理中断的单元,也就是hart;RISC-V可以有多个hart,每个hart都可以响应中断,不同的中断源可以由不同的hart处理 |
Claim/complete Register | 中断响应和中断完成是同一个寄存器:读该寄存器返回当前需要处理的外部中断ID;向该寄存器写入中断ID,表示该中断处理完成 |
non-idempotent memory-mapped I/O control register | 非幂等内存映射I/O控制寄存器:此类寄存器多次写入相同的值会产生不同的影响。比如complete Register寄存器,多次写入相同的中断ID,表示通知PLIC核心该中断完成了多次。(幂等寄存器(普通寄存器):多次写入相同值没有影响。比如一些用于存储数据的寄存器,多次写相同数据是没有影响的) |
第一章 介绍
本规范根据RISC-V平台级中断控制器规范(从RISC-V特权规范V1.11草案中删除)中定义的通用PLIC(platform-level interrupt controller )架构描述操作参数,以便在RISC-V系统的上下文中工作。
PLIC将各种设备中断复用到Hart上下文的外部中断线上,并且具有中断优先级的硬件支持。PLIC最多支持1023个中断(中断号0保留)和最多15872个上下文,但是实际中断和上下文的数量取决于PLIC实现。然而,PLIC的实现必须遵守PLIC操作参数内每个寄存器的偏移量。声称符合PLIC规范的标准PLIC必须遵循下面各章节中提到的实现。
1.1、中断目标和硬件线程上下文
中断目标通常是hart上下文,hart上下文是给定hart上给定特权模式(尽管还有其他可能的中断目标,比如DMA控制器)。例如,在2路SMT的4核系统中,有8个hart,每个hart可能至少有两种特权模式:机器模式和管理员模式。
不是所有hart上下文需要是中断目标,特别是,如果处理器核心不支持委托外部中断给低特权模式,那么低特权的hart上下文将不会是中断目标。PLIC产生的中断通知分别体现在M/S模式的mip/sip寄存器的meip/seip位中。
本规范之前的版本声明PLIC支持用户模式中断。这部分被移除了,因为特权架构没有定义用户模式中断。如果将来特权架构指定了用户模式中断,可以直接扩展PLIC规范来支持用户模式中断。
如果外部中断已经委托给较低特权模式,那么中断通知只会出现在较低特权模式的xip寄存器中。每个处理核心必须制定一个策略,当多个中断同时产生如果分配给核心上的多个hart上下文。对于单个hart上下文栈的简单情况,一个hart上下文支持每个特权模式,更高特权的中断上下文可以抢占正在执行的低特权上下文的中断处理程序。多线程处理器核心可以同时在不同的hart上下文中运行多个独立的中断处理程序。处理器核心还可以提供仅用于中断处理的hart上下文,以减少中断服务的延迟,并且这些上下文可以抢占同一核心上其他hart的中断处理程序。
PLIC独立的处理每一个中断目标,不考虑包含多个中断目标的组件所使用的任何中断优先级方案。因此,PLIC没有提供中断抢占或者中断嵌套的概念,所以这中断嵌套/抢占必须由处理多个中断目标上下文的核心处理。
- 总结:
- PLIC可以将中断上报给多个hart。有的RV CORE有多个hart,每个hart都可以响应中断,通过配置hart的使能寄存器来决定该hart响应哪些中断
- 可以设置中断源上报给多个hart,但是最终只有一个hart会响应中断。(hart获取中断ID和清除pending位是原子的,能保证只有第一个响应的hart能获取到中断ID)
1.2、中断网关
中断网关负责将全局中断信号转换成通用的中断请求格式,并且控制发送到PLIC核心的中断请求。在任何时间,每个中断源最多只能有一个中断请求在PLIC挂起,通过设置中断源的IP位来表示挂起。中断网关只有在收到同一个中断源之前的中断请求服务的中断处理程序已经完成的通知后,才会向PLIC核心发送新的中断请求。
如果全局中断使用电平敏感型中断,网关将把中断电平的第一个中断断言转换成中断请求,此后,网关不会转发额外中断请求,直到网关收到中断完成消息。接收到中断完成消息,如果中断是电平触发,并且中断仍然是断言,一个新的中断请求将会发送给PLIC核心。网关没有能力撤销转发到PLIC核心的中断请求。如果电平敏感中断源在PLIC核心接收到中断请求之后,在中断请求被服务之前解除中断,中断请求仍然体现在PLIC核心的IP位,将会被中断服务程序处理,该处理程序必须确定中断设备不再请求服务。
如果全局中断源是边沿触发,网关将转换第一个匹配到的信号边沿成中断请求。取决于设备和中断处理程序的设计,在发送中断请求和接收处理程序完成通知之间,网关可能忽略额外匹配到的边沿或者挂起中断计数器的值。在两种情况下,接收到前一个中断处理完成的消息后,网关才会发送下一个中断请求给PLIC核心。如果网关有一个挂起中断的计数器,当中断请求被PLIC核心接收计数器的值就减少。与专用的中断信号线不同,消息信号中断(MSI)是通过在系统互连的网络上发送一个消息包,此消息包描述了要发生断言的中断。消息包被解码来选择一个中断网关,被选中的网关将处理MSI,类似于处理边沿触发中断。
- 总结:
- 中断网关响应中断分为电平触发(高电平)和边沿触发(上升沿)
- 电平触发:中断网关感知到高电平就给PLIC核心上报一个中断请求,在该中断请求被处理完之前不会再发新的中断请求(哪怕中断源一直是高电平)
- 边沿触发:
- 中断网关感知到上升沿就给PLIC核心上报一个中断请求,在该中断请求被处理完之前不会再发新的中断请求(哪怕再次检测到多个上升沿)
- 可以实现一个计数器,每接收到一个上升沿就加一,表示收到一次有效中断。每给PLIC核心上报一个中断请求并被处理完计数器就减一,只有计数器的值大于0就一直上报中断请求。此种实现方法,网关给PLIC核心上报中断请求就不是硬件连线,而是要实现成通过发送消息包的方式来触发,实现比较复杂,但是这样能够保证上升沿触发方式不会丢中断。
- PLIC规范文档并没有定义设置每个中断源触发方式的寄存器,可以有两种解决方式:
- 增加Interrupt Trigger Register:软件设置每个中断源的触发方式
- 硬编码:在硬件代码中就定好每个中断源的触发方式,中断源要上报中断时只能按照中断网关定义好的触发方式。比如:硬件代码将触发方式固定成上升沿触发,那每个中断源都按照上升沿的方式来上报中断。
- 中断网关响应中断分为电平触发(高电平)和边沿触发(上升沿)
1.3、中断通知
每个中断目标在PLIC核心有一个外部中断挂起位(EIP),此位表明对应目标有一个挂起态的中断等待处理。由于中断源、中断目标或者其他代理修改PLIC中寄存器的值导致PLIC核心的状态发生改变,EIP的值也可能会发生变化。EIP的值作为中断通知传递给目的目标,如果目标是hart上下文,中断通知是到达meip位还是seip位,取决于hart上下文的特权等级。
在简单的系统中,中断通知是连接到处理器hart的简单信号线。在复杂系统中,中断通知可以作为消息在跨系统互连中路由。
PLIC硬件只支持中断的多播,这样所有使能的目标都可以接收到处于触发态中断的中断通知。多播提供快速的响应,因为最快的的响应者将申请中断,但是在高中断频率的场景,如果多个hart发生中断陷入但是只有一个hart可以成功声明,那这可能是一种浪费性能。软件可以调整PLIC IE位作为每个中断处理程序的一部分,以提供替代策略,例如中断亲和性或者轮询单播。
根据平台架构和用于传输中断通知的方式,这些通知可能需要一些时间才能被目标接收到。只要在PLIC核心中没有干预活动,PLIC保证最终所有状态变化能传递到所有目标的EIP位中。中断通知的值只保证保持过去某个时间点EIP的有效值。特别的,当第一个目标的中断通知还仍然在传输时,第二个目标可以响应和申请中断,这样当第一个目标尝试去申请中断时会发现PLIC核心里没有处于挂起态的中断。
- 总结:
- PLIC核心通知中断给hart,会体现在xeip(x是m或者s,取决于是机器模式还是管理员模式)位中
- 当hart收到中断,可以查阅xeip位来知道是否发生外部中断
1.4、中断标识
全局中断源被分配小的无符号整数标识符,从1开始分配,中断号0保留(意味着没有中断)。当两个或者更多具有相同优先级的中断源同时发生,中断标识符也用来打破并列关系,中断ID较小的中断优先于中断ID较大的中断。
1.5、中断流程
上图展示了通过PLIC处理时,代理之间的消息流:
- Interrupt Signalled:
- 中断源向中断网关触发中断(按照预定的触发方式:电平触发或者边沿触发)
- Interrupt Request:
- 中断网关接收到中断,把中断请求发送给PLIC核心
- PLIC核心接收到中断请求后,将中断源对应的pending位置一,并且屏蔽该中断
- Interrupt Notification:
- PLIC核心转发中断通知给一个或者多个目标,只要目标使能了任意一个处于挂起态的中断,并且处于挂起态中断的优先级超过目标的优先级阈值
- 被通知的hart会跳转到中断处理入口
- Interrupt Claim:
- hart读取PLIC内对应的claim Register,获取到要响应的中断ID
- Claim Response:
- 如果有多个中断都处于pending状态,PLIC核心需要仲裁出最高优先级的中断ID返给hart,并清除对应中断源的挂起位
- hart获取到需要响应的中断ID,执行对应的中断处理函数
- Interrupt Completion:
- hart退出中断处理程序前,将响应的中断ID写入到complete Register,通知网关中断处理完毕
- 网关接收到中断处理完成的消息,相同中断源就可以发起下一次中断
- Next Request:
- 中断网关可以发送同一个中断源的另一个中断请求给PLIC
第二章 操作参数
一般的PLIC操作参数寄存器定义如下:
- 中断优先级寄存器:记录每个中断源的中断优先级
- 中断挂起寄存器:记录每个中断源的状态
- 中断使能寄存器:每个上下文的中断源启用情况
- 中断阈值寄存器:每个上下文的中断优先级阈值
- 中断响应寄存器:每个上下文从该寄存器获取中断ID
- 中断完成寄存器:用于向中断网关发送中断完成消息
- 备注:中断响应寄存器和中断完成寄存器实际对应同一个寄存器,读时是中断响应寄存器,写时是中断完成寄存器
第三章 地址映射
base + 0x000000: Reserved (interrupt source 0 does not exist)
base + 0x000004: Interrupt source 1 priority
base + 0x000008: Interrupt source 2 priority
...
base + 0x000FFC: Interrupt source 1023 priority
base + 0x001000: Interrupt Pending bit 0-31
base + 0x00107C: Interrupt Pending bit 992-1023
...
base + 0x002000: Enable bits for sources 0-31 on context 0
base + 0x002004: Enable bits for sources 32-63 on context 0
...
base + 0x00207C: Enable bits for sources 992-1023 on context 0
base + 0x002080: Enable bits for sources 0-31 on context 1
base + 0x002084: Enable bits for sources 32-63 on context 1
...
base + 0x0020FC: Enable bits for sources 992-1023 on context 1
base + 0x002100: Enable bits for sources 0-31 on context 2
base + 0x002104: Enable bits for sources 32-63 on context 2
...
base + 0x00217C: Enable bits for sources 992-1023 on context 2
...
base + 0x1F1F80: Enable bits for sources 0-31 on context 15871
base + 0x1F1F84: Enable bits for sources 32-63 on context 15871
base + 0x1F1FFC: Enable bits for sources 992-1023 on context 15871
...
base + 0x1FFFFC: Reserved
base + 0x200000: Priority threshold for context 0
base + 0x200004: Claim/complete for context 0
base + 0x200008: Reserved
...
base + 0x200FFC: Reserved
base + 0x201000: Priority threshold for context 1
base + 0x201004: Claim/complete for context 1
...
base + 0x3FFF000: Priority threshold for context 15871
base + 0x3FFF004: Claim/complete for context 15871
base + 0x3FFF008: Reserved
...
base + 0x3FFFFFC: Reserved
PLIC寄存器的地址映射的基地址由平台实现时指定。本章中指定的内存映射寄存器的宽度为32位,这些位通过LW和SW指令自动访问。
第四章 中断优先级
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Source Priority Register | 记录每个中断源的优先级 | 每个寄存器32bit表示一个中断源的优先级,总共占用的地址空间1024*4=4096B(4KB) | 中断源编号0是保留的,不存在 |
0x000000: Reserved (interrupt source 0 does not exist)
0x000004: Interrupt source 1 priority
0x000008: Interrupt source 2 priority
...
0x000FFC: Interrupt source 1023 priority
中断优先级是无符号整数,平台指定支持的优先级的最大值。优先级0是保留用于表示不发生中断,中断优先级随着整数值的增加而增加(优先等级的值越大,优先级越高:优先级2比优先级1的优先等级更高)。每一个全局中断源都有一个对应的中断等级,保存在中断优先级寄存器中。不同的中断源可以设置成不同的中断优先级。有效的实现可以硬连接所有输入优先级。中断源优先级寄存器应该是WARL字段,允许软件决定每个优先级规范中读写位的数量和位置,如果有的话。为了简化发现支持的优先级值每个优先级寄存器必须支持寄存器内可变位的任意组合。如果在寄存器内有两个可变位,这些可变位的所有四种组合必须作为有效优先级别操作。
如果PLIC支持中断优先级,那么每个中断源可以设置一个中断优先级(通过写32位的优先级寄存器)。优先级别0是保留的,意味着永远不会发生中断,可以有效的禁止中断。优先级别1是最低的有效优先级,而最高优先级取决于PLIC的实现。具有相同优先级的全局中断直接的并列关系会被中断ID打破,较小中断ID的中断具有更高的优先级。
-
总结:
-
每个中断源拥有一个32位的优先级寄存器,可以单独设置每个中断源的优先级
-
优先级别0表示永远不会发生中断,相当于禁止该中断
-
优先级编号越低,优先等级越低
-
中断编号越小,优先等级越高
-
当同时接收到多个中断源,总裁规则:
- 先比较优先级别
- 优先级别一样,再比较中断编号
-
第五章 中断挂起位
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Pending Register | 表示中断源是否挂起 | 每个中断源占用1位表示挂起状态,总共占用1024/8=128B | 每个中断源占用1位,每个寄存器可以表示32个中断源 |
0x001000: Interrupt Source #0 to #31 Pending Bits
...
0x00107C: Interrupt Source #992 to #1023 Pending Bits
PLIC核心中中断源挂起位的当前状态可以从挂起态数组中读取。中断编号为N的挂起态位保存在第(N/32)个寄存器的第(N%32)位。第0个寄存器的第0位表示不存在的中断源编号0,硬连接为零。通过设置关联的使能位,然后执行响应,可以清除PLIC核心中的挂起位。
- 总结:
- 当中断源的使能位是打开的,中断源将中断信号上报到中断网关后,中断源对应的pending位会置一
- 当有目标从响应寄存器(claim Register)中读取出中断源ID后,此中断源对应的pending位被清零
第六章 中断使能
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Enable Register | 为每个上下文使能中断源 | (1024/8)*15872=2031616(0x1f0000)B | 每个上下文都有设置中断源的使能位,每个中断源的使能位占1位 |
0x002000: Interrupt Source #0 to #31 Enable Bits on context 0
...
0x00207C: Interrupt Source #992 to #1023 Enable Bits on context 0
0x002080: Interrupt Source #0 to #31 Enable Bits on context 1
...
0x0020FC: Interrupt Source #992 to #1023 Enable Bits on context 1
0x002100: Interrupt Source #0 to #31 Enable Bits on context 2
...
0x00217C: Interrupt Source #992 to #1023 Enable Bits on context 2
0x002180: Interrupt Source #0 to #31 Enable Bits on context 3
...
0x0021FC: Interrupt Source #992 to #1023 Enable Bits on context 3
...
...
...
0x1F1F80: Interrupt Source #0 to #31 on context 15871
...
0x1F1FFC: Interrupt Source #992 to #1023 on context 15871
通过设置使能寄存器中对应的位来使能每个全局中断。使能寄存器作为连续的32位寄存器数组被访问,其封装方式和挂起位相同。第0个寄存器的第0bit表示不存在的0号中断,硬连线为零。PLIC有15782个上下文中断使能模块。PLIC如何为上下文组织中断不再RISC-V PLIC规范范围之内,但是必须在供应商的PLIC规范中规定出来。
假如某些中断源只能路由到目标子集,大量潜在的使能位可能被硬连线为零。对于具有固定中断路由的内嵌设备,可以将大量使能位硬连线为1。即使全局中断源一直是使能的,中断优先级、阈值、hart内部的中断掩码在忽略外部中断方面提供了相当大的灵活性。
- 总结:
- 每个上下文都可以独立设置每个中断源是否上报到本上下文
- 通过设置不同上下文的同一个中断源的使能位,可以让中断源挂起后向多个上下文发起中断通知
第七章 中断阈值
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Priority Threshold Register | 为15872个上下文设置优先级阈值 | 4096 * 15872 =65011712(0x3e00000) bytes | 每个上下文都有独立的优先级阈值寄存器 |
0x200000: Priority threshold for context 0
0x201000: Priority threshold for context 1
0x202000: Priority threshold for context 2
0x203000: Priority threshold for context 3
...
...
...
0x3FFF000: Priority threshold for context 15871
PLIC提供基于上下文的阈值寄存器,用于设置每个上下文的中断优先级阈值。优先级阈值寄存器是一个WARL字段,PLIC将屏蔽优先级小于等于优先级阈值的中断。例如,优先级阈值设置成零,将允许所有优先级为非零的中断上报给上下文。
- 总结:
- 每个上下文可以设置优先级中断阈值,当处于挂起态的中断优先级小于等于优先级阈值,则中断挂起后也不会通知该上下文
第八章 中断响应
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Claim Register | 给上下文返回当前挂起态中断中优先级最高的中断ID | 4096 * 15872 =65011712(0x3e00000) bytes | 每个中断上下文有一个响应寄存器 |
0x200004: Interrupt Claim Process for context 0
0x201004: Interrupt Claim Process for context 1
0x202004: Interrupt Claim Process for context 2
0x203004: Interrupt Claim Process for context 3
...
...
...
0x3FFF004: Interrupt Claim Process for context 15871
目标接收到中断通知后,可以决定为哪个中断服务。目标发送一个中断响应消息给PLIC核心,响应消息通常实现被实现为读取一个非幂等内存映射I/O寄存器。PLIC核心接收到响应消息,将原子的确定目标处于挂起态的中断中最高优先级的中断ID,然后清除掉相应中断源的IP位,然后PLIC核心将返回中断ID给目标。如果PLIC核心处理响应消息时,目标并没有处于挂起态的中断,PLIC核心将返回零。
当最高优先级的挂起中断被目标响应,且对应的IP位被清除,低优先级的挂起中断可以被目标所见,因此在响应之后PLIC EIP位可能没有被清除。在退出中断处理程序之前,可以检查本地meip/seip/ueip位,这样可以更有效的服务其他中断,而不用恢复中断上下文再执行另一个中断的陷入。
即使EIP位没有设置,hart执行中断响应也是合法的。特别是,hart可以通过把优先级阈值设置成最大值来禁用中断通知,并使用周期性相应请求轮询挂起态中断,尽管实现轮询的一种更简单的方法是清除特权模式x对应的xie寄存器中的外部中断启用。
PLIC可以通过读claim/complete寄存器来执行中断请求,返回挂起态中最高优先级的中断ID,如果没有挂起态中断则返回零。请求成功将会清除中断源对应的挂起位。PLIC可以在任何时间执行请求,并且请求操作不受优先级阈值寄存器设置的影响。
- 总结:
- 当PLIC发送中断通知给目标后,目标读取对应的claim寄存器得到将要处理的中断ID
- 当目标读取claim寄存器时,决定出当前最高优先级的中断和清除中断pending的操作是原子的。这样能够保证当第一个请求的目标取走中断ID后,第二个申请的目标将不会再次获取到相同的中断ID(因为此时中断pending位已经被清除了),避免两个目标重复处理同一个中断。
第九章 中断完成
寄存器名字 | 功能 | 寄存器占用地址空间 | 描述 |
---|---|---|---|
Interrupt Completion Register | 向该寄存器写入完成中断的的ID,通知中断网关该中断处理完成 | 4096 * 15872 =65011712(0x3e00000) bytes | 每个上下文有一个中断完成寄存器 |
0x200004: Interrupt Completion for context 0
0x201004: Interrupt Completion for context 1
0x202004: Interrupt Completion for context 2
0x203004: Interrupt Completion for context 3
...
...
...
0x3FFF004: Interrupt Completion for context 15871
PLIC通过写从响应寄存器收到的中断ID到claim/complete寄存器来表示已经完成了中断处理程序的执行。PLIC不检查完成的中断ID是否和目标最后一次响应的ID相同。如果完成的中断ID和目标当前使能的中断源都不匹配,此次完成操作被静默忽略。
中断的处理程序完成后,相关的中断网关必须收到一个中断完成的消息,通常是写一个非幂等内存映射I/O控制寄存器。只有在接收到中断完成消息后,中断网关才能发送新的中断到PLIC核心。
- 总结:
- 当目标处理完中断(即执行完中断处理函数)后需要通知PLIC,通知方式就是向对应complete寄存器写入执行完成的中断ID
第十章 架构文档地址
第十一章 中断仲裁
- 在挂起态的中断中,筛选出中断优先级最高的中断
- 如果最高优先级有几个中断,则比较中断编号:中断编号越小,优先等级越高
第十二章 中断嵌套
12.1、非向量中断处理流程
- 保存32个通用寄存器、CSR寄存器(mepc、mcause等)到栈空间
- 向PLIC核心发送中断声明消息:读claim寄存器得到要响应的中断编号
- 打开全局中断(mstatus.mie/sie)
- 根据获取到的中断号执行对应的中断处理程序
- 向PLIC发送中断完成消息:把中断号写入complete寄存器
- 从栈中恢复32个通用寄存器和CSR寄存器
- 退出中断处理
12.2、向量中断处理流程
- 保存32个通用寄存器、CSR寄存器(mepc、mcause等)到栈空间
- 打开全局中断(mstatus.mie/sie)
- 执行中断处理程序
- 关闭全局中断(mstatus.mie/sie)
- 向PLIC发送中断完成消息:把中断号写入complete寄存器
- 从栈中恢复32个通用寄存器和CSR寄存器
- 退出中断处理
第十三章 中断咬尾
13.1、非向量中断
- 第一步:保存32个通用寄存器、CSR寄存器(mepc、mcause等)到栈空间
- 第二步:向PLIC核心发送中断声明消息:读claim寄存器得到要响应的中断编号
- 第三步:根据获取到的中断号执行对应的中断处理程序
- 第四步:向PLIC发送中断完成消息:把中断号写入complete寄存器
- 第五步:查询CORE的外部中断meip/seip位,如果还有外部中断未处理,则重新执行第二步
- 第六步:从栈中恢复32个通用寄存器和CSR寄存器
- 第七步:退出中断处理
13.2、向量中断
- 对于向量处理模式的中断而言,由于在跳入中断服务程序之前,处理器并没有进行上下文的保存,因此进行“中断咬尾”的意义不大,因此,向量处理模式的中断,没有“中断咬尾”处理能力