作者:Herbert,来源:易灵思
随着 RISC -V处理器在 FPGA 领域的广泛应用,易灵思 FPGA 的 Sapphire RISC-V 内核凭借软硬核的灵活支持,为开发者提供多样选择。本文深入探讨 Sapphire SoC 中 RISC - V 平台级中断控制器(PLIC),解析其架构与操作机制,助力你深入了解与应用。
RISC-V中断生态系统导论
1.1 中断在现代计算系统中的角色
在现代计算系统中,中断是实现异步事件处理的核心机制。它允许外部设备(如网络控制器、磁盘驱动器或用户输入设备)在需要处理器关注时主动发送信号,从而将处理器从低效的、持续性的轮询(polling)模式中解放出来。通过中断机制,系统能够高效地管理多任务并行、处理I/O操作,并对外部事件做出快速响应,这对于构建高性能、高能效的计算平台至关重要。
1.2 中断架构的划分:本地中断与全局中断
RISC-V的特权架构对中断进行了明确的分类,这种设计策略性地分离了不同层级的关注点,以同时优化延迟和可扩展性。中断源被划分为两大类:本地中断(Local Interrupts)和全局中断(Global or External Interrupts)。
本地中断
本地中断源直接连接到特定的处理器核心(在RISC-V中称为“Hart”),其处理不经过平台级中断控制器(PLIC)。RISC-V标准定义了两种核心的本地中断:软件中断(Software Interrupts)和计时器中断(Timer Interrupts)。这些中断通常由一个名为核心本地中断器(Core Local Interrupter, CLINT)或更高级的核心本地中断控制器(Core Local Interrupt Controller, CLIC)的模块管理 。本地中断的关键特性是延迟极低,因为它不需要在多个Hart之间进行仲裁,并且服务Hart可以通过直接读取mcause等控制与状态寄存器(CSR)来快速确定中断源 。这种设计确保了对时间敏感的核心级功能(如上下文切换调度、核间通信)的最高效处理。
全局(外部)中断
全局中断,也常被称为外部中断,源自处理器核心外部的平台级设备,例如UART、GPIO、以太网控制器或DMA引擎 。在一个复杂的片上系统(SoC)中,这类中断源的数量可能非常庞大。为了有效管理这些中断,RISC-V架构引入了一个专用的、可扩展的控制器,即平台级中断控制器(PLIC)。全局中断需要一个集中的仲裁单元来处理来自众多外设的请求,根据预设的优先级进行裁决,并将中断信号路由到一个或多个目标Hart 。
1.3 平台级中断控制器(PLIC)的目标与重要性
PLIC是RISC-V针对全局中断管理所提出的标准解决方案,其在SoC设计中扮演着基石的角色 。它的核心目标是解决在拥有丰富外设集的复杂系统中管理大量中断源的挑战。PLIC的主要功能可以概括为以下三点:
多路复用(Multiplexing):将来自多达1023个不同外部设备的中断信号汇集起来进行统一管理。 优先级仲裁(Prioritization):提供硬件支持的中断优先级机制,确保高优先级的紧急事件能够优先得到处理。 路由(Routing):根据软件的配置,将裁决后的中断通知精确地分发到指定的一个或多个Hart上下文(Hart Context)。
通过将全局中断的管理从核心中分离出来,PLIC的设计体现了RISC-V架构的模块化和可扩展性理念。它使得SoC设计者能够根据平台外设的复杂程度来调整PLIC的实现规模,而不会影响核心内部计时器等基础中断机制的性能。对于旨在运行如Linux等功能完备的操作系统的平台而言,一个高效的PLIC是不可或缺的组成部分。
PLIC架构深度解析
PLIC的硬件架构是一个精心设计的、可配置的硬件排序与过滤引擎。它将识别最高优先级中断的复杂任务从软件层面转移到硬件层面,从而显著降低了中断处理的延迟。若没有PLIC,软件在收到一个通用的“外部中断”信号后,必须通过轮询大量外设的状态寄存器来确定中断源,并依据软件逻辑判断其优先级,这一过程缓慢且效率低下。PLIC通过其硬件仲裁机制,将这一串行、低效的软件轮询循环转变为并行、高速的硬件决策过程。
2.1 概念框图与组件交互
下图(图1)展示了PLIC的架构,描绘了从中断源到中断目标(Hart)的完整信号流。

