4. 中断类型详解:SPI、PPI、SGI、LPI
ARM GIC(Generic Interrupt Controller)将中断源划分为四种基本类型:SPI(共享外设中断)、PPI(私有外设中断)、SGI(软件生成中断)和LPI(本地特定中断)。理解这四种中断类型的区别、路由规则和触发特性,是进行中断控制器配置和性能优化的基础。
4.1 中断类型概览
下表从中断源、路由目标、触发方式、配置粒度和典型用途五个维度,对四种中断类型进行对比:
| 特性 | SPI | PPI | SGI | LPI |
|---|---|---|---|---|
| 中断源 | 片内外设(UART、I2C、DMA等) | CPU私有外设(Timer、PMU等) | 软件写GICD_SGIR寄存器 | ITS(中断翻译服务)映射的设备MSI |
| 路由目标 | 任意一个或一组指定的PE | 固定的单个PE | 目标PE列表(亲和路由) | 通过ITS重定向到指定PE |
| 触发方式 | 电平触发 / 边沿触发 | 电平触发 / 边沿触发 | 边沿触发(软件写寄存器) | 边沿触发(MSI消息) |
| 配置粒度 | 全局(GICD) | 每PE私有(GICR) | 每PE私有(GICR) | 全局(ITS + GICR) |
| 典型用途 | 多核共享外设中断 | 本地定时器、性能监控 | 核间通信(IPI)、自中断 | PCIe MSI/MSI-X、虚拟化设备 |
4.2 SPI(共享外设中断)
SPI 是嵌入式系统中最常见的中断类型,用于连接片内外设(如 UART、I2C、SPI、DMA 控制器等)。SPI 中断号范围在 GICv2 中为 ID32~ID1019,在 GICv3/v4 中扩展为 ID32~ID4095。
- 路由特性:SPI 可以路由到任意一个 PE 或一组 PE(通过亲和路由寄存器配置)。
- 触发配置:支持电平触发(Level-sensitive)和边沿触发(Edge-triggered),通过 GICD_ICFGRn 寄存器配置。
- 优先级与安全:每个 SPI 可以独立配置优先级(GICD_IPRIORITYRn)和安全属性(GICD_IGROUPRn)。
- 中断挂起:SPI 的中断挂起状态由 GICD_ISPENDRn 和 GICD_ICPENDRn 控制。
4.3 PPI(私有外设中断)
PPI 是每个 PE 私有的中断,每个 PE 拥有独立的 PPI 实例。PPI 中断号范围为 ID16~ID31。典型用途包括:
- CPU 本地定时器:每个 PE 的 Generic Timer 产生的物理定时器中断(PPI ID26~ID29)。
- 性能监控单元(PMU):每个 PE 的 PMU 溢出中断(PPI ID23)。
- 调试接口:CoreSight 调试相关中断。
PPI 的配置寄存器位于每个 PE 的 Redistributor(GICR)中,包括:
// PPI 配置寄存器(GICR 基地址 + 偏移)
GICR_ICFGR1 // 触发方式配置(PPI ID16~31)
GICR_IPRIORITYR // 优先级配置(每 PPI 8 bits)
GICR_IGROUPR0 // 安全组配置(PPI + SGI)
GICR_ISPENDR0 // 中断挂起置位
GICR_ICPENDR0 // 中断挂起清除
4.4 SGI(软件生成中断)
SGI 由软件通过写 GICD_SGIR 寄存器(GICv2)或 GICR_SGIR 寄存器(GICv3)触发。SGI 中断号范围为 ID0~ID15,共 16 个。SGI 是核间通信(IPI)的核心机制。
SGI 的关键特性:
- 边沿触发:SGI 只能边沿触发,由写寄存器操作产生。
- 亲和路由:可以指定目标 PE 列表(通过 CPU mask 字段),支持广播到所有 PE。
- 自中断:PE 可以向自己发送 SGI,用于软件定时器或任务调度。
- 中断优先级:SGI 的优先级在 GICR_IPRIORITYR 中配置,与 PPI 共享寄存器。
在 Linux 内核中,SGI 的典型用途包括:
// Linux 内核 SGI 分配示例(arch/arm64/kernel/smp.c)
#define IPI_RESCHEDULE 0 // 调度器重新调度
#define IPI_CALL_FUNC 1 // 调用函数
#define IPI_CPU_STOP 2 // 停止 CPU
#define IPI_IRQ_WORK 3 // 中断工作
#define IPI_WAKEUP 4 // 唤醒 CPU
4.5 LPI(本地特定中断)
LPI 是 GICv3/v4 引入的新中断类型,专为大规模中断场景设计。LPI 中断号范围为 ID8192~ID16777215(共约 1600 万个)。LPI 不通过传统的 GICD 寄存器配置,而是通过 ITS(Interrupt Translation Service)和 GICR 的 LPI 配置表管理。
LPI 的核心机制:
- ITS 映射:设备通过 ITS 发送 MSI 消息,ITS 将设备事件 ID 翻译为 LPI 中断号并路由到目标 PE。
- 配置表:LPI 的优先级、使能状态存储在内存中的配置表(GICR_PROPBASER 指向的 LPI 配置表),中断挂起状态存储在 GICR_PENDBASER 指向的挂起表。
- 边沿触发:LPI 只能边沿触发,由 MSI 消息产生。
- 无硬件中断挂起清除:LPI 的挂起状态由软件通过写 GICR_PENDBASER 表清除,没有专用的清除寄存器。
4.6 中断类型选择指南
在实际系统设计中,根据中断源的特性和性能需求选择合适的中断类型:
| 场景 | 推荐中断类型 | 理由 |
|---|---|---|
| 低速外设(UART、GPIO) | SPI | 配置简单,兼容性好,延迟要求不高 |
| 高速外设(DMA、网络控制器) | LPI(通过 ITS) | 避免全局寄存器竞争,支持多队列 |
| CPU 本地定时器 | PPI | 每 PE 私有,无需路由,延迟最低 |
| 核间通信(IPI) | SGI | 软件触发,延迟极低,支持亲和路由 |
| 虚拟化设备中断 | LPI(vLPI) | GICv4 支持直接注入,减少 VM exit |
4.7 中断类型与 GIC 版本的关系
不同 GIC 版本对中断类型的支持存在差异,下表总结了关键区别:
| 特性 | GICv2 | GICv3 | GICv4 |
|---|---|---|---|
| SPI 最大 ID | 1019 | 4095 | 4095 |
| LPI 支持 | 不支持 | 支持(需 ITS) | 支持(增强 vLPI) |
| SGI 路由 | 通过 GICD_SGIR | 通过 GICR_SGIR(每 PE) | 同 GICv3 |
| PPI 配置 | GICD 全局寄存器 | GICR 每 PE 寄存器 | 同 GICv3 |
| 虚拟中断 | 不支持 | 不支持 | 支持 vLPI 直接注入 |