ARM GIC 中断控制器基础:GICv2/v3 架构、中断类型与路由机制
在 ARM 生态系统中,通用中断控制器(Generic Interrupt Controller, GIC)是管理中断的核心硬件单元。无论是裸机程序还是 Linux 内核,中断的配置、分发与优先级仲裁都依赖 GIC。理解 GICv2 与 GICv3 的差异,以及 SPI、PPI、SGI 三种中断类型,是后续迁移到 RISC-V PLIC/CLINT 的基础。
学习提示:GICv2 是 ARM Cortex-A7/A9/A15 等处理器的标配;GICv3 从 ARMv8 开始引入,支持更多 PE(Processing Element)和更灵活的路由。RISC-V 的 PLIC 在概念上更接近 GICv2 的 SPI 管理,而 CLINT 则承担了 SGI 和 PPI 的部分功能。
2.1 GIC 架构概览:从 v2 到 v3 的演进
GIC 的核心职责是:接收来自外设或软件的中断请求,进行优先级仲裁,然后将中断分发给目标 CPU 接口。GICv2 和 GICv3 在逻辑上均分为两个主要组件:分发器(Distributor)和CPU 接口(CPU Interface)。
关键区别:GICv3 将 CPU 接口从内存映射寄存器改为系统寄存器(SysReg)访问,并引入 Redistributor 概念,使得每个 PE 拥有独立的配置空间,大幅提升了可扩展性。
2.2 中断类型:SPI、PPI、SGI
GIC 将中断源分为三种类型,每种类型在中断号分配、路由目标和触发方式上存在显著差异。理解这些差异是正确配置设备树中断属性的前提。
| 中断类型 | 全称 | 中断号范围 | 路由目标 | 典型用途 |
|---|---|---|---|---|
| SGI | Software Generated Interrupt | ID0 – ID15(GICv2) ID0 – ID15(GICv3) |
任意 PE 或指定 PE 集合 | 核间通信(IPI)、CPU 自唤醒 |
| PPI | Private Peripheral Interrupt | ID16 – ID31(GICv2) ID16 – ID31(GICv3) |
仅绑定到单个 PE | 本地定时器、PMU、调试单元 |
| SPI | Shared Peripheral Interrupt | ID32 – ID1019(GICv2) ID32 – ID1019(GICv3) |
任意 PE 或 PE 组 | 外设中断(UART、I2C、DMA、网络) |
迁移注意:RISC-V 的 PLIC 只管理类似 SPI 的外设中断,且中断号从 1 开始(0 表示无中断)。SGI 和 PPI 在 RISC-V 中由 CLINT 管理,使用不同的寄存器接口。设备树中
interrupts 属性的编码方式完全不同。
2.3 中断号分配与路由机制
中断号是 GIC 识别中断源的唯一标识。在 GICv2 中,中断号直接映射到硬件中断线;GICv3 则通过 Interrupt ID 和 vINTID 实现更灵活的映射。路由机制决定了中断最终被哪个 CPU 核处理。
2.3.1 中断号分配规则
- SGI(ID0-15):由软件通过
GICD_SGIR寄存器(GICv2)或ICC_SGI0R_EL1系统寄存器(GICv3)触发。中断号本身编码了目标 PE 列表。 - PPI(ID16-31):每个 PE 拥有独立的 PPI 实例。例如,CPU0 的 PPI 定时器中断和 CPU1 的 PPI 定时器中断使用相同的中断号,但物理上独立。
- SPI(ID32+):全局共享,所有 PE 看到的是同一个中断源。外设中断通常映射到 SPI 范围。
2.3.2 路由机制详解
GICv2 的路由通过 GICD_ITARGETSRn 寄存器实现,每个 SPI 中断对应一个 8 位寄存器,每位代表一个 CPU 接口(最多 8 个 PE)。GICv3 则使用 GICR 路由表,支持更精细的 PE 选择,包括通过 Affinity 层级(Aff0、Aff1、Aff2、Aff3)定位到具体的 PE。
设备树中的中断路由:在 ARM 设备树中,
interrupts 属性通常包含 <中断类型 中断号 触发方式> 三元组。例如 interrupts = <0 33 4> 表示 SPI(类型 0)、中断号 33、高电平触发(4)。GIC 的 interrupt-parent 属性指向 GIC 节点,由 GIC 驱动解析这些信息并配置路由。
2.4 GICv2 与 GICv3 关键差异总结
| 特性 | GICv2 | GICv3 |
|---|---|---|
| CPU 接口访问方式 | 内存映射寄存器(GICC_*) | 系统寄存器(ICC_*_ELn) |
| PE 数量支持 | 最多 8 个 PE | 最多 256 个 PE(通过 Affinity 扩展) |
| 中断路由粒度 | 位图路由(8 位) | 路由表 + Affinity 层级 |
| LPI 支持 | 不支持 | 支持(通过 ITS) |
| 虚拟化支持 | 基本虚拟化扩展(GICv2m) | 原生虚拟化(GICv3+) |
| 设备树兼容字符串 | arm,gic-400 |
arm,gic-v3 |
迁移到 RISC-V 的桥梁:RISC-V 的 PLIC 在设备树中通常使用
riscv,plic0 兼容字符串,中断号从 1 开始连续编号,没有 SPI/PPI/SGI 的区分。但理解 ARM GIC 的中断类型有助于在移植时正确映射:外设中断对应 PLIC 的中断源,核间中断对应 CLINT 的 MSIP(Machine Software Interrupt),定时器中断对应 CLINT 的 MTIMER。
2.5 实践:在设备树中描述 GIC 中断
以下是一个典型的 ARM 设备树片段,展示了如何描述 GIC 中断控制器以及外设如何引用中断:
// GICv2 中断控制器节点
gic: interrupt-controller@f1000000 {
compatible = "arm,gic-400";
reg = <0x0 0xf1000000 0x0 0x10000>, // GICD 基地址
<0x0 0xf2000000 0x0 0x20000>; // GICC 基地址
interrupt-controller;
#interrupt-cells = <3>; // 3 个 cell:中断类型、中断号、触发方式
};
// 外设节点:UART0 使用 SPI 中断
uart0: serial@f0000000 {
compatible = "arm,pl011";
reg = <0x0 0xf0000000 0x0 0x1000>;
interrupts = <0 33 4>; // 类型 0=SPI, 中断号 33, 触发方式 4=高电平
interrupt-parent = <&gic>;
};
在 GICv3 中,设备树描述略有不同,需要增加 redistributor 区域:
// GICv3 中断控制器节点
gic: interrupt-controller@f1000000 {
compatible = "arm,gic-v3";
reg = <0x0 0xf1000000 0x0 0x10000>, // GICD
<0x0 0xf3000000 0x0 0x100000>; // Redistributor 区域
interrupt-controller;
#interrupt-cells = <3>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
};
常见错误:在移植到 RISC-V 时,开发者常错误地将 ARM 的
#interrupt-cells = <3> 直接套用到 PLIC 节点。RISC-V PLIC 通常使用 #interrupt-cells = <1>,只传递中断号,触发方式由外设自身或 SoC 固定配置。务必检查目标平台的设备树绑定文档。
通过本章的学习,你应该能够:
- 区分 GICv2 和 GICv3 的架构差异及适用场景。
- 识别 SPI、PPI、SGI 三种中断类型及其在中断号分配上的规律。
- 理解 GIC 的路由机制,包括位图路由和 Affinity 路由。
- 在设备树中正确描述 GIC 中断控制器和外设中断引用。