ARM 通用中断控制器GIC(Generic Interrupt Controller)
Zynq7000系列学习笔记
本文介绍分发器(Distributor)和CPU接口 (CPU Interface)的功能与协作,并提供C语言案例辅助理解。
总结来说,分发器设置中断使能和中断优先级,并将该中断信号发送给CPU,CPU设置可接受的最低优先级,进行中断处理和完成中断通知。
一、分发器和CPU接口的功能
分发器Distributor |
CPU 接口 (CPU Interface) |
|
基地址 |
|
|
功能 |
全局中断管理 |
CPU 核心的中断交互 |
管理对象 |
所有中断源 (SPI, PPI, SGI) |
连接到单个 CPU 核心的中断 |
具体使用 |
中断使能/禁用、优先级配置、路由到 CPU |
中断确认、优先级屏蔽、中断完成通知 |
可见性 |
全局唯一(CPU共享) |
每个CPU私有 |
二、工作原理与交互流程 (中断生命周期)
- 中断触发(外设 → 分发器)
- 外设中断源(如 GPIO / UART / Timer)触发中断信号。
- 分发器接收:
- 中断信号到达分发器(
0xF8F01000
) - 分发器检查该中断是否 已使能(
GICD_ISENABLERn
) - 分发器读取该中断的 优先级(
GICD_IPRIORITYRn
) - 分发器根据配置决定是否 转发 给目标 CPU
- 中断分发(分发器 → CPU 接口)
- 路由决策:
- 分发器检查目标 CPU 接口的状态(是否使能接收中断)
- 比较 中断优先级 与目标 CPU 当前的 运行优先级(通过 CPU 接口的 GICC_PMR 获取)
- 若中断优先级 > CPU 当前优先级,分发器将其挂起到目标 CPU 的待处理队列。
- 信号通知:
- 分发器通过硬件信号线通知目标 CPU 的 CPU 接口:有高优先级中断等待处理。
- CPU 响应 (CPU 接口 → CPU 核心)
- 中断通知:
- CPU 接口 (
0xF8F00100
)收到分发器通知。 - CPU 接口向 CPU 核心 发送 IRQ/FIQ 中断请求。
- CPU 核心响应:
- CPU 核心暂停当前程序,跳转到 中断向量表 入口。
- 执行中断服务程序 (ISR) 的 入口代码。
- 中断处理 (ISR 与 CPU 接口交互)
- 中断确认:
- ISR 通过 CPU 接口的 Interrupt Acknowledge Register(GICC_IAR)读取 中断号(INTID)
- 读取 GICC_IAR 的行为告知 GIC:“CPU 已开始处理此中断”
- GIC 自动将该中断状态从 Pending 改为 Active
- 服务中断:
- ISR 根据 INTID 执行具体的中断服务逻辑(如读取 UART 数据)
- 中断完成:
- ISR 结束时,向 CPU 接口的 End of Interrupt Register(GICC_EOIR)写入之前读到的 INTID。
- 写入 GICC_EOIR 告知 GIC:“此中断处理已完成”
- GIC 将该中断状态从 Active 改为 Inactive,并允许分发器再次发送此中断(若再次触发)
- 中断优先级与抢占
- 分发器 根据全局中断优先级决定哪些中断可发送给 CPU。
- CPU 接口 的 GICC_PMR 设置 CPU 可接受的最低优先级(低于此值的中断被屏蔽)
- 若高优先级中断到达,且当前中断优先级较低,CPU 接口可触发 中断抢占(暂停当前 ISR,转去处理更高优先级中断)。
三、软件编程详细案例
- 分发器初始化代码详解
- 全局使能分发器
*(volatile uint32_t *)(GIC_DIST_BASE + 0x000) = 0x1; // GICD_CTLR
- 作用:启用整个中断分发系统,是整个 GIC 系统的总开关
- 寄存器:
GICD_CTLR
(Distributor Control Register) - 地址计算:基地址
0xF8F01000
+偏移量0x000
- 位设置:
Bit 0 = 1
:使能中断转发到 CPU 接口Bit 1 = 0
:禁用安全扩展
- 配置中断优先级
*(volatile uint32_t *)(GIC_DIST_BASE + 0x400 + (84 * 4/4)) = 0xA0;
// GICD_IPRIORITYR[21]
- 作用:设置中断号84的优先级(优先级范围0~255,0最高)
- 寄存器:
GICD_IPRIORITYRn
(Interrupt Priority Registers) - 地址计算:
- 优先级寄存器组基偏移:0x400
- 中断号 84 的偏移:84 * 1(因为每个中断占 1 字节)
- 实际地址:
0xF8F01000 + 0x400 + 84 = 0xF8F01454
- Zynq-7000 中:0-31是私有中断(PPI)和软件中断(SGI);32-95是共享外设中断(SPI)
- 路由到 CPU0
*(volatile uint32_t *)(GIC_DIST_BASE + 0x800 + (84 * 4/4)) = 0x01;
// GICD_ITARGETSR[21]
- 作用:指定中断84发送到 CPU0
- 寄存器:
GICD_ITARGETSRn
(Interrupt Processor Targets Registers) - 地址计算:
0xF8F01000 + 0x800 + 84 = 0xF8F01854
- 位掩码:
0x01
表示 CPU0
Bit 0 = 1
:路由到 CPU0Bit 1 = 0
:不路由到 CPU1
- 注:在双核系统中,必须明确指定中断发送到哪个核心。
- 注:PPI/SGI 不能配置路由,只有 SPI(32+)可配置
- 使能中断号 84
*(volatile uint32_t *)(GIC_DIST_BASE + 0x100 + (84 / 32)*4) |= (1 << (84 % 32));
// GICD_ISENABLER
- 作用:启用特定中断源
- 寄存器:
GICD_ISENABLERn
(Interrupt Set-Enable Registers) - 地址计算:
- 使能寄存器组基偏移:
0x100
- 寄存器索引:
84 / 32 = 2
(第3个寄存器,管理中断64-95) - 位位置:
84 % 32 = 20
- 实际地址:
0xF8F01000 + 0x100 + 2*4 = 0xF8F01008
- CPU 接口初始化代码详解
- 设置优先级屏蔽阈值
*(volatile uint32_t *)(GIC_CPU_BASE + 0x0004) = 0xA0; // GICC_PMR
- 作用:定义 CPU 处理中断的最低优先级
- 寄存器:
GICC_PMR
(Priority Mask Register) - 地址:
0xF8F00100 + 0x0004 = 0xF8F00104
- 值说明:0xA0(160)表示:
- 优先级高于 160 的中断被屏蔽
- 优先级低于 160 的中断可被处理
- 防止低优先级中断打断高优先级任务
- 使能 CPU 接口
*(volatile uint32_t *)(GIC_CPU_BASE + 0x0000) = 0x1; // GICC_CTLR
- 作用:启用 CPU 与 GIC 的交互通道
- 寄存器:
GICC_CTLR
(CPU Interface Control Register) - 地址:
0xF8F00100 + 0x0000 = 0xF8F00100
- 位设置:
Bit 0 = 1
:使能中断信号到 CPUBit 1 = 0
:禁用 FIQ(通常用 IRQ)Bit 2 = 0
:禁用 ACK 特殊处理
- 注:不使能CPU接口,即使分发器发送中断,CPU 也不会响应
- 中断服务程序 (ISR)
- 读取中断号
uint32_t intid = *(volatile uint32_t *)(GIC_CPU_BASE + 0x000C); // GICC_IAR
- 作用:获取当前中断 ID 并确认接收
- 寄存器:
GICC_IAR
(Interrupt Acknowledge Register) - 地址:
0xF8F00100 + 0x000C = 0xF8F0010C
- 读取操作的意义:
- 获取中断号(低 10 位)
- 通知 GIC "CPU 已开始处理此中断"
- 状态转换:GIC 将中断状态从 Pending 改为 Active
- 注:必须读取此寄存器后才能安全处理中断
- 注:返回值可能包含其他信息(如 CPU 号),通常
intid & 0x3FF
提取 ID
- 执行中断处理
handle_irq(intid); // 用户定义的中断处理函数
- 应避免阻塞其他中断
- 不能调用可能阻塞的函数
- 清除外设中断标志
- 通知中断处理完成
*(volatile uint32_t *)(GIC_CPU_BASE + 0x0010) = intid; // GICC_EOIR
- 作用:告知 GIC 中断处理已完成
- 寄存器:
GICC_EOIR
(End of Interrupt Register) - 地址:
0xF8F00100 + 0x0010 = 0xF8F00110
- 注:必须写入从
GICC_IAR
读取的原始值 - 必须在中断处理完成后立即执行
- 此时中断状态从Active转换为Inactive,才能允许相同的中断再次触发