图 1: RISC-V PLIC 概念架构框图
该架构主要由四个核心组件构成:中断源、中断网关、PLIC核心以及中断目标。
2.2 中断源与中断网关:信号接收的前线
中断源(Interrupt Sources):指产生中断信号的外部物理设备,如UART、I2C控制器等。PLIC规范支持多达1023个中断源,其ID范围为1至10。中断ID 0被特殊保留,表示“无中断”。 中断网关(Interrupt Gateways):每个中断源都连接到一个专用的中断网关。网关的职责是处理来自设备的原始中断信号(可以是电平触发、边沿触发等),并将其转换为标准化的中断请求格式,然后转发给PLIC核心。网关确保在任何时刻,每个中断源在PLIC核心中最多只能有一个挂起的请求。只有在收到前一个中断的完成消息后,网关才会转发来自同一源的下一个新请求。
2.3 PLIC核心:仲裁与路由逻辑
PLIC核心是整个控制器的中枢,它接收来自所有网关的请求,并执行关键的仲裁与路由逻辑。其内部主要包含以下几个关键部分:
中断挂起(Interrupt Pending, IP)位:一个位数组,用于锁存(latch)从网关传入的中断请求。当某个中断源的IP位被置位时,表示该源有一个活跃的请求正在等待服务。 中断优先级(Interrupt Priority)寄存器:一个寄存器数组,每个中断源对应一个。软件可以通过写这些寄存器来为每个中断源分配一个数值化的优先级。 中断使能(Interrupt Enable, IE)矩阵:一个庞大的位矩阵,允许软件为每一个中断目标独立地使能或禁用每一个中断源。 优先级阈值(Priority Threshold)寄存器:每个中断目标都拥有一个独立的阈值寄存器。PLIC只有在检测到一个挂起且已使能的中断,并且其优先级严格大于目标当前的阈值时,才会向该目标发送中断通知。
2.4 中断目标:定义中断传递的Hart上下文
中断目标是中断通知的最终接收者。在RISC-V中,一个中断目标通常被定义为一个“Hart上下文”,即某个特定Hart上的特定特权模式(例如,Hart 0的机器模式,或Hart 1的监管者模式)。PLIC规范理论上最多可支持15872个独立的Hart上下文。
当PLIC决定向一个Hart上下文发送中断通知时,它会通过置位该Hart的mip(机器中断挂起)或sip(监管者中断挂起)CSR中的相应位来实现,具体来说是MEIP(机器外部中断挂起)或SEIP(监管者外部中断挂起)位。
一个至关重要的架构特性是,PLIC独立地对待每一个中断目标,其本身不提供中断抢占(preemption)或嵌套(nesting)的概念。这些复杂的行为必须由接收中断的处理器核心自身通过软件或硬件逻辑来处理。
PLIC的核心运行机制
PLIC的强大功能源于其优先级、阈值和使能机制的协同工作。这套机制构成了一个灵活的两级过滤系统,为操作系统设计者提供了巨大的便利。它成功地将设备的静态重要性(通过优先级设定)与运行核心的动态上下文(通过阈值调节)分离开来。
3.1 优先级系统:为中断源赋予重要性
每个中断源(ID 1-1023)都可以通过写入其专用的32位内存映射优先级寄存器来分配一个优先级。
优先级值为0是一个特殊值,表示“从不中断”,这等同于在源头禁用了该中断。 优先级1是最低的有效优先级。最高优先级级别由具体的PLIC实现来定义。 为了保证处理的确定性,当多个中断具有相同的优先级时,PLIC会采用中断ID作为决胜规则:ID号较小的中断拥有更高的有效优先级。
3.2 基于阈值的过滤:每个目标的动态中断屏蔽
每个中断目标(Hart上下文)都拥有自己的优先级阈值寄存器 。PLIC在向目标发送中断通知前,会进行一次关键的比较:只有当一个挂起中断的优先级严格大于该目标阈值寄存器中的值时,通知才会被发出。
将阈值设置为0,意味着允许所有优先级大于0的中断通过。 将阈值设置为实现所支持的最高优先级,则可以有效地屏蔽该目标的所有全局中断。
这种机制赋予了软件(如操作系统内核)强大的动态控制能力。例如,当一个核心进入一个不希望被中等优先级的网络中断打扰的临界区时,内核可以临时将其阈值提升到网络中断的优先级之上。这仅仅屏蔽了该核心的中断,而不会影响其他核心,也无需全局禁用中断。
3.3 中断使能矩阵:对每个目标的精细化控制
PLIC内部维护着一个庞大的中断使能(IE)位数组,该数组按目标进行组织。对于每一个目标,都有一组与之对应的使能位,每个中断源占用其中一位。只有当中断源针对某个目标的IE位被置位时,该中断才会被该目标所考虑。
这种矩阵式的控制结构支持非常灵活的路由策略。例如,一个调试用的UART中断可以只为Hart 0使能(用于控制台I/O),而对正在执行实时控制任务的Hart 1禁用。
3.4 交互与优先级:两阶段过滤流程
综合来看,PLIC决定是否向一个Hart发出中断信号是一个严谨的多阶段过滤过程。对于一个中断源N和一个中断目标T,必须同时满足以下所有条件,中断通知才会被发出:
中断N必须处于挂起状态(其IP位为1)。
中断N必须为目标T使能(其在IE矩阵中对应T的位为1)。
中断N的优先级必须大于0。
中断N的优先级必须严格大于目标T的当前阈值。
在所有同样满足以上四个条件的中断中,不存在任何其他中断M的优先级高于N。
完整的中断生命周期:从设备信号到软件完成
理解PLIC的完整工作流程需要贯穿硬件和软件两个领域。整个过程可以划分为四个主要阶段,每个阶段都有明确的责任实体。
4.1 阶段一:中断生成与PLIC裁决(硬件域)
信号生成:一个外部设备(如GPIO)因某个事件(如电平变化)而触发并拉高其中断线。 网关请求:与该设备连接的PLIC中断网关接收到信号,并向PLIC核心发送一个标准化的中断请求。 请求锁存:PLIC核心收到请求后,在其中断挂起(IP)位数组中将对应中断ID的位设置为1。 持续评估:PLIC的硬件逻辑持续地、并行地评估所有挂起的IP位。对于每一个中断目标,它都会检查该中断是否被使能,以及其优先级是否高于该目标的阈值。 发送通知:一旦某个挂起的中断通过了针对某个目标的所有检查,PLIC就会向该目标Hart发出外部中断通知,通常是置位Hart的mip.MEIP或sip.SEIP位。
4.2 阶段二:Hart通知与陷入机制(软硬件接口)
信号检测:目标Hart检测到其外部中断挂起位(如mip.MEIP)被置位,并且对应的中断使能位(mie.MEIE)也已置位。 陷入触发:在mstatus寄存器中的全局中断使能位(MIE)为1的前提下,Hart会停止当前指令的执行,进入一个“陷入”(trap)状态。 硬件自动操作:Hart的硬件会自动执行一系列上下文保存操作:将当前的程序计数器(PC)值保存到mepc寄存器;在mcause寄存器中设置一个值以表明陷入原因是外部中断(例如,机器模式外部中断的原因为11);保存当前的全局中断使能状态,然后禁用全局中断以防嵌套;最后,跳转到mtvec寄存器所指向的地址开始执行异常处理代码。
4.3 阶段三:关键的声明/完成协议(软件域)
声明(Claim):此时,软件中断处理程序开始执行。它的首要任务是向PLIC声明中断。这通过读取其Hart上下文专用的claim/complete寄存器来完成。PLIC硬件在响应这次读操作时,会返回当前待处理的、优先级最高的中断ID。这次读取操作是原子的,并且带有一个至关重要的副作用:PLIC会自动清除该中断ID对应的IP位。 服务(Service):软件通过上一步获得的ID,知道了中断源。它利用这个ID来调用相应的设备驱动程序中的中断服务例程(Interrupt Service Routine, ISR)。ISR执行具体的工作,例如从UART的FIFO中读取数据。一个常被忽略但至关重要的步骤是:ISR必须清除设备本身的中断状态。 完成(Complete):ISR执行完毕后,软件必须向PLIC发送完成信号。这通过将之前声明时获得的同一个中断ID写回到同一个claim/complete寄存器来完成。这个写操作通知了中断网关,该中断已被完全处理,允许网关在需要时转发来自同一源的新中断。
4.4 阶段四:上下文恢复与中断重使能(软硬件接口)
返回:主陷入处理程序执行一条特权返回指令(如mret)。 硬件自动恢复:Hart硬件自动将mepc中保存的地址恢复到PC,并恢复陷入前的全局中断使能状态,从而重新允许中断的发生,程序从被中断处继续执行 。
下表清晰地总结了在中断生命周期的每个环节中,各个实体的责任。
表1:中断生命周期责任矩阵

