Skip to content

LV015-中断与GIC

一、中断处理

对于 S3C2440 等较旧的 ARM 体系结构版本,芯片厂家在设计外部中断控制器时具有很大的自由度,在中断的数量、类型、与中断控制器模块接口等时方面 都可以自由发挥。

通用中断控制器 v2( GIC)架构提供了更为严格的规范,不同厂商的中断控制器之间具有更高的一致性。这使中断处理程序代码更易于移植。

1. 外部中断请求

ARM 核有两个外部中断请求 FIQ 和 IRQ,如下图所示:

image-20230910095454580

各个不同的芯片实现,里面都有中断控制器。中断控制器接受来自各种外部源的中断请求并将它们映射为 FIQ 或 IRQ,从而导致 ARM 核发生异常。

通常,只有当相应的 CPSR 禁止位(分别为 F 和 I 位)清零并且相应的输入为有效时,才可以产生中断异常。

CPS 指令提供了一种简单的机制来启用或禁用由 CPSR A, I 和 F 位(分别为异步中止, IRQ 和 FIQ)控制的异常。

CPS IE 或 CPS ID 将分别启用或禁用异常。使用字母 A, I 和 F 中的一个或多个指定要启用或禁用的异常。省略了相应字母的异常将不会被修改。

在 Cortex-A 系列处理器中,可以配置 CPU 核,以使 FIQ 不能被软件屏蔽。这被称为不可屏蔽 FIQ,并由 CPU 核复位时采样的硬件配置的输入信号控制。发 生 FIQ 异常后,它们仍将自动被屏蔽。

2. 分配中断

中断控制器接受来自各种源的中断, 并决定谁能优先被处理(仲裁)。控制器通常包含多个寄存器,通过这些寄存器,我们能够屏蔽或使能各个中断源,为 各个中断源分配优先级;当发生中断时,可以确定中断源。

中断控制器可以由芯片厂家自己设计(比如比较老的芯片 S3C2440),也可以使用 ARM 通用中断控制器( GIC)架构。

3. 简单的中断处理

image-20230910100505193

上图代表了最简单的中断处理程序。发生中断时,将禁用其他同类中断,直到稍后显式启用。我们只能在第一个中断请求完成时才能处理其他中断,并且在 此期间没有更高优先级或更紧急的中断需要处理。在这种情况下是不可重入的中断处理程序。处理中断所采取的步骤如下:

  • (1)外部硬件引发 IRQ 异常

ARM 核自动执行几个步骤:

① 当前模式下 PC 的内容存储在 LR_IRQ 中;

② CPSR 寄存器被复制到 SPSR_IRQ;

③ CPSR 内容被更新,设置模式位为 IRQ 模式,并且将 I 位设置为屏蔽其他 IRQ;

④ PC 被设置为向量表中的 IRQ 入口。

  • (2)执行向量表中 IRQ 入口处(中断异常的分支)的指令;

  • (3)中断处理程序保存被中断程序的上下文:它将被该中断处理程序损坏的所有寄存器压入堆栈。

  • (4)中断处理程序确定中断源,然后调用响应的处理程序。

  • (5)恢复现场。

通过将 SPSR_IRQ 复制到 CPSR,并还原先前保存的上下文,准备将 CPU 核切换到先前的执行状态,最后从 LR_IRQ 恢复 PC。相同的顺序也适用于 FIQ 中断。

4. 嵌套中断处理

嵌套中断处理是软件可以在完成对当前中断的处理之前,接受另一个中断。这可以将中断进行优先级分级,降低高优先级事件的响应延迟,代价是增加了软 件的复杂性。值得注意的是,嵌套中断处理是由软件来实现。

支持中断嵌套的处理函数,被称为“可重入中断处理程序”。在 Linux 系统中,不支持中断嵌套。对于裸机程序,我们也不需要花费精力在这上面。在实时性要求很高的 RTOS 中,也许对中断嵌套有所要求。但是可以通过其他办法习实现,比如把某个中断单独设置在 FIQ。

二、通用中断控制器(GIC)

