3. 中断类型详解:SPI、PPI、SGI、LPI
在 ARM GIC(Generic Interrupt Controller)架构中,中断被划分为四种基本类型:SPI(共享外设中断)、PPI(私有外设中断)、SGI(软件生成中断)和 LPI(本地特定中断)。理解这四种中断类型的区别、路由机制和触发方式,是进行中断号映射与配置的前提。
3.1 四种中断类型概览
下表从中断源、路由目标、触发方式、中断号范围等维度,对四种中断类型进行对比:
| 中断类型 | 中断源 | 路由目标 | 触发方式 | 典型中断号范围 | 是否可配置 |
|---|---|---|---|---|---|
| SPI | 共享外设(如 UART、I2C、Ethernet) | 任意一个指定的 PE(处理单元) | 电平触发 / 边沿触发 | 32 ~ 1019(GICv3) | 是(路由、触发方式) |
| PPI | 私有外设(如每个 CPU 的本地定时器、PMU) | 固定的单个 PE | 电平触发 / 边沿触发 | 16 ~ 31 | 是(触发方式) |
| SGI | 软件(通过写 GIC 寄存器产生) | 一个或多个 PE(可指定 CPU 掩码) | 边沿触发(固定) | 0 ~ 15 | 是(目标 PE、中断 ID) |
| LPI | 支持 MSI(消息信号中断)的外设 | 任意一个指定的 PE | 电平触发(固定) | 8192 ~ 以上(GICv3) | 是(路由、优先级) |
关键概念:中断号(INTID)在 GICv3 中是一个 32 位整数,但实际使用范围被划分为上述四个区域。SPI 和 PPI 是传统板级外设中断的主要类型,SGI 用于核间通信(IPI),LPI 则是为高性能 PCIe 设备设计的可扩展中断机制。
3.2 SPI(共享外设中断)
SPI 是嵌入式系统中最常见的中断类型。它由片外或片内共享外设产生,可以被路由到任意一个指定的 CPU 核心。SPI 的中断号范围在 GICv3 中为 32 ~ 1019,每个 SPI 中断在 GIC 中都有一个独立的中断路由寄存器(GICD_IROUTERn),用于配置目标 PE。
- 触发方式:支持电平触发(高电平/低电平)和边沿触发(上升沿/下降沿),通过
GICD_ICFGRn寄存器配置。 - 路由特性:一个 SPI 中断在任意时刻只能被一个 PE 处理,但可以通过软件动态修改路由目标,实现负载均衡。
- 典型应用:UART 接收中断、I2C 传输完成中断、GPIO 外部中断等。
/* 设备树中 SPI 中断的典型描述 */
/* 中断控制器父节点为 GIC,第一个字段为中断类型(0=SPI),第二个字段为中断号偏移,第三个字段为触发方式 */
interrupts = <0 33 4>; /* SPI 中断号 33,高电平触发 */
3.3 PPI(私有外设中断)
PPI 是每个 PE 私有的中断,中断号范围为 16 ~ 31。每个 PE 都有自己独立的 PPI 中断实例,例如每个 CPU 核心的本地定时器中断(通常映射到 PPI 13 或 14)和性能监视单元(PMU)中断。
- 私有性:PPI 中断只能由它所属的 PE 处理,不能被路由到其他 PE。GIC 为每个 PE 维护独立的 PPI 配置寄存器。
- 触发方式:同样支持电平触发和边沿触发,通过
GICR_ICFGRn(Redistributor 中的寄存器)配置。 - 典型应用:CPU 本地定时器(Arch Timer)、PMU 溢出中断、调试中断。
驱动开发注意:在设备树中描述 PPI 中断时,中断类型字段应使用 1(代表 PPI)。例如:
interrupts = <1 13 4>; 表示 PPI 中断号 13,高电平触发。驱动中不需要指定目标 CPU,因为 PPI 自动绑定到当前运行的 PE。
3.4 SGI(软件生成中断)
SGI 是一种由软件主动触发的中断类型,中断号范围为 0 ~ 15。它通过写 GIC 的 GICD_SGIR(GICv2)或 ICC_SGI1R(GICv3)寄存器产生,主要用于核间通信(IPI)。
- 触发方式:固定为边沿触发,不可配置。
- 路由特性:可以发送给一个 PE、一组 PE(通过 CPU 掩码指定),或所有 PE(广播)。
- 典型应用:Linux 内核中的
smp_call_function()、TLB 刷新、任务迁移等 IPI 场景。
/* GICv3 中触发 SGI 的示例(汇编伪代码) */
/* 向 CPU 核心 1 和 2 发送 SGI 中断号 3 */
uint64_t sgi_value = (3 << 24) | /* SGI ID */
(0x6 << 16) | /* 目标 CPU 掩码:bit1 和 bit2 置位 */
(0 << 0); /* 目标列表,0 表示使用掩码 */
asm volatile("msr ICC_SGI1R_EL1, %0" : : "r" (sgi_value));
3.5 LPI(本地特定中断)
LPI 是 GICv3 引入的新型中断类型,专为支持大量中断源的场景设计(如 PCIe MSI/MSI-X)。LPI 的中断号从 8192 开始,理论上可以扩展到非常大的范围(GICv3 支持最多 2^32 个 LPI)。
- 触发方式:固定为电平触发,不可配置。
- 路由特性:通过 ITS(中断翻译服务)和 Redistributor 中的 LPI 配置表进行路由,支持动态映射和优先级配置。
- 配置方式:LPI 的配置不通过传统的 MMIO 寄存器,而是通过内存中的配置表(LPI Configuration Table 和 LPI Pending Table)进行管理,由 GIC 的 Redistributor 硬件自动读取。
- 典型应用:NVMe SSD、高性能网卡、GPU 等 PCIe 设备的中断。
LPI 配置注意事项:LPI 的使能和优先级配置是通过写内存表实现的,而不是直接操作 GIC 寄存器。驱动开发中,通常由 PCIe 子系统或 ITS 驱动负责管理 LPI 的分配和映射。LPI 中断号在设备树中通常不直接描述,而是通过 PCIe MSI 控制器动态分配。
3.6 四种中断类型的架构关系
下面的 SVG 图展示了 GICv3 中四种中断类型与中断控制器组件(Distributor、Redistributor、ITS)之间的路由关系:
3.7 中断类型选择与驱动开发建议
在实际的驱动开发中,选择哪种中断类型取决于硬件设计和性能需求:
| 场景 | 推荐中断类型 | 原因 |
|---|---|---|
| 板级外设(UART、I2C、SPI) | SPI | 中断号固定,路由灵活,支持多种触发方式 |
| CPU 本地功能(定时器、PMU) | PPI | 每个 CPU 独立实例,无需路由配置,延迟最低 |
| 核间通信(IPI) | SGI | 软件触发快速,支持广播和掩码,延迟可控 |
| PCIe 设备(NVMe、网卡) | LPI | 支持大量中断号,动态分配,无需硬件连线 |
| 需要负载均衡的外设 | SPI 或 LPI | 支持动态路由到不同 CPU,适合多核系统 |
实战要点:在编写设备驱动时,通过设备树或 ACPI 获取的中断号已经是经过 GIC 映射后的硬件中断号。驱动开发者不需要关心中断类型的具体路由细节,但需要正确配置触发方式(尤其是 SPI 和 PPI),并在中断处理函数中考虑中断亲和性(affinity)设置,以优化多核性能。