RISC-V CLINT、PLIC及芯来ECLIC中断机制分析 —— RISC-V中断机制(一)
在长期的嵌入式开发实践中,对中断机制的理解始终停留在表面层次,特别当开发者长期局限于纯软件抽象层面时,对中断机制的理解极易陷入"知其然而不知其所以然"的困境,这种认知的局限更为明显;随着工作需要不断深入底层技术,对硬件机制的了解逐渐加深,并积累了大量的学习笔记。借此机会,我将这些零散的知识进行系统化梳理,既是对自身知识的复盘,也希望能为相关领域的开发者提供些许帮助和参考。关于RISC-V中断机制的分析,本文将从硬件实现原理和软件应用以下两个方面来展开介绍:
RISC-V CLINT、PLIC及芯来ECLIC中断机制分析 —— RISC-V中断机制(一)
ECLIC中断流程及实际应用 —— RISCV中断机制(二)
背景
RISC-V 的中断控制器(CLINT+PLIC)以 简洁模块化 为核心,CLINT 负责本地定时器/软件中断,PLIC 管理外部设备优先级,逻辑清晰易实现。相比之下,ARM GIC 为满足复杂场景迭代多年,功能完备但配置繁琐,历史兼容性导致接口臃肿。RISC-V 通过标准化接口降低开发门槛,但其设计合理性仍需市场长期验证;ARM 则凭借成熟生态在工业级应用中占据优势。下面就来一起学习下RISCV的中断机制。
RISC-V把中断类型分为内部和外部中断,其中内部中断包括:软件中断和定时器中断;外部中断则是由外设触发的中断。
在RISC-V架构中,CLINT(Core-Local Interruptor)和PLIC(Platform-Level Interrupt Controller)是两类功能互补的中断控制器,分别负责本地中断和全局中断的管理。
CLINT只负责处理软件中断和时钟中断,因为这两个中断是RISC-V架构中定义的。经过CLINT不需要进行任何的仲裁,直接将中断(Software与Timer)送入RISC-V核中。
1 CLINT(Core-Local Interruptor)
CLINT并未未被单独列为RISC-V特权架构的独立章节,但其功能通过机器模式(M-mode)的CSR寄存器间接定义
1.1 核心功能
CLINT专用于管理本地中断,包括两类:
- 软件中断(Software Interrupt):通过写特定寄存器(MSIP/SSIP)触发,常用于多核系统中的核间通信(IPI)。
- 定时器中断(Timer Interrupt):通过mtime(mtime_hi, mtime_lo)(计时器)和mtimecmp(mtimecmp_hi, mtimecmp_lo)(比较器)触发周期性中断。
1.2 设计特点
- 无优先级仲裁:所有中断直接触发,响应速度快,但无法处理优先级冲突。
- 与CPU核心绑定:CLINT直接关联到单个CPU核心(Hart),适用于单核或轻量级多核系统。
- 操作模式:
- 直接模式:所有中断和异常跳转到固定入口地址(mtvec.BASE),需软件判断中断源。
- 向量模式:中断跳转到向量表索引对应的地址,硬件自动处理中断号,减少软件开销。
1.3 相关寄存器
- MSIP寄存器:用于生成软件中断,通过写最低有效位触发。
- mtime/mtimecmp:64位寄存器,控制定时器中断的触发时机。
1.4 标准化状态
由于CLINT无优先级仲裁、多核支持不足、扩展性不好等等,实际上各架构设计公司并未直接使用,而是对其做了改进,比如sifive的ACLINT(Advanced CLINT)
- 模块化设计:
- 将CLINT拆分为MTIMER(机器级定时器)和MSWI(机器级软件中断)模块,支持独立配置和扩展。
- 新增SSWI(监管者级软件中断),为Supervisor模式提供中断支持。
- 多核优化:
- 为每个CPU核心(Hart)分配独立的定时器和软件中断寄存器,避免资源竞争。
- 支持高效的核间通信(IPI),通过写目标Hart的MSWI寄存器触发中断。
- 向后兼容:
- ACLINT的寄存器布局与SiFive早期CLINT实现完全兼容,原有代码无需修改即可运行。
- mtime和mtimecmp寄存器地址与传统CLINT一致。
这里只是简单概括下ACLINT,感兴趣移步https://github.com/riscvarchive/riscv-aclint学习,另ACLINT已成为RISC-V官方标准化模块。
另外后续回详细介绍芯来科技的ECLIC,所以这里对CLINT也不做很过多说明。
2 PLIC(Platform-Level Interrupt Controller)
PLIC(platform-level interrupt controller),平台级中断控制器。用来将外部的全局中断请求处理后转至中断目标。PLIC理论上支持1023个外部中断源和15872个上下文,但真实设计实现时一般不需要这么多。
我们可以把PLIC看做医院的分诊台护士,把中断目标看做值班医生,而外部中断请求就像前来就诊的病人。
如果所有病人一窝蜂挤进诊室,医生会手忙脚乱无法高效诊断。因此,医院设置了分诊台:
- 分诊规则:护士(PLIC)根据病人(中断)的紧急程度(优先级)、科室开放状态(中断使能)和医生接诊能力(阈值),动态安排就诊顺序。
- 智能调度:护士只放行最危急的病人(如心脏病突发)进入诊室,普通感冒患者需排队等待。
- 资源隔离:不同医生(多核处理器)可独立配置接诊规则(如外科医生不处理眼科患者),避免任务冲突。
2.1 基本介绍
-
核心功能
PLIC负责管理全局外部中断,如GPIO、UART等外设中断,支持多核系统的优先级仲裁。 -
设计特点
- 优先级仲裁:支持中断优先级配置、阈值设置和Claim/Complete机制,确保高优先级中断优先响应。
- 多核支持:可为每个Hart的不同特权模式(如M-mode、S-mode)分配独立的中断上下文。
- 内存映射寄存器:通过基地址访问中断优先级(priority)、使能(enable)、挂起(pending)等寄存器。
-
中断处理流程
- 中断触发:外设向PLIC发送中断信号。
- 优先级仲裁:PLIC根据优先级和阈值选择最高优先级中断。
- 中断响应:Hart读取claim寄存器获取中断ID,处理完成后写入complete寄存器。
-
标准化状态
- PLIC已从RISC-V特权文档中分离,成为独立规范,托管于GitHub仓库https://github.com/riscv/riscv-plic-spec。
- 支持最多1023个中断源和15872个上下文(Hart+特权模式组合)
2.2 PLIC原理
通用PLIC操作参数寄存器块,具体包括:
- Interrupt Priorities registers:
中断优先级寄存器,每个中断源的中断优先级。 - Interrupt Pending Bits registers:
中断待处理位寄存器:每个中断源的中断等待状态。 - Interrupt Enables registers:
中断使能寄存器:每个上下文的中断源使能。 - Priority Thresholds registers:
优先级阈值寄存器:每个上下文的中断优先级阈值。 - Interrupt Claim registers:
中断请求寄存器:用于获取每个上下文的中断源ID的寄存器。 - Interrupt Completion registers:
中断完成寄存器:用于向门控关发送中断完成消息的寄存器。
-
中断汇聚与标准化接口
- 统一接入:所有外设中断(如UART、GPIO、DMA等)均接入PLIC,形成全局中断池,而非直接连接处理器核心。
- 标准化管理:PLIC为每个中断源分配唯一ID,并通过内存映射寄存器提供统一配置接口(优先级、使能、目标核心等)。
-
可编程优先级仲裁
- 动态配置:用户可通过寄存器配置以下参数:
- 中断优先级(Priority):数值越大优先级越高(如0为禁用,1~7为可配置等级)。
- 目标核心阈值(Threshold):每个处理器核心(Hart)仅响应优先级高于阈值的中断。
- 中断使能(Enable):按需开启/屏蔽特定中断源。
- 仲裁规则:
- 筛选所有已使能且优先级高于目标阈值的中断。
- 选择优先级最高的中断(若优先级相同,按中断ID升序裁决)。
- 将胜出中断路由至目标核心,触发中断服务程序(ISR)。
- 动态配置:用户可通过寄存器配置以下参数:
-
中断处理与状态维护
- Claim/Complete机制:
- Claim阶段:目标核心读取PLIC的claim寄存器获取中断ID,PLIC自动标记该中断为“处理中”,防止重复触发。
- Complete阶段:核心处理完成后,向complete寄存器写入同一中断ID,PLIC清除挂起状态,允许后续中断触发。
- 多核支持:支持为每个核心分配独立的中断上下文(Context),实现多核系统的负载均衡与中断隔离。
- Claim/Complete机制:
2.3 中断流程
相关名词:
- 外部中断源:如UART、GPIO等外设,产生中断请求。
- 中断Gateway:外设与PLIC之间的接口模块,负责初步处理中断信号。
- PLIC内核:全局中断控制器,执行优先级仲裁与路由。
- 中断目标:处理器核心(Hart),负责执行中断服务程序(ISR)。
-
步骤1:中断触发与Gateway处理
- 中断触发:外设(如UART接收完成)产生中断信号,向与其绑定的中断Gateway发送请求。
- Gateway响应:Gateway接收请求后,执行以下操作:
- 设置Pending位:将对应外设的中断挂起标志(Pending Bit)置位,标记该中断等待处理。
- 转发至PLIC:将中断请求提交给PLIC内核,等待仲裁。
-
步骤2:PLIC仲裁与中断通知
- PLIC仲裁:PLIC根据以下配置参数筛选并裁决中断:
- 优先级(Priority):数值越高中断越优先。
- 使能状态(Enable):仅处理已使能的中断源。
- 目标阈值(Threshold):仅转发优先级高于目标核心阈值的中断。
- 仲裁规则:优先级最高者胜出(优先级相同则中断ID较小者优先)。
- 通知中断目标:PLIC将仲裁胜出的中断请求发送至目标处理器核心,触发核心进入中断处理流程。
- PLIC仲裁:PLIC根据以下配置参数筛选并裁决中断:
-
步骤3:中断认领与处理
- Claim操作:
目标核心通过读取PLIC的claim寄存器获取中断ID,触发以下行为:- 清除Pending位:PLIC自动清除该中断在全局队列中的挂起状态(不涉及外设自身的Pending位)。
- 锁定Gateway:Gateway暂时禁止同一外设的新中断进入PLIC(防止重复仲裁),直到收到完成通知。
- 执行中断服务程序(ISR):核心根据中断ID跳转至对应的ISR,完成外设请求的具体处理(如读取UART数据)。
- Claim操作:
-
步骤4:中断完成与资源释放
- Complete操作:ISR执行完毕后,核心向PLIC的complete寄存器写入中断ID,触发以下操作:
- 释放Gateway:Gateway收到完成信号后,重新允许该外设的新中断请求进入PLIC。
- 更新PLIC状态:PLIC标记该中断处理完成,可参与后续仲裁。
- Complete操作:ISR执行完毕后,核心向PLIC的complete寄存器写入中断ID,触发以下操作:
2.4 PLIC寄存器
不同的处理器会给PLIC不同的基址(base),程序访问的地址采用base+offset的方式访问。
2.4.1. 中断源优先级寄存器
地址偏移(Hex) | 中断源(ID) | 功能描述 |
---|---|---|
0x000000 | 无(保留) | 中断源0不存在,保留空间 |
0x000004 | 1 | 中断源1的优先级配置 |
0x000008 | 2 | 中断源2的优先级配置 |
… | … | … |
0x000FFC | 1023 | 中断源1023的优先级配置 |
- 功能:标记中断请求的等待处理状态。
- 置位条件:外设触发中断时自动置1。
- 清除条件:读claim寄存器会清除pending位
2.4.3. 中断使能位(Enable Bits)
地址偏移(Hex) | 上下文(Context) | 中断源范围 | 功能描述 |
---|---|---|---|
0x002000 | 0 | 0–31 | 上下文0的中断源0~31使能位 |
0x002004 | 0 | 32–63 | 上下文0的中断源32~63使能位 |
… | … | … | … |
0x00207C | 0 | 992–1023 | 上下文0的中断源992~1023使能位 |
0x002080 | 1 | 0–31 | 上下文1的中断源0~31使能位 |
0x002084 | 1 | 32–63 | 上下文1的中断源32~63使能位 |
… | … | … | … |
0x1F1FFC | 15871 | 992–1023 | 上下文15871的中断源992~1023使能位 |
PLIC 通过连续32位使能寄存器组管理全局中断的启用状态,每个寄存器对应32个中断源,排列方式与挂起位一致
2.4.4. 优先级阈值、声明、完成寄存器
地址偏移(Hex) | 上下文(Context) | 功能描述 |
---|---|---|
0x200000 | 0 | 上下文0的优先级阈值配置 |
0x200004 | 0 | 上下文0的中断声明(Claim)与完成(Complete) |
0x201000 | 1 | 上下文1的优先级阈值配置 |
0x201004 | 1 | 上下文1的中断声明与完成 |
… | … | … |
0x3FFF000 | 15871 | 上下文15871的优先级阈值配置 |
0x3FFF004 | 15871 | 上下文15871的中断声明与完成 |
-
优先级阈值寄存器
PLIC为每个上下文(Hart+特权模式)提供优先级阈值寄存器(WARL字段),用于设定最低响应优先级。所有优先级≤阈值的中断将被屏蔽。例如:- 阈值为0时,允许所有非零优先级中断通过。
- 阈值设为最大值时,完全禁止中断通知,核心可通过轮询claim寄存器主动查询中断。
阈值机制平衡了实时性与中断负载,优先级范围由具体实现决定。
-
声明寄存器(Claim)
核心通过读取claim寄存器触发中断认领操作,流程如下:- PLIC原子性确定目标上下文中最高优先级的中断ID,并清除其全局挂起位(Pending Bit)。
- 若无待处理中断则返回ID=0。
- 即使核心未收到中断通知(EIP未置位),仍可主动发起认领,实现灵活轮询。
此操作不受阈值限制,但优先级≤阈值的中断不会参与仲裁。
-
完成寄存器(Complete)
核心处理完中断后,向complete寄存器写入中断ID,触发以下行为:- PLIC不校验ID是否匹配或有效,若ID无效则静默忽略。
- 网关收到完成信号后,允许对应中断源提交新请求。
- 若未完成操作,网关将阻塞同一中断源的新请求,确保原子性。
完成机制与声明操作协同,形成“认领-处理-释放”闭环。
3 芯来ECLIC
3.1 蜂鸟E200处理器的中断接口
我们先来看下蜂鸟E200处理器的中断接口
- 在处理器顶层接口中有 根中断输入信号,分别是软件中断、计时器 中断、外部中断和调试中断。
- Soc 的 CLINT 模块产生 根软件中断信号和 根计时器中断信号,通给蜂鸟 E200 处理器核
- soc 的PLIC 接入 个外部中断源将其仲裁后生成 根外部中断信号,通给蜂鸟 E200 处理器核
- Soc 层面的调试模块生成 根调试中断,通给蜂鸟 E200 处理器核
- 所有的中断信号均由蜂鸟 E200 处理器核的交付模块进行处理
硬件源码感兴趣的同学移步:https://github.com/SI-RISCV/e200_opensource
3.2 ECLIC
ECLIC(Enhanced Core-Local Interrupt Controller)是芯来科技(Nuclei)基于 RISC-V 社区提出的 CLIC(Core-Local Interrupt Controller)扩展提案 设计的增强型中断控制器,后续ECLIC也是芯来商用的内核的核心中断控制器架构。
另外CLIC 目前是 RISC-V 社区提出的 扩展提案,尚未正式纳入 RISC-V 特权架构规范(Privileged Specification)感兴趣可以参考:https://github.com/riscv/riscv-fast-interrupt。这里不做具体介绍
相较于标准 CLIC,ECLIC 在以下方面进行了增强:
- 中断咬尾(Tail-Chaining)技术:
- 通过硬件指令(如 JALMNXTI)实现零延迟中断切换,减少上下文保存与恢复的开销。
- 示例场景:当低优先级中断 B 在中断 A 处理时触发,处理完A的服务程序后,直接跳转至 B 的服务程序,无需退出 A 的上下文。
- 扩展中断号与优先级范围:
- 支持更多中断源(如外部中断号扩展至 19~4095),适配复杂外设场景。
- 优先级阈值与动态调整机制,优化实时任务响应。
- 硬件加速指令集成:
- 自定义指令优化中断处理流程,例如单指令完成中断挂起位清除与优先级更新。
- 低功耗设计:
- 在中断空闲时自动进入低功耗状态,减少嵌入式设备的能耗。
优点
避免传统多控制器(CLINT+PLIC)的复杂协同问题,简化芯片设计。
3.2.1 中断接口连线
0-18中断号用于内部中断,19~4095中断号用于外部中断,注意中断的编号与中断的优先级是没有关系的。
其中:
- 1、3号中断是内核TIMER单元生成的软件中断。
- 5、7号中断是内核TIMER单元生成的计时器中断。
uCore在M模式下处理mtime_irq和msip,在S模式下处理stime_irq和ssip。
3.2.2 ECLIC逻辑结构
有几点需要注意的是:
- 1、ECLIC单元只服务于一个core,且在核心内部是私有的
- 2、需要通过将CSR寄存器mtvec的LSB位设置为ECLIC模式来启用ECLIC(MTVEC寄存器配置)
- 3、ECLIC在功能上与PLIC互斥。
ECLIC单元用于仲裁多个内部和外部中断,向核心发送中断请求,并支持中断抢占。
3.2.3 ECLIC相关概念
和通用中断设计一样也有一系列的概念描述,下面简单说明下
-
中断目标
如3.2.2中图所示ECLIC单元通过一条线路将中断源仲裁给处理器核心(作为中断目标) -
中断源
ECLIC单元最多可支持4096个中断源。 -
中断源ID
ECLIC单元为每个中断源分配了一个唯一的ID。例如,如果ECLIC单元的硬件实现确实配置为支持4096个中断,则该ID应为0到4095。
**NOTE:**在Nuclei处理器核心中,0到18的中断ID预留用于核心指定的内部中断。3和7在所有Nuclei处理器核心中固定为软件中断和定时器中断。某些Nuclei核心可能有更多内部中断。
3.2.4 ECLIC寄存器
eclic相对core的基址是0x20000
ECLIC中的寄存器和相应的偏移量
偏移量 (Offset) | 权限 (Permission) | 寄存器名称 (Register) | 位宽 (Width) | 说明 |
---|---|---|---|---|
0x0000 | MRW/SRO | cliccfg | 8-bit | 配置寄存器,控制ECLIC全局行为(优先级位宽)。 |
0x0004 | SRO | clicinfo | 32-bit | 只读信息寄存器,包含ECLIC硬件实现信息(优先级位宽、实际中断源数量)。 |
0x000B | MRW | mth | 8-bit | 机器模式阈值寄存器,设置当前上下文的中断响应优先级阈值。 |
0x1000 + 4*i | MRW | clicintip[i] | 8-bit | 中断挂起寄存器组,标记中断源 i 的挂起状态(Pending Bit)。 |
0x1001 + 4*i | MRW | clicintie[i] | 8-bit | 中断使能寄存器组,控制中断源 i 是否启用。 |
0x1002 + 4*i | MRW | clicintattr[i] | 8-bit | 中断属性寄存器组,配置中断源 i 的触发方式(边沿/电平)和模式(向量/非向量)。 |
0x1003 + 4*i | MRW | clicintctl[i] | 8-bit | 中断控制寄存器组,设置中断源 i 的优先级(Priority Level)。 |
上述“i”表示中断ID、ECLIC注册仅支持对齐访问、ECLIC寄存器的地址空间为0x0000到0x FFFF的范围
- MRW:机器模式可读写(Machine Read-Write)。
- SRO:监管者模式只读(Supervisor Read-Only)。
-
cliccfg
cliccfg寄存器是一个全局配置寄存器,用来控制中断级别和优先级。芯来官方spec有具体说明,感兴趣可以自行学习。 -
clicinfo
clicinfo寄存器是一个全局信息寄存器,软件通过读取该寄存器查询全局参数。
一般cliccfg配置会基于从clicinfo获取到的信息。 -
mth
mth寄存器用于设置目标中断阈值水平。- 作用:mth(Machine Threshold Register)寄存器用于设置当前上下文(Context)的中断响应优先级阈值,仅允许优先级高于该阈值的中断触发处理器响应。
- 例如:设置 mth = 3:仅优先级 ≥4 的中断会触发处理器处理。
-
clicintip[i]
用于标记 中断源的挂起状态,即记录哪些中断请求已触发但尚未被处理器响应或处理。- 如果一个中断源的IP位为1,则表示触发了该中断。
- 对于边沿触发中断源,硬件本身可以清除IP位。
- 如果中断源设置为电平触发,IP位实时反映中断源的电平,
-
clicintie[i]
用来控制对应中断号的使用,
如果clicintie[i]寄存器被编程为0,则表示该中断源被屏蔽。反之表示中断被使能 -
clicintattr[i]
用于设置1、中断源的电平或边沿触发属性 2、中断是向量型还是非向量型 -
clicintctl[i]
ECLIC中断级别和优先级,受cliccfg的影响。- 级别值(Level)越大,优先级越高
- 优先级值(Priority)越大,优先级越高
- 中断ID越大,优先级越高
芯来官方ISA spec有具体说明,感兴趣可以自行找来深入学习
3.2.5 ECLIC中断仲裁机制
-
中断参与仲裁的前提是:
- 1、对应中断使能打开
- 2、对应penging位置起(中断触发)
-
仲裁原则是:
- 1、检查中断Level,中断源的Level越大,仲裁优先级越高。
- 2、如果Level相同,则检查Priority,优先级值较大的中断源具有更高的仲裁优先级。
- 3、如果电平和优先级都相等,则考虑ID。中断ID较大的中断源具有更高的仲裁优先级。
3.2.6 向量(Vectored)和非向量(Non-Vectored)中断
在CLIC模式下,每个中断源可以配置为向量处理模式或非向量处理模式(通过ECLIC寄存器clicintattr[i]的shv字段)。
-
向量中断(Vectored Interrupts)
- 定义:每个中断源拥有独立的中断服务程序(ISR)入口地址,硬件根据中断 ID 直接跳转至对应入口,无需软件判断中断源。
- 核心特性:
- 硬件自动跳转:中断触发后,ECLIC 根据中断源 ID 直接定位 ISR 入口地址。
- 低延迟:省去软件查询中断源的时间,显著提升实时性。
- 向量表配置:需预先在内存中定义中断向量表,存储各中断源的 ISR 地址。
-
非向量中断(Non-Vectored Interrupts)
定义与机制
• 定义:所有中断共享一个公共入口地址,软件需读取中断挂起寄存器(Pending Bits)判断具体中断源,再分派至对应 ISR。
• 核心特性:
○ 统一入口:所有中断触发后均跳转至同一入口地址。
○ 软件分派:通过读取 CLICINTIP 或 CLICCLAIM 寄存器确定中断源。
○ 灵活性高:适合中断源动态变化或资源受限的系统。
对于具体的实现可以参考 ECLIC中断流程及实际应用 —— RISCV中断机制(二)。
3.2.7 中断抢占(Preemption)和中断咬尾(Tail-Chaining)
CLINT模式下,core不支持中断抢占和中断咬尾的。芯来科技设计的增强型中断控制器,在 RISC-V 标准 CLIC 基础上优化了中断响应效率,二者协同工作以实现低延迟和高实时性的中断处理
1. 中断抢占(Preemption)
如果处于CLIC模式,当核心正在处理一个中断时,可能会有更高优先级的新中断请求,此时核心可以停止当前的中断服务例程,开始处理新的中断并执行其“中断服务例程”。因此,形成了中断抢占(即前一个中断尚未返回,而新中断已被抢占),并且可能存在多层嵌套。
定义与机制:
- 定义:高优先级中断可抢占当前正在处理的低优先级中断,立即获得处理器执行权。
- 触发条件,新到达的中断满足以下条件:
1. 优先级更高:新中断的 Level + Priority 组合高于当前中断。
2. 阈值允许:新中断的优先级高于当前上下文的阈值(mth 寄存器配置)。
3. 中断使能:新中断在 CLICINTIE[i] 中被启用。
抢占流程:
- 抢占检测:ECLIC 硬件实时监控中断队列,发现更高优先级中断时触发抢占。
- 上下文保存:当前中断的上下文(如寄存器状态)被压栈保护。
- 执行新中断:处理器跳转至新中断的服务程序(ISR)入口。
- 恢复原中断:新 ISR 执行完成后,恢复原中断的上下文并继续执行。
NOTE:向量中断和非向量中断的中断抢占是有区别的,具体参考 ECLIC中断流程及实际应用 —— RISCV中断机制(二)。
- 中断咬尾
如果在CLIC模式下,核心正在处理一个中断时,启动一个新的中断请求,但是新请求的级别不高于处理的请求,因此新的中断请求不能抢占处理的请求。在处理完当前中断后,理论上需要恢复上下文,然后退出中断服务例程,返回主程序,然后处理新的中断。“尾链”可以节省这种背靠背的“上下文保存”和“上下文恢复”的成本。
定义与机制:
- 定义:当新中断到达时,若其优先级 不高于当前中断,但当前中断已处理完成,ECLIC 硬件直接跳转至新中断的 ISR,跳过上下文恢复与再保存的开销。
- 触发条件:
1. 新中断优先级 ≤ 当前中断优先级。
2. 当前中断的 ISR 已执行完毕(ECLIC_CLAIM 完成操作)。
咬尾流程:
- 中断完成:当前 ISR 执行 ECLIC_CLAIM 完成操作,释放中断源。
- 检测新中断:ECLIC 检测到挂起的新中断,优先级 ≤ 当前中断。
- 直接跳转:硬件直接加载新 ISR 的入口地址,不恢复原上下文(因原上下文已处理完毕)。
NOTE:只有非向量中断才支持中断咬尾功能,具体参考 ECLIC中断流程及实际应用 —— RISCV中断机制(二)。
- 抢占与咬尾的协同作用
场景 | 抢占机制 | 咬尾机制 |
---|---|---|
高优先级中断到达 | 立即抢占当前中断 | 不触发 |
同优先级中断连续到达 | 不触发 | 直接跳转至下一个中断 |
低优先级中断到达 | 不触发 | 等待当前中断完成后触发咬尾 |
协同流程示例:
- 中断 A(Level=2)正在执行。
- 中断 B(Level=3)到达 → 触发抢占,保存 A 的上下文,执行 B。
- 中断 C(Level=2)在 B 执行期间到达 → 不满足抢占条件,挂起等待。
- B 执行完成 → 检测到挂起的 C,触发咬尾,直接跳转至 C 的 ISR。
- C 执行完成 → 恢复 A 的上下文,继续执行。
ECLIC 通过 中断抢占 和 中断咬尾 机制,显著优化了中断响应效率:
- 抢占 确保高优先级任务即时响应,适合安全关键型应用。
- 咬尾 减少冗余的上下文操作,适合高频、连续中断场景。
开发者需结合具体需求,合理配置 Level、Priority 和阈值,并利用芯来提供的自定义指令( jalmnxti)和特权寄存器CSR_JALMNXTI最大化硬件加速潜力,具体参考 ECLIC中断流程及实际应用 —— RISCV中断机制(二)。
4 CLINT、PLIC、ECLIC区别总结
这里总结不是简单罗列,也是尽量补充些前面没提到的。
CLINT其实没什么可以多总结的,就是负责管理内部中断(软件中断和定时器中断),ECLIC是相当于对本地和全局中断上做了整合,相当于CLINT+PLIC的结合;在单core下确实可以这么认为,但现在大多数的芯片都是多个core,所以ECLIC并不能直接替代CLINT+PLIC。
实际上ECLIC也只是本地中断管理,也就是说在多core的情况下,每个core都有一个ECLIC,此时外部来了一个中断要分给哪个core呢?
4.1 单core下的中断接线架构
- 单核下仅配置了PLIC和CLINT
- 单核下仅配置了ECLIC
可以看到单核下eclic确实可以直接完成PLIC+CLINT的工作
4.2 多核(SMP)中断接线图
图示为多核情况下为配置了PLIC和ECLIC,外部进来的中断可以通过PLIC直接分发给各个core,而ECLIC则需要一个CIDU(Cluster Interrupt Distribution Unit )分发给特定core内部的ECLIC,仲裁后再给core处理
ECLIC没有PLIC的门控或者说路由功能,需要CIDU协助,理论上SMP多核可以完全通过CIDU+ECLIC来取代PLIC+CLINT,AMP多核则可以在CLUSTER外部在加一级soc平台级别IDU来完成;但实际上是还要看芯片的复杂程度和最终的应用。这可能也是芯来科技做ECLIC方案的原因之一吧。
简单总结一下:
特性 | PLIC | ECLIC |
---|---|---|
中断类型 | 仅外部中断 | 本地中断 + 外部中断 |
优先级机制 | 单一优先级(0~7) | Level + Priority 分层仲裁 |
延迟优化 | 中等(需仲裁和 Claim 操作) | 极低(抢占 + 咬尾机制) |
多核协同 | 依赖核间中断(IPI) | CIDU 来支持中断分发 |
适用场景 | 通用多核系统(服务器、网关) | 实时嵌入式系统(工业控制、自动驾驶) |
问题
最后这里留个问题:为什么多核服务器和网关推荐使用PLIC呢?
我认为原因有二:
- 1、当前linux操作系统不支持中断抢占,PLIC的Claim/Complete 机制,确保PLIC中断处理的原子性,而ECLIC的中断抢占和咬尾正是其优势所在。
- 2、linux对PLIC驱动做了适配,无需重复造轮子(可以通过idu+eclic配合保证中断处理的原子性,linux已经做了PLIC的驱动的适配,所以也没必要再折腾)
有问题及不足之处,还请各位大佬补充。
参考:
RISV ISA spec
riscv-plic-spec
riscv-fast-interrupt
Sive ACLINT spec
芯来科技N级别指令集架构
RISC-V SiFive U54内核——中断和异常详解