ARM 体系结构定义了通用中断控制器(GIC, Generic InterruptController),该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。 GIC 提供了内存映射寄存器,可用于管理中断源和行为,以及(在多核系统中)用于将中断路由到各个 CPU 核。它使软件能够屏蔽,启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。它还提供对 TrustZone 安全性扩展的支持。GIC 接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致 IRQ 或 FIQ 异常发生。

GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核中的 NVIC。目前 GIC 有 4 个版本: V1~V4, V1 是最老的版本,已经被废弃了。 V2~V4 目前正在大量的使用。 GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A7、 Cortex-A9、 Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。

1. 基本结构

从软件角度来看, GIC 具有两个主要功能模块,

image-20230910103302383
  • 分发器(Distributor)

系统中的所有中断源都连接到该单元。可以通过仲裁单元的寄存器来控制各个中断源的属性,例如优先级、状态、安全性、路由信息和使能状态。分发器把中断输出到“ CPU 接口单元”,后者决定将哪个中断转发给 CPU 核。

  • CPU 接口单元(CPU Interface)

CPU 核通过控制器的 CPU 接口单元接收中断。 CPU 接口单元寄存器用于屏蔽,识别和控制转发到 CPU 核的中断的状态。系统中的每个 CPU 核心都有一个单独的 CPU 接口。中断在软件中由一个称为中断 ID 的数字标识。中断 ID 唯一对应于一个中断源。软件可以使用中断 ID 来识别中断源并调用相应的处理程序来处理中断。呈现给软件的中断 ID 由系统设计确定,一般在 SOC 的数据手册有记录。

2. 中断的类型与状态

2.1 中断可以有多种不同的类型

  • (1)软件触发中断(SGI, Software Generated Interrupt)

这是由软件通过写入专用仲裁单元的寄存器即软件触发中断寄存器( ICDSGIR)显式生成的。它最常用于 CPU 核间通信。 SGI 既可以发给所有的核,也可以发送给系统中选定的一组核心。中断号 0-15 保留用于 SGI 的中断号。用于通信的确切中断号由软件决定。

  • (2)私有外设中断(PPI, Private Peripheral Interrupt)

这是由单个 CPU 核私有的外设生成的。 PPI 的中断号为 16-31。它们标识 CPU 核私有的中断源,并且独立于另一个内核上的相同中断源,比如,每个核的计时器。

  • (3)共享外设中断(SPI, Shared Peripheral Interrupt)

这是由外设生成的,中断控制器可以将其路由到多个核。中断号为 32-1020。SPI 用于从整个系统可访问的各种外围设备发出中断信号。中断可以是边沿触发的(在中断控制器检测到相关输入的上升沿时认为中断触发,并且一直保持到清除为止)或电平触发(仅在中断控制器的相关输入为高时触发)。

2.2 中断可以处于多种不同状态

(1)非活动状态(Inactive): 这意味着该中断未触发。

(2)挂起(Pending): 这意味着中断源已被触发,但正在等待 CPU 核处理。待处理的中断要通过转发到 CPU 接口单元,然后再由 CPU 接口单元转发到内核。

(3)活动(Active): 描述了一个已被内核接收并正在处理的中断。

(4)活动和挂起(Active and pending):描述了一种情况,其中 CPU 核正在为中断服务,而 GIC 又收到来自同一源的中断。

3. GIC 的逻辑结构

GIC 控制器的逻辑结构 如下图(《ARM® Generic Interrupt Controller Architecture version 2.0》的 2.1 About GIC partitioning)所示:

image-20260115171952118

中断的优先级和可接收中断的核都在分发器(distributor)中配置。外设发给分发器的中断将标记为 pending 状态(或 Active and Pending 状态,如触发时果状态是 active)。 distributor 确定可以传递给 CPU 核的优先级最高的 pending 中断,并将其转发给内核的 CPU interface。通过 CPU interface,该中断又向 CPU 核发出信号,此时 CPU 核将触发 FIQ 或 IRQ 异常。

作为响应, CPU 核执行异常处理程序。异常处理程序必须从 CPU interface 寄存器查询中断 ID,并开始为中断源提供服务。完成后,处理程序必须写入 CPU interface 寄 存 器 以 报 告 处 理 结 束 。 然 后 CPU interface 准备转发 distributor 发给它的下一个中断。