PLIC寄存器接口与内存映射
PLIC是一个内存映射设备,这意味着CPU通过标准的加载(load)和存储(store)指令来访问其内部寄存器。RISC-V规范定义了PLIC各个寄存器块的相对偏移量,但并未强制规定其基地址。基地址由具体的SoC平台定义,软件需要从设备树(Device Tree)等平台信息中获取。
下表整合了来自多个规范文档的信息,提供了一个全面的PLIC寄存器内存映射参考。
表2:PLIC综合寄存器内存映射表

注:Base指PLIC模块的内存映射基地址,C指上下文ID,N指中断源ID。
PLIC软件编程实践指南
PLIC的声明/完成协议为软件和硬件之间建立了一份严格且不可协商的契约。对同一个寄存器地址的读(声明)和写(完成)所附带的副作用是这份契约的核心,是整个外部中断系统正确运行的基础。任何对该协议的违反,例如声明后忘记完成,或是不声明而直接轮询设备,都会导致系统中断功能失常甚至崩溃。
6.1 系统初始化与PLIC配置序列
在系统启动阶段,通常由引导加载程序(bootloader)或操作系统内核对PLIC进行初始化。一个标准的配置序列如下:
全局禁用中断:通过清除mstatus.MIE位,确保在配置过程中不会有中断发生。 禁用所有源:遍历所有PLIC中断源的优先级寄存器,将其值全部设为0,以禁用所有中断源。 清除所有使能:清除所有上下文的所有中断使能位。 设置默认阈值:为所有目标的优先级阈值寄存器设置一个安全的默认值。通常设为0,表示允许所有已使能的中断;或设为最大值,表示暂时屏蔽所有中断。 配置特定中断:对于需要使用的每个设备中断: 在其中断源优先级寄存器中设置一个非零的优先级值(例如1到7)。 在中断使能矩阵中,为需要接收该中断的目标上下文设置相应的使能位。 设置陷入向量:配置mtvec寄存器,使其指向主陷入处理程序的入口地址。 全局使能中断:设置mstatus.MIE位,重新允许中断。
6.2 实现健壮的中断服务例程(ISR)
一个健壮的PLIC中断处理程序必须严格遵循“声明 -> 服务 -> 完成”的序列。以下是伪代码示例和关键点说明:

