
本篇定位:RISC-V 专题第 5 篇。本篇讲清 RISC-V 中断的三层结构(本地中断/CLINT/PLIC)、claim-complete 流程、中断嵌套、ECLIC模式及AIA 新标准。先理清三层结构RISC-V 中断不是一坨,是三层:本地中断(核心内部,3 种):MTI/MSI/MEI——直接进 CPU,不经外部控制器CLINT(Core Local Interruptor):给 MTI/MSI 用的内存映射器件,产生定时器和软件中断PLIC(Platform-Level Interrupt Controller):给 MEI 用的外部中断仲裁器,所有外设(I2C/UART/SPI)中断经它你配的外设如 I2C 中断 外设中断 → PLIC → MEI → CPU。你的 FreeRTOS tick 定时器 → CLINT → MTI → CPU。两条路,分开理解。目录一、三种本地中断(MTI/MSI/MEI)1.1 使能与触发条件1.2 MSI(软件中断)1.3 MTI(定时器中断)——FreeRTOS tick 的基础1.4 MEI(外部中断)——所有外设中断的统一入口二、CLINT(Core Local Interruptor)关键点三、PLIC:外部中断仲裁器(重点)3.1 PLIC 的角色3.2 PLIC 寄存器布局(典型)3.3 配置一个外设中断的完整流程3.4 claim/complete 流程(handler 里)3.5 优先级与阈值3.6 多核分发四、中断嵌套(④的延伸,这里讲中断视角)4.1 默认不嵌套4.2 开嵌套4.3 优先级嵌套(用 PLIC threshold)五、AIA:新中断架构5.1 传统 PLIC 的局限5.2 AIA 的组成5.3 AIA 的好处六、ECLIC增强型核本地中断控制器6.1 ECLIC功能6.2 AIA 和 ECLIC核心区别七、PLIC / CLINT / AIA / ECLIC理解八、中断延迟与性能8.1 中断延迟的组成8.2 优化方向九、ARM vs RISC-V 中断对照十、本篇小结速查表一、三种本地中断(MTI/MSI/MEI)RISC-V 每个核有 3 种本地中断,对应 mcause code:中断mcause code来源用途MSI(Machine Software Interrupt)3软件写 MSIP 位核间通信(IPI)、自中断MTI(Machine Timer Interrupt)7mtime ≥ mtimecmpFreeRTOS tick、延时MEI(Machine External Interrupt)11PLIC 仲裁外设I2C/UART/SPI/GPIO 等所有外设1.1 使能与触发条件一个中断要触发,需同时满足:mstatus.MIE 1(全局中断开)mie对应位 1(MSIE/MTIE/MEIO)mip对应位 1(挂起)不被更高优先级屏蔽mip 位:bit3MSIP, bit7MTIP, bit11MEIP mie 位:bit3MSIE, bit7MTIE, bit11MEIE1.2 MSI(软件中断)触发:写 CLINT 的 MSIP 位(内存映射,每核一个)用途:核间中断(IPI)——核 A 写核 B 的 MSIP,核 B 收到 MSI也用于自中断:核自己写自己 MSIP,触发延迟处理mip.MSIP 可写(其他 mip 位大多只读)1.3 MTI(定时器中断)——FreeRTOS tick 的基础触发:mtime(实时计数器)≥mtimecmp(比较寄存器)关键:MTI 不会自动清除——handler 必须更新 mtimecmp(设成下次 tick 的时间),否则中断立即重触发FreeRTOS tick:每个 tick 设 mtimecmp tick_period,触发 MTI,handler 更新 mtimecmp// FreeRTOS tick handler(简化)voidvPortTickHandler(void){// 清中断:更新 mtimecmp 到下个 tickmtimecmpmtimeTICK_PERIOD;// 调度if(xTaskIncrementTick()!0){vTaskSwitchContext();}}嵌入式视角:这就是 FreeRTOS tick 的底层如果决定用MTI作为系统tick你用 FreeRTOS 的vTaskDelay、tick 计数,底层全是 MTI。RISC-V port 里vPortSetupTimerInterrupt()就是配 mtimecmp。你调 tick 精度,本质是调 mtimecmp 的更新逻辑。ARM SysTick 有自动重载,RISC-V 没有全靠软件——又一个软件负责的例子。有些厂商可能会使用自己的timer外设作为tick不使用MTI1.4 MEI(外部中断)——所有外设中断的统一入口触发:PLIC 仲裁后,把某外设中断送进 CPU 的 MEI 线handler 看到 MEI 后,要问 PLIC “是谁触发的”(claim),PLIC 返回中断号处理完要告诉 PLIC “处理完了”(complete),PLIC 才会再送下一个二、CLINT(Core Local Interruptor)CLINT 是内存映射的器件,管 MTI 和 MSI:CLINT 寄存器(典型布局,地址看芯片手册): MSIP0 (0x0) 核0 软件中断挂起(写1触发,写0清除) MSIP1 (0x4) 核1 ... ... MTIMECMP0(0x4000) 核0 定时器比较 MTIMECMP1(0x4008) 核1 ... ... MTIME (0xBFF8) 全局实时计数器(64位,所有核共享)关键点mtime是 64 位计数器,持续递增(由时钟驱动),所有核共享每核有自己的mtimecmp,比较触发各自 MTImtime读取要小心:rv32 下 64 位要读两次(高低 32 位),可能撕裂。标准读法:uint64_tread_mtime(void){uint32_thi,lo,hi2;do{hi*(uint32_t*)(CLINT0xBFFC);lo*(uint32_t*)(CLINT0xBFF8);hi2*(uint32_t*)(CLINT0xBFFC);}while(hi!hi2);// 防撕裂return((uint64_t)hi32)|lo;}CLINT vs ARM SysTickARM SysTick 是核内 24 位倒计数器,简单但精度有限。RISC-V 的 mtime 是 CLINT 里 64 位正计数器,精度高、可共享、可多核。代价是要配 CLINT(外部器件)而非核内寄存器。三、PLIC:外部中断仲裁器(重点)PLIC 是 RISC-V 处理外设中断的标准器件。你配 I2C/UART/SPI 中断,核心就是配 PLIC。3.1 PLIC 的角色外设中断源(I2C/UART/SPI/GPIO...) │ ▼ ┌────────┐ │ PLIC │ 仲裁:优先级/使能/目标分发 └────────┘ │ ▼ CPU 的 MEI 线(每个核一个或多个)PLIC 干三件事:收集所有外设中断源仲裁:按优先级选一个,送给目标 CPUclaim/complete:CPU 问谁中断了,PLIC 给中断号;CPU 处理完告诉 PLIC3.2 PLIC 寄存器布局(典型)PLIC 基址(看芯片手册): 0x000000 Source 1 Priority (中断源1的优先级) 0x000004 Source 2 Priority ... 0x001000 Enable bits for hart 0 (核0的使能位图,每位对应一个中断源) 0x001080 Enable bits for hart 1 ... 0x020000 Hart 0 Threshold (核0的优先级阈值) 0x020004 Hart 0 Claim/Complete (核0读claim,写complete)3.3 配置一个外设中断的完整流程以 I2C 中断为例(假设 I2C 是 PLIC 中断源 5):// 1. 设优先级(0禁用,1-7 越大越高)*(uint32_t*)(PLIC0x000014)5;// source 5 priority 5// 2. 在目标核的使能位图里开这位*(uint32_t*)(PLIC0x001000)|(15);// hart0 enable source 5// 3. 设阈值(只接受优先级 threshold 的中断)*(uint32_t*)(PLIC0x020000)0;// hart0 threshold 0(接受所有)// 4. CPU 侧开 MEIE(mie.bit11)asmvolatile(csrs mie, %0::r(111));// 5. 开全局中断 MIEasmvolatile(csrs mstatus, %0::r(13));3.4 claim/complete 流程(handler 里)voidexternal_interrupt_handler(void){// 1. Claim:问 PLIC 谁中断了uint32_tsource*(uint32_t*)(PLIC0x020004);// PLIC 自动清除该源的挂起位,返回中断号// 2. 分发处理switch(source){case5:i2c_irq_handler();break;case6:uart_irq_handler();break;case7:spi_irq_handler();break;case0:return;// 0 无中断(假中断)default:unknown_irq(source);}// 3. Complete:告诉 PLIC 处理完了*(uint32_t*)(PLIC0x020004)source;// PLIC 收到后,允许该源再次触发}claim/complete 是防中断丢失/重触发的关键claim 时 PLIC 自动清挂起位:所以你不用手动清外设的挂起(部分外设还要清自己的)complete 前 PLIC 不会再送同一源:所以你必须在处理完后再 complete忘了 complete:该中断源永远不再触发(PLIC 以为你还在处理)complete 太早:中断还没处理完,PLIC 又送同一个,handler 重入,栈爆这俩错误是 RISC-V 中断调试的高频坑。3.5 优先级与阈值每个中断源有优先级(0-7 或更高,0禁用)每个核有阈值(threshold),只接受优先级 threshold 的中断多个中断同时挂起:PLIC 送优先级最高的;同优先级按中断号小的先软件嵌套:handler 里调高 threshold,只允许更高优先级中断抢占3.6 多核分发每个核有自己的使能位图和阈值一个中断源可设成送给多个核(PLIC 选一个送)或固定送某核多核系统中,IPI 常用 MSI(写 MSIP)而非 PLIC四、中断嵌套(④的延伸,这里讲中断视角)4.1 默认不嵌套进 trap 硬件 MIE←0,handler 期间新中断挂起但不触发。4.2 开嵌套handler 里开 MIE:voidirq_handler(void){// MIE0(硬件关的)csrc mie,MEIE_BIT;// 先关外部中断,防自己重入csrs mstatus,MIE_BIT;// 开全局,允许 MTI/MSI 抢占// ... 处理 ...csrc mstatus,MIE_BIT;// 关csrs mie,MEIE_BIT;// 恢复}4.3 优先级嵌套(用 PLIC threshold)voidhigh_prio_irq_handler(void){// 当前 threshold T,本中断优先级 P Tuint32_told_thresholdread_plic_threshold();write_plic_threshold(P);// 只允许 P 的中断抢占csrs mstatus,MIE_BIT;// 开嵌套handle();csrc mstatus,MIE_BIT;write_plic_threshold(old_threshold);}这是 RISC-V 实现优先级嵌套的标准手法——ARM 用 NVIC 优先级寄存器自动做,RISC-V 靠 PLIC threshold 手动开关。五、AIA:新中断架构AIA(Advanced Interrupt Architecture)是 RISC-V 新标准,解决传统 PLIC 的局限:5.1 传统 PLIC 的局限只支持 MSI 通知(一个中断一个核),不支持 MSI 向量化不支持虚拟化(中断不能直接送 Guest)优先级数量有限多核负载均衡弱5.2 AIA 的组成组件作用APLIC(Advanced PLIC)替代 PLIC,支持直接派发或转 MSIIMSIC(Incoming MSI Controller)每核一个,接收 MSI 消息,支持向量化(每个中断独立入口)MSI 机制外设写内存地址触发中断(类似 PCIe MSI)5.3 AIA 的好处向量化:每个 MSI 有独立向量号,省 claim 分发AIA 的向量化体现在每个 MSI 自带独立 ID软件 claim 时直接拿到 ID 号无需像 PLIC 那样做位扫描bit-scan来定位中断源省掉了 claim 阶段的遍历开销。但 ISR 分发跳转仍是软件负责的不像 ECLIC 那样硬件直接跳转到向量表入口。虚拟化友好:中断可送 Guest,无需 hypervisor 转发AIA 为每个 hart 的 IMSIC 配备了 Guest Interrupt FileVS 级中断文件配合 RISC-V IOMMU 的 MSI 重映射能力可以实现 PCIe 设备的 MSI 写请求经过 IOMMU 重映射后直接写入目标 vCPU 的 Guest Interrupt File全程零 VM-exit。Hypervisor 不需要捕获、模拟、再注入Guest OS 可以直接读写 vstopei 等 CSR 来管理自己的中断就像在物理机上一样。 对比传统 PLIC 方案每次外部中断都要 VM-exit 到 Hypervisor由 Hypervisor 从 PLIC claim 后再通过 hvip 注入给 Guest开销巨大。AIA 把这个路径彻底打通了这也是它成为 RISC-V 服务器标配的关键原因。更适合 PCIe/高性能:MSI 是 PCIe 标配,AIA 原生支持PCIe 规范本身就要求使用 MSI/MSI-X 作为中断机制INTx 是遗留兼容模式。AIA 的 IMSIC 就是专门为 MSI 设计的分布式接收器每个 hart 独立一个 IMSIC设备直接写目标 hart 的 IMSIC 物理地址4KiB 对齐的内存映射页即可触发中断无需经过中央仲裁器消除了 PLIC 的集中竞争瓶颈。中断在 NoC 上以标准 store 事务传输与普通内存写同质化QoS 可统一管理。RISC-V 服务器 SoC 规范甚至明确禁止支持 INTx强制要求 MSIAIA 就是为此量身定做的。统一优先级排序:AIA 新增的 CSR如 stopi、mtopi允许软件对定时器中断、软件中断IPI、外部中断做跨类型的统一优先级排序这在 PLIC 时代是做不到的。IPI 与 MSI 机制统一核间中断IPI也通过写目标 hart 的 IMSIC 来实现和设备 MSI 走同一条路径简化了软件模型。评估新芯片的中断架构拿到新芯片手册,先看 interrupt controller 章节:是 CLINTPLIC(传统)还是 ACLINTAIA(新)。这决定你的中断初始化代码、RTOS 移植层、性能特性。别假设所有 RISC-V 芯片中断一样——模块化的代价就是各芯片实现不同。六、ECLIC增强型核本地中断控制器6.1 ECLIC功能是芯来科技在 RISC-V CLIC 标准基础上优化的中断控制器专为嵌入式实时场景设计硬件向量化每个中断源有独立向量入口硬件自动跳转无需软件分发。优先级抢占支持 per-interrupt 的 8 位优先级配置高优先级可抢占低优先级实现中断嵌套。轻量简洁不包含虚拟化等复杂功能最小化硬件面积适合资源受限的 MCU。6.2 AIA 和 ECLIC核心区别对比维度AIAAdvanced Interrupt ArchitectureECLICEnhanced Core-Local Interrupt Controller定位高性能服务器/多核处理器嵌入式/MCU 实时系统标准状态RISC-V 国际官方规范2023年批准基于 CLIC 扩展芯来等厂商实现MSI 支持✅ 原生支持通过 IMSIC❌ 不支持虚拟化✅ 硬件级中断文件直通❌ 无硬件向量❌ 不支持✅ 支持硬件直接跳转到 ISR优先级抢占软件可配置相对优先级✅ 硬件级中断嵌套抢占核心组件IMSIC APLIC单控制器类似 CLINT 增强版典型芯片香山昆明湖、服务器 SoCGD32VF103、芯来 N 系列AIA 是大而全的服务器级中断架构解决 MSI、虚拟化、多核扩展问题ECLIC 是小而精的嵌入式中断控制器解决实时性、低延迟、硬件向量问题。两者不冲突可在同一系统中分别负责外部中断和核本地中断的管理。七、PLIC / CLINT / AIA / ECLIC理解对比维度CLINTPLICCLIC / ECLICAIA定位核本地中断器平台级中断控制器核本地中断控制器增强版高级中断架构标准归属RISC-V 官方规范RISC-V 官方规范CLIC 官方规范 / ECLIC 芯来扩展RISC-V 官方规范2023批准管理范围软件中断 定时器中断外部中断外设统一管理所有中断外部中断 MSI 虚拟化中断硬件向量❌❌✅ 每中断独立入口❌ID化软件分发中断嵌套❌❌✅ 硬件级抢占咬尾✅ 阈值抢占MSI 支持❌❌❌✅ 原生支持IMSIC虚拟化❌❌❌✅ 硬件级直通零 VM-exit中断延迟~20 周期~20 周期最快 6 周期微秒级虚拟化场景典型场景所有 RISC-V 芯片Linux SoC / 通用平台嵌入式 MCU / 工业实时服务器 / AI / PCIe 设备代表芯片香山雁栖湖/南湖SiFive U54芯来 N200/N300、SiFive E2香山昆明湖、玄铁 C908八、中断延迟与性能8.1 中断延迟的组成中断发生 → 进入 handler 的时间: ① PLIC 仲裁延迟 (几个 cycle) ② CPU 响应延迟 (当前指令执行完,几个 cycle) ③ trap 入口硬件动作 (改 CSR,1-2 cycle) ④ 软件保存上下文 (存 N 个寄存器,几十 cycle) ⑤ (可选)切栈 (csrrw,1 cycle) ⑥ 跳到 C handler (几个 cycle)8.2 优化方向减少保存寄存器:只存 C handler 实际用的(但要保证正确)关键中断用汇编直接处理:跳过 C 调用开销vectored 模式:省 claim 读取优先级分离:高频中断低延迟处理,低频中断慢处理九、ARM vs RISC-V 中断对照ARM Cortex-M(NVIC)RISC-V(CLINTPLIC)定时器SysTick(核内)mtimemtimecmp(CLINT,外置)软件中断无专用(用 SVCall)MSI(写 MSIP)外部中断NVIC 统一管PLIC 仲裁优先级NVIC 优先级寄存器(硬件)PLIC prioritythreshold向量表VTOR(硬件自动跳转)mtvec(软件分发或 vectored)中断号固定(厂家定)PLIC source ID(可配)嵌套硬件自动(优先级)软件(MIE threshold)Claim无(向量表直接跳)必须读 PLIC claim 寄存器Complete清外设挂起即可必须写 PLIC completeRISC-V 中断比 ARM 啰嗦的原因ARM NVIC 把优先级/向量表/嵌套全做进硬件,你配几个寄存器就完事。RISC-V 把这些分散到 CLINTPLIC软件,你要手动 claim/complete/管嵌套。灵活(可定制仲裁、多核分发)但繁琐。你写 RISC-V 中断初始化代码比 ARM 长 2-3 倍,这是架构代价。十、本篇小结三种本地中断:MSI(软件/IPI)/MTI(定时器,FreeRTOS tick)/MEI(外部,经 PLIC)CLINT 管 MTI/MSI(mtime/mtimecmp/MSIP,内存映射)PLIC 管外设中断:priority/enable/threshold/claim-completeclaim/complete 必须成对:claim 清挂起,complete 允许再触发中断嵌套靠手动开 MIE PLIC threshold 控制优先级AIA 是新标准(APLICIMSIC),支持 MSI 向量化和虚拟化,评估新芯片要识别PLIC / CLINT / AIA / ECLIC区别中断延迟的评估与优化ARM NVIC 高度集成,RISC-V 分散到 CLINTPLIC软件,灵活但繁琐速查表想干啥怎么做开全局中断csrs mstatus, 8开外部中断csrs mie, (111)开定时器中断csrs mie, (17)配 PLIC 中断源写 priority enable 位handler 里识别中断读 PLIC claim 寄存器处理完通知 PLIC写 PLIC complete 寄存器设 tickmtimecmp mtime periodIPI写目标核的 MSIP 位优先级嵌套handler 里调高 PLIC threshold 开 MIE看中断挂起csrr t0, mip技术之路漫漫分享是为了更好地交流。如果本文的内容对你有启发希望能得到你的点赞 和收藏 ⭐。如果你在调试过程中遇到了其他问题欢迎在评论区 留言我们一起探讨。也欢迎关注 我一起交流底层开发的那些事儿。