
PLIC简介
RISC-V架构本身只定义了处理器如何响应中断,并没有规定具体SoC中外设中断如何连接到CPU。
例如,一个SoC中可能存在:
- UART
- GPIO
- SPI
- DMA
- Ethernet
- PCIe
这些外设都会产生中断请求,如果每个外设直接连接CPU,会导致硬件连接复杂。
因此,RISC-V定义了 PLIC(Platform-Level Interrupt Controller ,平台级中断控制器),用于管理平台外部中断。
PLIC主要完成:
- 汇聚多个Interrupt Source
- 保存中断Pending状态
- 根据Priority进行仲裁
- 根据Hart Context分发中断
- 通过Claim/Complete完成中断处理

PLIC在RISC-V中断体系中的位置
RISC-V中断主要分为两类:
- Local Interrupt:CPU本地中断
- External Interrupt:外部设备中断
PLIC负责的是External Interrupt。
外设产生中断后,首先进入PLIC。
PLIC完成仲裁后,将结果发送给CPU,CPU通过External Interrupt入口进入异常处理流程。

PLIC核心硬件模型
PLIC内部主要包含几个部分:
Interrupt Source -> Interrupt Gateway -> Pending Register -> Priority Comparator -> Enable Register -> Threshold -> Claim / Complete
3.1 Interrupt Source
每一个外设中断对应一个Source ID。
例如:
Source ID
1 UART
2 GPIO
3 DMA
4 SPI
PLIC通过Source ID区分不同外设。
Zephyr中对应:
local_irq
3.2 Priority Register
每个Interrupt Source拥有一个优先级。
例如:
DMA IRQ
priority = 7
UART IRQ
priority = 3
当多个中断同时Pending时,PLIC选择Priority最高的中断。

3.3 Pending Register
当外设产生中断后,PLIC记录Pending状态。
例如:
GPIO IRQ5 triggered
Pending[5] = 1
表示:
该中断已经发生,但是还没有被CPU处理。

3.4 Enable Register
PLIC支持不同Hart Context独立控制中断是否开启。
例如:
IRQ1 IRQ2 IRQ3
Context0 1 0 1
Context1 0 1 1
Context0接收IRQ1和IRQ3。
Context1接收IRQ2和IRQ3。

3.5 Hart Context
PLIC中一个重要概念是:
Interrupt Target不是简单的CPU,而是Hart Context。
一个Hart可能存在多个Context:
Hart0
|
+---- M-mode Context
|
+---- S-mode Context
Hart1
|
+---- M-mode Context
|
+---- S-mode Context
因此PLIC寄存器不是简单:
PLIC + IRQ
而是:
PLIC
|
+---- Context0
|
+---- Context1
|
+---- Context2
最后就是中断完成,completion

Zephyr中的PLIC驱动
Zephyr通过Interrupt Controller Driver实现PLIC支持。
驱动文件:
drivers/interrupt_controller/intc_plic.c
首先定义DeviceTree匹配:

表示:
DeviceTree中的:
compatible = "sifive,plic-1.0.0";
会匹配该驱动,比如下面的cpu处情况,这里的 cpu-intc 并不是 PLIC,而是 CPU 内部的中断入口(Hart Local Interrupt Controller,很多资料也简称 HLIC):
它负责接收 CPU 本地能够处理的所有中断,例如:
- Machine Software Interrupt(MSI)
- Machine Timer Interrupt(MTI)
- Machine External Interrupt(MEI)
也就是说,CPU-INTC 是 CPU 与外部中断控制器之间的连接点。

中断控制器处:
PLIC 最多支持 1024 个 Interrupt Source。
&hlic0 0x0b 表示:PLIC 可以向 CPU0 的 Local Interrupt Controller 发送 Interrupt ID = 11
11在 RISC-V Privileged Spec 中就是:Machine External Interrupt (MEI)
0x09 表示:Supervisor External Interrupt (SEI)
CLINT 与 PLIC 最大区别是:
- PLIC 管理 External Interrupt
- CLINT 管理 Software Interrupt 和 Timer Interrupt
它们都通过 interrupts-extended 连接到 CPU Local Interrupt Controller,但负责的中断类型完全不同。

PLIC寄存器模型与Zephyr映射
PLIC使用Memory-Mapped Register访问。
Zephyr根据PLIC Spec定义寄存器偏移:

Zephyr如何创建PLIC设备
Zephyr Device Model通过DeviceTree自动生成设备。
- plic_config
- plic_data
- device对象

PLIC初始化流程
入口:
static int plic_init(const struct device *dev)
初始化主要完成三件事:
关闭所有中断,避免系统启动过程中产生未知中断。清除Priority。设置Threshold。

初始化完成后:
所有IRQ disabled
Priority = 0
Threshold = 0
PLIC中断使能流程
上文最后也有调用 config->irq_config_func()

应用调用:


PLIC中断处理流程
这是PLIC最核心的部分。
硬件流程:
Peripheral -> PLIC Pending -> Priority Arbitration -> Hart External Interrupt -> CPU Trap
Zephyr流程:
plic_irq_handler() -> claim register read -> get IRQ number -> _sw_isr_table -> ISR callback -> complete interrupt

总结:从Spec到Zephyr源码
PLIC硬件概念和Zephyr实现对应关系:
|-------------------|-----------------------------|
| PLIC Spec | Zephyr |
| Interrupt Source | local_irq |
| Priority Register | config->prio |
| Enable Register | config->irq_en |
| Hart Context | hart_context |
| Threshold | get_threshold_priority_addr |
| Claim/Complete | claim_complete_addr |
| Interrupt Handler | plic_irq_handler |