关键点:在设备ISR(如uart0_isr())内部,必须通过访问设备自身的寄存器来清除引起中断的条件。对于电平触发的中断,如果不这样做,设备会持续断言中断信号,导致PLIC在完成当前中断后立即再次触发同一个中断,形成“中断风暴” 。
6.3 高级考量:多核与中断处理循环
多核仲裁:PLIC的claim操作是原子的,这为多核环境提供了天然的硬件锁。即使一个中断被同时路由到多个Hart,PLIC也确保只有一个Hart能够成功声明它。当一个Hart读取claim寄存器并获得中断ID时,其他Hart在同一时刻读取只会得到0(表示无中断)。 处理多个挂起中断:为了提高效率,一个设计良好的中断处理程序应该在一个循环中处理中断。在完成一个中断后,应立即尝试再次声明。如果返回非零ID,则表示有另一个中断正在等待处理,应继续服务。此循环应持续进行,直到claim操作返回0为止。这避免了为处理背靠背发生的多个中断而反复进入和退出陷入处理程序的巨大开销。
结论:PLIC作为RISC-V可扩展性的基石
7.1 关键架构特性总结
PLIC的设计体现了RISC-V架构的核心理念。其关键特性包括:集中的硬件仲裁机制、基于优先级的硬件排序、通过使能矩阵实现的灵活路由,以及通过每个目标的独立阈值实现的动态屏蔽。这些特性共同构成了一个强大而高效的全局中断管理框架。
7.2 PLIC在使能复杂多外设系统中的作用
PLIC的架构使其能够从容应对从仅有少量外设的简单微控制器到拥有数十个中断源和多个核心、能够运行Linux等复杂操作系统的应用处理器的扩展需求。它通过硬件加速中断仲裁,为构建高性能、高响应性的RISC-V系统提供了坚实的基础。
7.3 未来方向与演进中的RISC-V中断格局
PLIC规范体现了RISC-V的一个核心哲学:定义一个最小化的、功能性的基线以确保互操作性,同时为实现者保留优化和差异化的空间。这是一个双刃剑。规范定义了寄存器的偏移量和行为,为通用软件驱动程序提供了基础 。然而,它将基地址、源和目标的最大数量、优先级级别数以及中断触发类型(电平/边沿)的配置等细节留给了实现者。
这种灵活性允许SoC设计者根据成本和需求量体裁衣,但同时也给软件带来了挑战。操作系统需要一种机制来发现其所运行硬件的具体参数。因此,设备树(DeviceTree)等平台描述机制在RISC-V生态中至关重要,它为软件提供了一种标准化的方式来了解硬件的具体实现。
同时,RISC-V中断生态系统也在不断演进。虽然PLIC是当前管理全局中断的标准,但新的架构如核心本地中断控制器(CLIC)正在为需要更低延迟和向量化中断支持的场景提供替代方案。这表明RISC-V社区在中断处理领域持续创新,以满足更多样化的应用需求。总而言之,PLIC的设计完美地展示了规范的简洁性/灵活性与软件的复杂性/可移植性之间的权衡,这也是整个RISC-V生态系统的一个显著特征