在处理中断时,中断的状态开始为 pending, active,结束时变成 inactive。中断状态保存在 distributor 寄存器中。

4. GIC 的配置

GIC 作为内存映射的外围设备,被软件访问。所有内核都可以访问公共的 distributor 单元,但是 CPU interface 是备份的,也就是说,每个 CPU 核都使用相同的地址来访问其专用 CPU 接口。一个 CPU 核不可能访问另一个 CPU 核的 CPU 接口。Distributor 拥有许多寄存器,可以通过它们配置各个中断的属性。这些可配置属性是:

  • 中断优先级: Distributor 使用它来确定接下来将哪个中断转发到 CPU 接口。
  • 中断配置:这确定中断是对电平触发还是边沿触发。
  • 中断目标:这确定了可以将中断发给哪些 CPU 核。
  • 中断启用或禁用状态:只有 Distributor 中启用的那些中断变为挂起状态时,才有资格转发。
  • 中断安全性:确定将中断分配给 Secure 还是 Normal world 软件。
  • 中断状态。

Distributor 还提供优先级屏蔽,可防止低于某个优先级的中断发送给 CPU 核。每个 CPU 核上的 CPU interface,专注于控制和处理发送给该 CPU 核的中断。

5. 初始化 GIC

Distributor 和 CPU interface 在复位时均被禁用。复位后,必须初始化 GIC,才能将中断传递给 CPU 核。在 Distributor 中,软件必须配置优先级、目标核、安全性并启用单个中断;随后必须通过其控制寄存器使能。

对于每个 CPU interface,软件必须对优先级和抢占设置进行编程。每个 CPU 接口模块本身必须通过其控制寄存器使能。

在 CPU 核可以处理中断之前,软件会通过在向量表中设置有效的中断向量并清除 CPSR 中的中断屏蔽位来让 CPU 核可以接收中断。

可以通过禁用 Distributor 单元来禁用系统中的整个中断机制;可以通过禁用单个 CPU 的 CPU 接口模块或者在 CPSR 中设置屏蔽位来禁止向单个 CPU 核的中断传递。也可以在 Distributor 中禁用(或启用)单个中断。

为了使某个中断可以触发 CPU 核,必须将各个中断, Distributor 和 CPUinterface 全部使能,并将 CPSR 中断屏蔽位清零,如下图:

image-20230910103319234

6. GIC 中断处理

当 CPU 核接收到中断时,它会跳转到中断向量表执行。顶层中断处理程序读取 CPU 接口模块的 Interrupt Acknowledge Register,以获取中断 ID。除了返回中断 ID 之外,读取操作还会使该中断在 Distributor 中标记为 active 状态。一旦知道了中断 ID(标识中断源),顶层处理程序现在就可以分派特定于设备的处理程序来处理中断。

当特定于设备的处理程序完成执行时,顶级处理程序将相同的中断 ID 写入 CPU interface 模块中的 End of Interrupt register 中断结束寄存器, 指示中断处理结束。除了把当前中断移除 active 状态之外,这将使最终中断状态变为 inactive 或 pending(如果状态为 inactive and pending),这将使 CPU interface 能够将更多待处理 pending 的中断转发给 CPU 核。这样就结束了单个中断的处理。

同一 CPU 核上可能有多个中断等待服务,但是 CPU interface 一次只能发出一个中断信号。顶层中断处理程序重复上述顺序,直到读取特殊的中断 ID 值 1023,表明该内核不再有任何待处理的中断。这个特殊的中断 ID 被称为伪中断 ID( spurious interrupt ID)。

伪中断 ID 是保留值,不能分配给系统中的任何设备。

7. 中断控制器寄存器

GIC 分为两部分: Distributor 和 CPU interface,它们的寄存器都有相应的前缀:“GICD_”、“ GICC_”。这些寄存器都是映射为内存接口(memery map),CPU 可以直接读写。

这里就不详细介绍了,需要的时候可以查看《ARM® Generic Interrupt Controller Architecture version 2.0》的 Chapter 4 Programmers’ Model 一节。