4. GIC(通用中断控制器)介绍:ARM GIC架构概述

通用中断控制器(Generic Interrupt Controller,GIC)是ARM架构中用于管理中断的核心硬件单元。在设备树中,GIC节点是中断控制器节点的典型代表,其属性配置直接决定了操作系统如何感知和处理外设中断。理解GIC的架构层次与变体差异,是正确编写中断控制器DTS节点的前提。

4.1 GIC架构层次

ARM GIC从v2到v4架构经历了多次演进,每一代都在中断数量、分发机制和虚拟化支持上有所增强。下表总结了主流GIC架构版本的核心差异:

GIC版本 典型实现 最大中断数 CPU接口 虚拟化支持 常见应用
GICv2 GIC-400 最多1020个SPI 内存映射寄存器 基本虚拟中断 Cortex-A7/A9/A15,嵌入式Linux
GICv3 GIC-500 最多1024个SPI + 16个PPI 系统寄存器(ICC_*) 硬件虚拟化(LPI) Cortex-A53/A72,服务器SoC
GICv4 GIC-600 扩展LPI空间 系统寄存器 直接注入虚拟中断(vLPI) 虚拟化密集场景,数据中心
设备树要点: GICv2使用内存映射寄存器基地址(如0x2c001000),而GICv3/v4的CPU接口通过系统寄存器访问,设备树中只需指定Distributor和Redistributor基地址,无需为CPU接口分配内存地址空间。

4.2 GIC-400:GICv2的典型代表

GIC-400是ARM为Cortex-A系列处理器配套的GICv2实现,广泛用于移动SoC、工业控制板和入门级服务器。其内部结构分为两个主要逻辑单元:

  • Distributor(分发器): 负责中断优先级管理、使能/屏蔽、触发方式配置(电平/边沿)以及中断目标CPU选择。
  • CPU Interface(CPU接口): 每个CPU核心拥有独立的接口,负责向核心发送中断信号、应答中断(EOI)以及获取中断ID。

GIC-400支持三种中断类型:

中断类型 ID范围 说明
SGI(软件生成中断) ID 0–15 用于核间通信,由软件写入寄存器触发
PPI(私有外设中断) ID 16–31 每个CPU私有,如本地定时器、PMU
SPI(共享外设中断) ID 32–1019 所有CPU共享,由外设触发
DTS配置示例(GIC-400):
gic: interrupt-controller@2c001000 {
    compatible = "arm,cortex-a15-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x2c001000 0x1000>,  /* Distributor基地址 */
          <0x2c002000 0x2000>;  /* CPU接口基地址 */
};

注意:#interrupt-cells = <3> 表示每个中断描述符占用3个cell,分别表示中断类型/ID、触发标志和优先级。

4.3 GIC-500:GICv3的演进

GIC-500是ARM面向64位服务器和高端嵌入式场景推出的GICv3实现。相比GIC-400,它在架构上做了重大调整:

  • Redistributor(再分发器)引入: 每个CPU核心拥有独立的Redistributor单元,负责管理PPI和SGI,减轻Distributor的负担。
  • 系统寄存器访问: CPU接口不再占用内存地址空间,而是通过ICC_*系统寄存器直接操作,降低了访问延迟。
  • LPI(Locality-specific Peripheral Interrupt)支持: 基于消息的中断机制,适合PCIe MSI/MSI-X场景,中断数量可扩展至几乎无限。

GIC-500的设备树节点需要指定Distributor和Redistributor的基地址,且Redistributor区域大小与CPU核心数相关:

DTS配置示例(GIC-500):
gic: interrupt-controller@2f000000 {
    compatible = "arm,gic-v3";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x2f000000 0x10000>,  /* Distributor基地址 */
          <0x2f100000 0x200000>; /* Redistributor基地址(覆盖所有CPU) */
    interrupts = <1 9 4>;        /* 维护中断(可选) */
};

GICv3的#interrupt-cells仍为3,但第三个cell的编码含义与GICv2不同,需参考ARM GICv3规范。

4.4 GIC-400与GIC-500关键对比

对比维度 GIC-400(GICv2) GIC-500(GICv3)
CPU接口访问方式 内存映射寄存器 系统寄存器(ICC_*)
中断路由 Distributor直接路由到CPU Distributor → Redistributor → CPU
LPI支持 不支持 支持(基于ITS)
虚拟化扩展 基本虚拟中断(GICv2m) 硬件虚拟化(vPE、vLPI)
设备树reg属性 2个区域(Distributor + CPU Interface) 2个区域(Distributor + Redistributor)
典型兼容字符串 arm,cortex-a15-gic arm,gic-v3
常见配置错误: 在GIC-500的设备树中,如果Redistributor区域大小不足以覆盖所有CPU核心,系统启动时GIC驱动将无法完成初始化。计算方式为:每个Redistributor帧大小(通常0x20000)× CPU核心数。例如4核系统至少需要0x80000字节。

4.5 GIC架构内部数据流

下图展示了GIC-400与GIC-500在中断从外设到CPU核心的传递路径上的结构差异:

GIC-400 vs GIC-500 中断传递路径对比 GIC-400(GICv2)与 GIC-500(GICv3)中断传递路径对比 GIC-400 架构 外设(SPI中断源) 中断请求 Distributor(分发器) 路由决策 CPU Interface(内存映射) IRQ/FIQ信号 CPU Core 0 GIC-500 架构 外设(SPI/LPI中断源) 中断请求 Distributor(分发器) 转发到目标Redistributor Redistributor(再分发器) 系统寄存器(ICC_*) CPU Core 0 关键差异 GIC-400:Distributor直接路由到CPU Interface(内存映射)→ CPU GIC-500:Distributor转发到Redistributor(每个CPU独立)→ 系统寄存器访问 → CPU

4.6 中断触发方式与编码

在设备树中,中断触发方式通过interrupts属性的第二个cell(对于GICv2/v3)或专用标志位描述。GIC-400和GIC-500均支持以下触发模式:

触发方式 编码值(GICv2) 编码值(GICv3) 说明
上升沿触发 0x01 0x01 边沿敏感,适合定时器、DMA完成
高电平触发 0x04 0x04 电平敏感,适合按键、外部唤醒
下降沿触发 0x02 0x02 较少使用,部分外设支持
低电平触发 0x08 0x08 电平敏感,低电平有效
DTS中断描述示例:
/* GIC-400:SPI中断,ID=42,高电平触发 */
interrupts = <0 42 4>;

/* GIC-500:PPI中断,ID=14,上升沿触发 */
interrupts = <1 14 1>;

第一个cell:0=SPI,1=PPI;第二个cell:中断ID;第三个cell:触发标志。

4.7 选择GIC变体的工程考量

在实际SoC设计中,选择GIC-400还是GIC-500取决于以下因素:

  • CPU架构: 32位Cortex-A系列通常搭配GIC-400;64位Cortex-A系列推荐GIC-500。
  • 中断密度: 需要超过1020个SPI或大量MSI中断时,GIC-500的LPI机制更具优势。
  • 虚拟化需求: 运行多个虚拟机(如KVM、Xen)时,GIC-500的硬件虚拟化支持能显著降低中断延迟。
  • 软件兼容性: 现有Linux内核版本对GIC-400支持更成熟,GIC-500需要较新内核(4.0+)和ATF固件配合。
设备树兼容性策略: 对于支持多种GIC变体的SoC,可以在DTS中同时列出兼容字符串,例如 compatible = "arm,gic-v3", "arm,cortex-a15-gic";,内核驱动会按优先级匹配。但实际硬件必须与第一个兼容字符串对应,否则可能导致初始化失败。