ARM中断处理

ARM Cortex-A 系列如何从外设中断信号到处理例程完成一次完整的中断响应?本文从 FIQ/IRQ 硬件信号线出发,逐级深入精简中断处理、嵌套中断抢占、GIC 架构及其配置与处理流水线。

1. 中断系统概述

在 ARMv7-A 发布之前,ARM 架构对外部中断控制器的设计给予了实现者相当大的自由度——不同厂商的系统在中断数量、中断类型以及软件接口模型上缺乏一致性。这导致中断处理代码难以在不同平台间移植。

ARM 通用中断控制器 v2(GICv2)架构的引入改变这一局面。GIC 提供了严格规范的中断管理模型,统一了 Distributor(分发器)和 CPU Interface(CPU 接口)两层架构,使中断处理代码具备跨平台可移植性。

ARM 术语中,”interrupt” 特指 IRQ 和 FIQ 两类异步外部异常——它们由硬件外设产生,经中断控制器汇聚后送入核心,与同步异常(SVC、Data Abort 等)在触发机制、优先级和屏蔽策略上均有本质区别。

NVIC 是 Cortex-M 内核自带的简单中断控制器;GIC 是面向复杂、多核系统的独立中断控制器。对比如下:

对比项 NVIC GIC
使用平台 Cortex-M Cortex-A / Cortex-R
是否集成在CPU内核 否(独立 IP)
是否支持多核 否(主要单核)
中断来源 外设直接连 NVIC 外设 → GIC → CPU
中断优先级 支持 支持,更复杂
中断嵌套 支持 支持
中断路由 不支持 支持(可路由到指定 CPU)
中断类型 外部中断、系统异常 SGI、PPI、SPI、LPI(v3)

2. 外部中断请求信号

ARM 核心通过两条硬件信号线接收来自中断控制器的异步通知——FIQ 用于高优先级快速响应,IRQ 用于标准中断处理。本节从信号电气特性出发,分析 CPSR 如何控制中断屏蔽以及 Non-Maskable FIQ 的硬件配置机制。

2.1 FIQ 与 IRQ

所有 ARM 核心均具备两个外部中断请求输入:

  • IRQ(Interrupt Request):标准中断请求输入
  • FIQ(Fast Interrupt Request):快速中断请求输入,优先级高于 IRQ

两者均为电平敏感、低电平有效(level-sensitive, active-LOW)信号。SoC 实现中使用中断控制器接收来自各类外设的中断源,将其映射到 FIQ 或 IRQ,最终导致核心触发异常。

2.2 CPSR 中断屏蔽位

中断异常仅在以下条件同时满足时才能被核心响应:

  • CPSR 中对应的屏蔽位(F 位对应 FIQ,I 位对应 IRQ)为 0
  • 相应的硬件输入信号被断言

CPSR 中断屏蔽位布局:

1
2
3
CPSR bit 7 (I) = 1 → IRQ masked
CPSR bit 6 (F) = 1 → FIQ masked
CPSR bit 8 (A) = 1 → Imprecise asynchronous abort masked 屏蔽非精确的外部异步Abort,对同步Abort没有影响

2.3 CPS 指令控制

CPS(Change Processor State)指令提供了启用或禁用异常的简洁机制:

1
2
CPSIE   aif          @ Enable exceptions (A=abort, I=IRQ, F=FIQ)
CPSID aif @ Disable exceptions

CPSIE i 仅启用 IRQ,不影响 A 和 F 对应的异常;CPSID if 同时禁用 IRQ 和 FIQ。未在指令中出现的字母对应的异常状态保持不变。

2.4 Non-Maskable FIQ

Cortex-A 系列处理器支持将 FIQ 配置为不可屏蔽中断(Non-Maskable FIQ)。该功能由 SoC 的硬件配置引脚决定,CPU 仅在复位时读取一次该配置,软件运行过程中无法修改。启用后,FIQ 不再受 CPSR.F 位控制;但进入 FIQ Handler 时,CPU 仍会自动将 F 位置 1,以防止 FIQ 嵌套。


3. 精简中断处理

精简中断处理(simplistic interrupt handler)是最基本的不可重入实现。核心特征是:进入中断处理后,同类型中断被自动屏蔽,直到显式重新启用。在处理完成当前中断前,不会有更高优先级的中断被响应。

这种模型不适合复杂嵌入式系统,但它是理解更实际的中断处理实现的基础。

3.1 硬件自动执行步骤

1

当 IRQ 异常被外部硬件触发时,核心自动完成以下动作:

  1. 当前执行模式的 PC 值保存到 LR_IRQ
  2. 当前 CPSR 复制到 SPSR_IRQ
  3. CPSR 模式位更新为 IRQ 模式(0x12),I 位置 1 屏蔽后续 IRQ
  4. PC 设置为向量表中 IRQ 入口地址(0x000000180xFFFF0018

这些步骤纯由硬件完成,无需任何软件干预。

3.2 软件处理流程

进入中断处理例程后,软件侧执行以下步骤:

步骤一:保存上下文。 将处理例程会破坏的寄存器压入 IRQ 模式栈。调用 identify_and_clear_source 确认中断源并在硬件层清除中断标志(如果不清除,返回后将立即再次触发同一个中断)。

步骤二:调用设备处理函数。 根据识别到的中断源调用对应的 C 语言设备驱动处理例程。

步骤三:恢复上下文并返回。 从栈中弹出寄存器,使用 SUBS PC, LR, #4 完成异常返回——S 后缀将 SPSR_IRQ 原子地恢复到 CPSR,同时 PC ← LR - 4,核心回到中断前的模式和指令流。

2

3.3 汇编代码分析

精简中断处理器的完整汇编实现:

1
2
3
4
5
6
IRQ_Handler:
PUSH {r0-r3, r12, lr} @ 保存 AAPCS 调用约定寄存器和 LR
BL identify_and_clear_source
BL C_irq_handler
POP {r0-r3, r12, lr} @ 恢复寄存器
SUBS pc, lr, #4 @ 异常返回,原子恢复 CPSR

逐行说明:

  • **PUSH {r0-r3, r12, lr}**:AAPCS(ARM 体系结构过程调用标准)规定 r0-r3 和 r12 是调用者保存寄存器,可能在 C_irq_handler 中被修改,必须提前保存。lr 保存的是 LR_IRQ 中的返回地址,也需要入栈保护。
  • **BL identify_and_clear_source**:跳转至硬件相关的中断源识别与清除函数。此步骤在调用 C 处理函数之前完成,末尾的分号不是语法需要,而是分隔同一行的两条 BL 指令。
  • **BL C_irq_handler**:跳转至 C 语言实现的中断处理函数,该函数根据中断 ID 分发到具体设备驱动。
  • **POP {r0-r3, r12, lr}**:恢复之前保存的寄存器。注意 lr 此时加载的是返回地址,但该值将在下一步被使用而非直接用于返回。
  • **SUBS pc, lr, #4**:核心返回指令。ARM 三级流水线中,IRQ 异常发生时 LR_IRQ = PC + 4,而实际应返回的是被中断指令的下一条指令,即 PC = LR_IRQ - 4S 后缀触发 SPSR_IRQ → CPSR 的原子复制,恢复中断前的处理器模式和中断屏蔽状态。

3.4 上下文切换图示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
进入 IRQ 前的用户程序上下文:
┌──────────┐
│ PC (App) │──→ 中断发生时 PC 值
│ CPSR │──→ 当前模式和中断使能状态
│ r0-r3 │──→ AAPCS 临时寄存器
│ r12 │──→ 帧内调用暂存寄存器
│ lr │──→ 应用程序返回地址
└──────────┘

▼ 硬件自动完成
┌──────────────────┐
│ LR_IRQ ← PC │
│ SPSR_IRQ ← CPSR │
│ CPSR[4:0] ← 0x12 │
│ CPSR[I] ← 1 │
└──────────────────┘

▼ PUSH {r0-r3, r12, lr}
┌──────────────┐
│ [IRQ Stack] │
│ r0, r1, r2 │──→ Caller-saved regs
│ r3, r12, lr │──→ LR_IRQ (return addr)
└──────────────┘

▼ Interrupt processing...

▼ POP + SUBS PC, LR, #4
┌──────────────┐
│ Restore Ctx │
│ CPSR←SPSR_IRQ│
│ PC←LR_IRQ-4 │
└──────────────┘

精简中断处理简单可靠,但致命缺陷是中断延迟不可控——低优先级中断会阻塞所有后续请求。这引出了下一节讨论的嵌套中断机制。


4. 嵌套中断处理

嵌套中断处理允许软件在当前中断处理完成之前接受另一个中断,以此实现中断优先级抢占,显著改善高优先级事件的响应延迟。

嵌套中断是软件选择的结果——通过中断优先级配置和中断控制来实现,而非硬件强制行为。

4.1 核心问题:LR 与 SPSR 覆盖

3

可重入中断处理面临一个根本性问题:

当核心正在 IRQ 模式执行第一个中断的处理例程时,如果此时 IRQ 被重新启用且第二个中断到来,硬件会将**第二个中断的返回地址写入 LR_IRQ将当前 CPSR 再次复制到 SPSR_IRQ**——这会覆盖第一个中断保存的返回地址和状态。

具体示例如下:假设主程序在地址 0x8000_3000 处被低优先级 IRQ #1 打断,硬件将 LR_IRQ = 0x8000_3004SPSR_IRQ = CPSR(User 模式)。IRQ #1 的处理例程在切换到 System 模式后调用 CPSIE i 重新启用中断。此时若高优先级 IRQ #2 到来,硬件再次进入 IRQ 模式并执行:LR_IRQ ← 0x8000_8004(IRQ #2 的返回地址),SPSR_IRQ ← CPSR(System 模式,I=0)。IRQ #1 之前保存在 LR_IRQ 中的 0x8000_30040x8000_8004 覆盖。当 IRQ #1 最终尝试返回到主程序时,LR_IRQ 中的值已变为 0x8000_8004,导致程序跳转到错误地址。

这就是为何必须使用 SRS 在启用中断前将 LR_IRQSPSR_IRQ 转存到备用模式栈上——硬件只在 IRQ 模式下写入 banked 寄存器,提前转存到其他模式的栈可保证原始上下文不被覆盖。

4.2 模式切换方案

解决策略是:在重新启用中断之前,将处理上下文切换到备用处理器模式,使新中断的硬件保存行为操作的是备用模式的 banked 寄存器,而非原始 IRQ 模式的 LR_IRQ / SPSR_IRQ

ARMv7-A 提供了两条专用指令来支持这一操作:

  • **SRS**(Store Return State):将 LRSPSR 原子地保存到指定模式的栈上,无需手动加载 SPSR 到通用寄存器
  • **RFE**(Return From Exception):从栈中原子地恢复 PCCPSR

SRSRFE 配对使用,避免了手动处理 SPSRPC 时可能出现的竞态窗口。

4.3 完整处理流程

可重入中断处理例程的完整步骤:

  1. 保存被中断程序的上下文(寄存器、返回地址、SPSR_IRQ)到备用内核模式栈
  2. 识别中断源,在硬件层清除中断标志(防止返回后立即再次触发)
  3. 切换到 SVC 模式,保持 CPSR I 位置 1(中断仍被屏蔽)
  4. 将异常返回地址保存到新模式的栈上,然后重新启用中断
  5. 调用相应的设备处理代码
  6. 处理完成后,禁用 IRQ 并从栈中弹出异常返回地址
  7. 从备用内核模式栈直接恢复被中断程序的上下文,包括通过 RFE 原子恢复 PC 和 CPSR

4.4 汇编代码分析

以下是完整的嵌套中断处理汇编实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IRQ_Handler:
SUB lr, lr, #4
SRSFD sp!, #0x1f @ 将 LR_irq 和 SPSR_irq 原子保存到 System 模式栈
CPS #0x1f @ 切换到 System 模式
PUSH {r0-r3, r12} @ 保存剩余 AAPCS 寄存器
AND r1, sp, #4 @ 检查栈对齐
SUB sp, sp, r1 @ 若未对齐则调整
PUSH {r1, lr} @ 保存对齐调整值和 LR_sys
BL identify_and_clear_source
CPSIE i @ 启用 IRQ
BL C_irq_handler
CPSID i @ 禁用 IRQ
POP {r1, lr} @ 恢复 LR_sys
ADD sp, sp, r1 @ 撤销栈对齐调整
POP {r0-r3, r12} @ 恢复 AAPCS 寄存器
RFEFD sp! @ 从 System 模式栈原子返回

逐行说明:

  • **SUB lr, lr, #4**:与精简处理相同,将 LR_IRQ 调整为实际返回地址(PC = LR_IRQ - 4)。
  • **SRSFD sp!, #0x1f**:将 LR_IRQSPSR_IRQ 原子地保存到 System 模式(0x1F)的栈上 sp_sysFD 后缀表示 Full Descending 栈操作。此操作在 IRQ 模式执行,数据却写入 System 模式栈——这是 SRS 的核心价值:在仍处于 IRQ 模式时就将关键状态转存到安全位置。
  • CPS #0x1f:切换到 System 模式(CPSR[4:0] = 0x1F)。System 模式与 User 模式共享寄存器组,但拥有特权级访问权限,且不自动屏蔽中断——这是选它而非 SVC 模式的原因。
  • **PUSH {r0-r3, r12}**:现在操作的是 System 模式栈,保存调用者寄存器。
  • **AND r1, sp, #4 / SUB sp, sp, r1**:AAPCS 要求栈在函数调用时 8 字节对齐。这两条指令检测当前栈是否 4 字节对齐(若 SP bit[2]=1 则未 8 字节对齐),并通过调整 SP 来纠正。
  • **PUSH {r1, lr}**:保存对齐调整值 r1LR_sys(System 模式下的链接寄存器,用于从 C_irq_handler 返回)。
  • **BL identify_and_clear_source**:清除当前中断源。
  • **CPSIE i**:在调用 C 处理函数之前启用 IRQ——这是嵌套能力的关键:此时核心已不在 IRQ 模式,新中断到来时硬件不会覆盖 LR_IRQ/SPSR_IRQ
  • **BL C_irq_handler**:在 IRQ 启用状态下调用设备处理函数。若此时更高优先级中断到来,核心将重新进入 IRQ 模式,开始新一轮嵌套处理。
  • **CPSID i**:处理完毕,禁用 IRQ,进入临界区准备恢复。
  • **POP {r1, lr}**:恢复对齐调整值和 LR_sys
  • **ADD sp, sp, r1**:撤销栈对齐调整。
  • **POP {r0-r3, r12}**:恢复 AAPCS 寄存器。
  • **RFEFD sp!**:从 System 模式栈原子弹出 PCCPSRRFE 指令自动将 SPSR 值写回 CPSR,实现原子模式切换和中断返回。

4.5 嵌套路径示意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Main Program

├── IRQ #1 (low prio) ─────────┐
│ │ IRQ_Handler
│ │ ├── SRS (save to System stack)
│ │ ├── CPS switch to System mode
│ │ ├── CPSIE i (re-enable IRQ)
│ │ │
│ │ ├── IRQ #2 (high prio) ──┐
│ │ │ │ Re-enter IRQ_Handler
│ │ │ │ Handle #2 → RFE return
│ │ │ ◄────────────────────┘
│ │ │
│ │ ├── CPSID i (disable IRQ)
│ │ └── RFE return
│ ◄──────────────────────────┘

Continue Main

上图描述了嵌套中断的完整抢占路径:主程序在执行中被低优先级 IRQ #1 打断,进入 IRQ_Handler 后执行 SRS 将 IRQ 模式下的 LR 和 SPSR 保存到 System 模式栈、CPS 切换到 System 模式并启用 IRQ。此时更高优先级的 IRQ #2 到来——核心重新进入 IRQ 模式,通过相同的 IRQ_Handler 入口开始新一轮处理。IRQ #2 完成处理后通过 RFE 返回到 System 模式下 IRQ #1 的处理上下文,IRQ #1 接着完成自身处理并通过 RFE 返回主程序。

嵌套中断处理实现了优先级抢占,但其复杂性显著增加——每个中断源需要独立的优先级配置,栈使用量随嵌套深度增长。这些管理任务正是 GIC 统一中断控制器要解决的核心问题。

NOTE:为了解决中断嵌套 LR 与 SPSR 会被覆盖的问题 ,异常处理程序会先保存当前异常模式的 LR 和 SPSR 到 System 模式栈,再切换到 System 模式执行,从而避免后续同类异常覆盖异常模式的 Banked LR 和 SPSR。


5. GIC 架构

ARM 通用中断控制器(GIC)的架构定义了一套标准化的硬件资源,用于在单核或多核系统中管理中断。GIC 提供了一组内存映射寄存器,软件通过读写这些寄存器来管理中断源和中断行为,并在多核系统中将中断路由到特定核心。

5.1 拓扑结构

从软件视角看,GIC 包含两个主要功能模块:

  • Distributor(分发器):系统中所有中断源的汇集点。Distributor 包含寄存器用于控制每个中断的属性——优先级、触发类型、目标核心列表、使能状态和安全配置。Distributor 负责决定哪个待处理中断应被转发到哪个核心。
  • CPU Interface(CPU 接口):每个核心独享一个 CPU Interface,用于接收 Distributor 转发的中断并将其信号传递给核心。CPU Interface 包含屏蔽、识别和控制中断状态的寄存器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
      ┌─────────────────────────────────────┐
│ Distributor │
│ ┌────┬────┬────┬────┬────┬────┐ │
│ │SPI0│SPI1│SPI2│... │ │SPIn│ │
│ └────┴────┴────┴────┴────┴────┘ │
│ Priority / Routing / State Mgmt │
└───┬────────────┬────────────────────┘
│ │
┌─────────▼──┐ ┌──────▼─────────┐
│ CPU I/F #0 │ │ CPU I/F #1 │ ...
│ Prio Mask │ │ Prio Mask │
│ Int Ack │ │ Int Ack │
│ End of Int │ │ End of Int │
└─────┬──────┘ └──────┬─────────┘
│ │
┌─────▼──────┐ ┌──────▼─────────┐
│ Core #0 │ │ Core #1 │
│ IRQ/FIQ │ │ IRQ/FIQ │
└────────────┘ └────────────────┘

5.2 内存映射与 Banked 访问

GIC 作为内存映射外设被访问。所有核心可以访问公共的 Distributor 模块,但 CPU Interface 采用 banked 设计——每个核心使用相同的地址访问自己私有的 CPU Interface。一个核心无法访问另一个核心的 CPU Interface 寄存器

这意味着在多核系统中,CPU Interface 的配置代码无需区分核心编号——硬件根据当前执行的核心自动选择对应的 banked 寄存器组。

5.3 中断 ID 编号

GIC 使用中断 ID(Interrupt ID)在软件中唯一标识每个中断源。中断 ID 的值由系统设计决定,软件通过中断 ID 来识别中断源并调用对应的处理例程。


6. 中断分类与状态机

GIC 管理的中断并非单一类型——根据产生方式(硬件外设 vs. 软件触发)和归属范围(核心私有 vs. 系统共享)分为三个类别,各自占用独立的 ID 区间。同时,每个中断在 GIC 内部遵循严格的状态转换规则,Distributor 基于这些状态来决定中断的转发时机。

6.1 中断类型

GIC 将中断按来源和范围分为三类:

类型 ID 范围 数量 用途
SGI(Software Generated Interrupt) 0–15 16 软件显式触发,用于核间通信
PPI(Private Peripheral Interrupt) 16–31 16 每个核心私有的外设中断,如 per-core timer
SPI(Shared Peripheral Interrupt) 32–1020 988 系统级共享外设中断,可路由到多个核心

SGI 由软件向 Distributor 的软件生成中断寄存器(ICDSGIR)写入目标核心掩码来生成。这是多核系统中实现核间通信(IPI, Inter-Processor Interrupt)的标准机制。SGI 可定向到全部核心或指定核心子集。

PPI 是每个核心私有的中断源,例如 per-core 定时器。核心 A 的 PPI 与核心 B 的同类型 PPI 相互独立,互不影响。

SPI 是系统中绝大多数外设中断的来源——UART、SPI 控制器、DMA 引擎、以太网 MAC 等均通过 SPI 接入 GIC。

6.2 触发方式

GIC 支持两种中断触发方式,由 Distributor 中的配置寄存器 GICD_ICFGRn(Interrupt Configuration Register)按中断 ID 逐位控制——每个中断占 2 位配置域:

  • 边沿触发(edge-triggered):GICD_ICFGRn[2*ID+0] = 1 表示该中断为边沿触发。中断控制器检测到相关输入上的上升沿时认为中断被断言,保持断言状态直到软件清除
  • 电平敏感(level-sensitive):GICD_ICFGRn[2*ID+0] = 0 表示电平敏感。仅当中断控制器的相关输入为高电平时才认为中断被断言。这是复位后的默认配置

6.3 中断状态机

每个中断在 GIC 内部经历一个四态生命周期:

4

各状态含义:

  • Inactive:中断源未被断言,处于空闲状态
  • Pending:中断源已被断言,等待核心处理。Pending 中断是 Distributor 决定转发到 CPU Interface 的候选者
  • Active:中断已被核心确认(通过读 CPU Interface 的 Interrupt Acknowledge Register),正在被服务
  • Active and Pending:核心正在服务该中断,同时同一中断源又产生了新的中断请求——上图中 Active 写 EOIR 直接回到 Inactive(无新中断),而 Active & Pending 写 EOIR 后退回 Pending(需二次处理)

Distributor 在每个中断源的状态寄存器中维护这些状态信息。

6.4 优先级仲裁

Distributor 持续监控所有 Pending 状态的中断,从中选出最高优先级且可交付的中断,将其转发到对应核心的 CPU Interface。在 CPU Interface 端,中断被进一步通过优先级掩码过滤——优先级低于掩码阈值的中断被阻止到达核心。

最终,CPU Interface 向核心发出 IRQ 或 FIQ 信号,核心响应异常并进入中断处理流程。


7. GIC 配置与处理流程

前两节介绍了 GIC 的架构组件和中断类型,本节将这些组件串联为完整的工作流——从系统启动后的 GIC 初始化开始,经过中断产生、仲裁、转发、确认到处理完成的端到端链路。理解这条流水线是编写 GIC 驱动代码的前提。

7.1 三级使能链

一个中断要最终到达核心并被处理,必须满足以下全部四级条件

1
2
[中断源] ──► [Distributor] ──► [CPU Interface] ──► [Core CPSR]
个体使能 模块使能 模块使能 I/F 位清除
  1. 个体中断使能:Distributor 中该中断的 enable bit 被置位
  2. Distributor 使能:Distributor 控制寄存器的全局 enable 位被置位
  3. CPU Interface 使能:对应核心的 CPU Interface 控制寄存器的 enable 位被置位
  4. CPSR 中断屏蔽位清除:核心 CPSR 中的 I 位(IRQ)或 F 位(FIQ)为 0

任何一级未使能,中断都无法到达核心。反过来,可以分别通过禁用 Distributor(全局禁用所有中断)、禁用某个 CPU Interface(禁用到达特定核心的中断)、或设置 CPSR 屏蔽位(仅当前核心临时屏蔽中断)来实现不同粒度的中断关闭。

7.2 Distributor 配置

复位后 Distributor 处于禁用状态。软件必须逐个中断源配置以下属性:

  • 优先级(Priority):通过 GICD_IPRIORITYRn 寄存器配置,每个中断 8 位优先级域,Distributor 使用此值在多个 Pending 中断中进行仲裁——值越小优先级越高
  • 触发类型(Configuration):通过 GICD_ICFGRn 寄存器配置,每中断 2 位——0b00 电平敏感,0b11 边沿触发
  • 目标核心(Target):通过 GICD_ITARGETSRn 寄存器配置,每个中断 8 位目标位掩码——每 bit 对应一个核心,可同时路由到多个核心。写入 0x01 表示仅路由到 Core #0,0x03 表示 Core #0 和 Core #1
  • 安全属性(Security):通过 GICD_IGROUPRn 寄存器配置,每中断 1 位——0 表示 Secure Group 0,1 表示 Group 1(Normal World)
  • 使能状态(Enable):通过 GICD_ISENABLERn 置位使能,通过 GICD_ICENABLERn 清除禁用

完成所有中断源的属性配置后,软件通过写 Distributor 控制寄存器来使能整个 Distributor 模块。

7.3 CPU Interface 配置

每个核心的 CPU Interface 也需要独立配置:

  • 优先级掩码(Priority Mask):设置阈值,优先级低于此值的中断被 CPU Interface 过滤
  • 抢占设置(Preemption):配置是否允许高优先级中断抢占当前正在处理的低优先级中断

最后,使能 CPU Interface 控制寄存器,使该核心准备好接收中断。

7.4 软件侧准备

在核心预期接收中断之前,软件必须完成:

  1. 在向量表中设置有效的中断向量入口
  2. 清除 CPSR 中的中断屏蔽位

这意味着即使 GIC 完全配置就绪,如果向量表未初始化或 CPSR 屏蔽位未清除,核心仍不会响应中断。

7.5 中断处理流水线

核心响应中断后,进入六阶段标准处理流水线。外设首先生成中断信号,经 Distributor 优先级仲裁和路由决策后到达目标核心的 CPU Interface;核心硬件自动完成模式切换与返回地址保存后跳转到向量表入口;软件从 IAR 获取中断 ID 进行分派处理;处理完毕后通过 EOIR 释放中断状态;之后循环检查是否仍有挂起中断——直到 IAR 返回 1023 表示全部处理完毕。

gic-handling-flow1

以下逐步拆解每个阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌─────────────────────────────────────────────────────┐
│ 1. Peripheral asserts interrupt │
│ → Distributor marks as Pending │
│ → Arbitration → forwards to target CPU I/F │
│ → CPU I/F signals IRQ/FIQ to core │
└────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ 2. Core takes IRQ/FIQ exception │
│ → HW saves LR/SPSR, switches to IRQ/FIQ mode │
│ → PC jumps to vector table entry │
└────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ 3. Top-level handler reads IAR │
│ → Reads Interrupt Acknowledge Register │
│ from CPU Interface to obtain Interrupt ID │
│ → Read also marks interrupt Active in Distributor│
└────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ 4. Dispatch to device handler by Interrupt ID │
│ → ID 0-15: IPI / inter-core comm │
│ → ID 16-31: Private peripheral handler │
│ → ID 32-1020: Shared peripheral handler │
└────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ 5. Device handler completes, write EOIR │
│ → Write Interrupt ID to End of Int Register │
│ in CPU Interface │
│ → Clear Active status │
│ → If Active & Pending → becomes Pending │
│ → Else → becomes Inactive │
│ → CPU I/F ready to forward next interrupt │
└────────────────────┬────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ 6. Check for more pending interrupts │
│ → Read IAR again │
│ → If ID=1023 (Spurious ID): exit loop │
│ → Else: repeat steps 4-6 │
└─────────────────────────────────────────────────────┘

7.6 伪中断 ID 1023

GIC 架构定义了一个特殊的伪中断 ID(Spurious Interrupt ID):1023。当顶层中断处理例程读取 IAR 且当前核心没有更多待处理中断时,IAR 返回此值。

伪中断 ID 是保留值——系统中任何设备都不能分配此 ID。顶层处理例程通过不断读取 IAR → 分派处理 → 写 EOIR 的循环,直到 IAR 返回 1023,然后退出中断处理,准备恢复被中断的任务。

这一机制确保了即使有多个中断在等待同一核心,每个中断都会被依次处理,而不会遗漏。


8. 关键要点

  1. IRQ 和 FIQ 是 ARM 核心的两个独立中断信号线,均为电平敏感低有效。CPSR 的 I 位和 F 位分别控制屏蔽,CPSIE/CPSID 指令提供原子使能/禁用。

  2. 精简中断处理是不可重入的:处理期间同类型中断被硬件自动屏蔽,处理完成后才恢复。适用于简单系统,不适合实时性要求高的场景。

  3. 嵌套中断的核心难点是 banked 寄存器覆盖:重新启用中断后,新中断会将返回地址写入 LR_IRQ、状态写入 SPSR_IRQ,覆盖原中断的上下文。解决方案是在启用中断前切换到备用处理器模式。

  4. SRS + RFE 指令对是 ARMv7-A 嵌套中断的关键基础设施SRS 原子保存 LRSPSR 到指定模式栈,RFE 从栈中原子恢复 PCCPSR,避免了手动操作 SPSR 导致的竞态窗口。

  5. GIC 采用 Distributor + CPU Interface 两层架构:Distributor 集中管理所有中断的属性(优先级、路由、使能),CPU Interface 为每个核心提供独立的中断传递通道,采用 banked 设计。

  6. GIC 支持三类中断:SGI(核间通信,ID 0-15)、PPI(核私有外设,ID 16-31)、SPI(系统共享外设,ID 32-1020)。每种中断在 GIC 内经历 Inactive → Pending → Active → Inactive 的四态生命周期。

  7. 中断需要经过四级使能才能到达核心:个体中断使能 → Distributor 使能 → CPU Interface 使能 → CPSR 中断屏蔽位清除。任何一级被禁用,中断都无法被响应。

  8. GIC 中断处理流水线是 IAR → Dispatch → EOIR 的标准三段式:读 IAR 获取中断 ID 并标记 Active,分派到设备处理函数,写 EOIR 清除 Active 状态。循环直到 IAR 返回伪中断 ID 1023(Spurious ID),确保所有挂起中断都被处理。