浅谈 OpenHarmony LiteOS-A 内核之基础硬件——中断控制器 GIC400
一、前言
OpenAtom OpenHarmony(以下简称“OpenHarmony”)采用多内核架构,支持 Linux 内核的标准系统、LiteOS-A 的小型系统、LiteOS-M 的轻量系统。
其中 LiteOS-A 要求设备具备一定的处理能力,对比 LiteOS-M,LiteOS-A 支持以下特性:
(1)MMU 支持:通过 MMU 支持内核态和用户态分离,支持虚拟单元;
(2)支持独立进程:调度对象分别为进程、线程;
(3)支持文件系统:包括虚拟文件和块设备等;
(4)支持更复杂的 IPC:包括 LiteIPC 等;
(5)支持多核调度:支持双核 MCU,支持双核调度;
(6)支持 POSIX3 接口:为 APP 开发提供更多帮助。
LiteOS-A 内核特性都是建立在 CPU 硬件的基础上,而中断控制器在支持 LiteOS-A 内核的 CPU 中发挥着巨大的作用:它管理和控制可屏蔽中断并对可屏蔽中断进行优先权判定,减少 CPU 的负载,使得 CPU 更加专注于计算。
在嵌入式领域,ARM 公司提供的芯片目前是市场的主流,OpenHarmony LiteOS-M 内核目前支持的 ARM 公司的 Cortex-M 系列的芯片,而 LiteOS-A 内核支持的则是 ARM 公司功能更强大的 Cortex-A/R 系列的芯片,GIC 是 ARM 公司给 Cortex-A/R 系列芯片提供的一个中断控制器,在移植 OpenHarmony LiteOS-A 内核到特定板子的实践中,我们遇到了很多 GIC 中断控制器相关的技术问题,所以需要深入了解 ARM 体系架构下 GIC 中断控制器的原理和使用方法,特此总结并共享给各位网友。
二、GIC 控制器概述
1、GIC 简介
GIC 是 ARM 公司给 Cortex-A/R 核提供的一个中断控制器,类似 Cortex-M 中的 NVIC。目前 GIC 有 4 个版本:V1~V4:V1 是最老的版本,已经被废弃了;V2~V4 目前正在被大量地使用。GIC V2 是给 ARMv7-A 架构使用的,比如 Cortex-A5,Cortex-A7、Cortex-A9、Cortex-A15 等,V3 和 V4 是给 ARMv8-A/R 架构使用的,也就是 64 位芯片使用的。
GIC V2 最多支持 8 个核。ARM 会根据 GIC 版本的不同研发出不同的 IP 核,半导体厂商直接购买对应的 IP 核即可,比如 ARM 针对 GIC V2 就开发出了 GIC400 中断控制器 IP 核。注意,具体产品是 GIC400,设计规范是 V2。当 GIC 接收到外部中断信号以后汇报给 ARM 内核,但是 ARM 内核只提供四个信号给 GIC 来汇报中断情况:VFIQ、VIRQ、FIQ 和 IRQ,他们之间的关系如图所示:
在图中,GIC 接收众多的外部中断,并对其进行处理,最终只通过四个信号报给 ARM 内核,这四个信号的含义如下:
● VFIQ: 虚拟快速 FIQ
● VIRQ: 虚拟外部 IRQ
● FIQ: 快速中断 IRQ
● IRQ: 外部中断 IRQ
2、GIC 整体实现
下图左侧部分是中断源,中间部分是 GIC 控制器,最右侧是中断控制器向处理器内核发送中断信息。我们重点要看的是中间的 GIC 部分,GIC 将众多的中断源分为三类: ①SPI(Shared Peripheral Interrupt),共享中断,即所有 Core 共享的中断,外部中断都属于 SPI 中断。比如按键中断、串口中断等,这些中断所有的 Core 都可以处理,不限定特定 Core。
在图中,每个 SPI 设计是共享的,在 SMP 系统中每个 SPI 都需要连接一个线到各个 CPU 中。
②PPI(Private Peripheral Interrupt),私有中断,GIC 是支持多核的,每个核有自己独有的中断,需要指定的核心处理。
在图中,每个 CPU 都有属于自己的 PPIs,对应关系是 1 对 1。
③SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器 GICD_SGIR 写入数据来触发,系统使用 SGI 中断来完成多核之间的通信。
在图中,SGI 是软件(软件运行在 CPU 上)触发,所以 SGI 中断源头是各个 CPU 上的应用,另外 SGI 和 PPI 一样,每个 CPU 都有属于自己的 SGI,所以必须指定 CPU。
3、中断 ID
中断源有很多,为了区分不同的中断源要给它们分配唯一 ID,也就是中断 ID。每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019。1020 个 ID 包含了 PPI、SPI 和 SGI,那么这三类中断是如何分配这 1020 个中断 ID 的呢?分配如下:
● ID0~ID15:这 16 个 ID 分配给 SGI
● ID16~ID31:这 16 个 ID 分配给 PP
● ID32~ID1019:这 988 个 ID 分配给 SPI,像 GPIO 中断、串口中断等这些外部中断
至于具体到某个 ID 对应哪个中断,那就由半导体厂商根据实际情况去定义。比如 I.MX6U 的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID,I.MX6U 的中断源共有 128+32=160 个。
4、GIC 逻辑模块
GIC 架构分为了两个逻辑块:Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。Distributor(分发器端):参考 GIC 整体实现的图,此逻辑块负责处理各个中断事件的分发问题,确定中断事件应该发送到哪个 CPU Interface 上去。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到 CPU 接口端。分发器端要做的主要工作如下:
① 全局中断使能控制
② 控制每一个中断的使能或者关闭
③ 设置每个中断的优先级
④ 设置每个中断的目标处理器列表
⑤ 设置每个外部中断的触发模式:电平触发或边沿触发
⑥ 设置每个中断属于组 0 还是组 1,此设置涉及到另外一个领域安全领域
CPU Interface(CPU 接口端):CPU 接口端和 CPU Core 相连接,因此每个 CPU Core 都可以在 GIC 中找到一个与之对应的 CPU Interface。CPU 接口端是分发器和 CPU Core 之间的桥梁,CPU 接口端主要工作如下:
① 使能或者关闭发送到 CPU Core 的中断请求信号
② 应答中断
③ 通知中断处理完成
④ 设置优先级掩码,通过掩码来设置哪些中断不需要上报给 CPU Core
⑤ 定义抢占策略
⑥ 当多个中断到来的时候,选择优先级最高的中断通知给 CPU Core
三、GIC400 原理
1、整体框架图
GIC-400 实现了以下的中断类型:
● 16 个软件产生的中断(SGI)
● 每个处理器有 6 个外部私有外设中断(PPI)
● 每个处理器有 1 个内部 PP
● 可配置的共享外设中断(SPI)的数量
GIC-400 的 BD 如下图所示,GIC-400 从中断输入信号中检测 PPI 和 SPI。每个处理器的每个 PPI 中断 ID 都有一个信号。每个 SPI 中断 ID 只有一个输入信号,与 SoC 中处理器的数量无关。SGI 没有输入信号,在 GIC-400 中使用 AXI 编程接口生成。
2、输入信号
上图中左边就是来自外设的 interrupt source 输入信号。分成两种类型,分别是 PPI(Private Peripheral Interrupt)和 SPI(Shared Peripheral Interrupt)。PPI 中断信号是 CPU 私有的,每个 CPU 都有其特定的 PPI 信号线。而 SPI 是所有 CPU 之间共享的。通过寄存器 GICD_TYPER 可以配置 SPI 的个数(最多 480 个)。GIC-400 支持多少个 SPI 中断,其输入信号线就有多少个 SPI interrupt request signal。同样的,通过寄存器 GICD_TYPER 也可以配置 CPU interface 的个数(最多 8 个),GIC-400 支持多少个 CPU interface,其输入信号线就提供多少组 PPI 中断信号线。一组 PPI 中断信号线包括 6 个实际的 signal:
(1)nLEGACYIRQ 信号线。对应 interrupt ID 31,在 bypass mode 下(这里的 bypass 是指 bypass GIC functionality,直接连接到某个 processor 上),nLEGACYIRQ 可以直接连到对应 CPU 的 nIRQCPU 信号线上。在这样的设置下,该 CPU 不参与其他属于该 CPU 的 PPI 以及 SPI 中断的响应,而是特别为这一根中断线服务。
(2)nCNTPNSIRQ 信号线。来自 Non-secure physical timer 的中断事件,对应 interrupt ID 30。
(3)nCNTPSIRQ 信号线。来自 secure physical timer 的中断事件,对应 interrupt ID 29。
(4)nLEGACYFIQ 信号线。对应 interrupt ID 28。概念与 nLEGACYIRQ 信号线相同。
(5)nCNTVIRQ 信号线。对应 interrupt ID 27。Virtual Timer Event,和虚拟化相关,这里不予描述。
(6)nCNTHPIRQ 信号线。对应 interrupt ID 26。Hypervisor Timer Event,和虚拟化相关,这里不予描述。
3、输出信号
所谓输出信号,就是 GIC 和各个 CPU 直接的接口,这些接口包括:
(a)触发 CPU 中断的信号。nIRQCPU 和 nFIQCPU 信号线,主要用来触发 ARM CPU 进入 IRQ mode 和 FIQ mode。
(b)Wake up 信号。nFIQOUT 和 nIRQOUT 信号线,协助 ARM CPU 的电源管理模块,用来唤醒 CPU。
(c)AXI slave interface signals。AXI(Advanced eXtensible Interface)是一种总线协议,属于 AMBA 规范的一部分。通过这些信号线,ARM CPU 可以和 GIC 硬件 block 进行通信(例如寄存器访问)。
4、触发路径
下面总结下 SGI、PPI、SPI 三种类型中断的触发路径:
SGI(ID0-ID15):是由 CPU 内部软件触发,所以从 CPU CORE-->CPU interface-->Distributor-->CPU Interface-->CPU CORE。
PPI(ID16-ID31):是由外部器件触发,从 Peripheral-->Distributor-->CPU interface-->CPU CORE。
SPI(ID32-ID1019):也是外部器件触发,从 Peripheral-->Distributor-->CPU interface-->CPU CORE。
5、中断状态迁移图
对于每一个中断而言,有以下 4 个状态:
inactive:中断处于无效状态。
pending:中断处于有效状态,但是 cpu 没有响应该中断。
active:cpu 在响应该中断。
active and pending:cpu 在响应该中断,但是该中断源又发送中断过来。
PS:对于电平触发的中断而言,一个中断的处理阶段也会有 active and pending 的阶段,这是比较特殊的。下面是官方提供中断迁移的条件和场景说明,要注意的是文中 either 表示转移条件满足其一即可:
下面是官方提供中断迁移的条件和场景说明,要注意的是文中 either 表示转移条件满足其一即可:
四、GIC400 寄存器
1、GIC-400 寄存器地图
所有 GIC-400 寄存器都有短名称。在这些名称中,前三个字符是 GIC,第四个字符表示 GIC-400 的功能块:
● GICD_ 分配器
● GICC_ 中央处理器接口
● GICH_ 虚拟接口控制块
● GICV_ 虚拟 CPU 接口。
GIC-400 寄存器是内存映射的,下表列出了地址范围:
内存映射的含义:对于 ARM 体系架构而言,IO 和内存是统一编制,寄存器会占用内存的地址范围。至于 GIC-400 寄存器首地址映射在哪个内存地址上,这是硬件厂商设计,并通过 CP15 协处理器来获取。
2、具体寄存器
下面解释几个经典的寄存器,更多的寄存器请参考 ARM 出品的官方文档。
(1)GICD_CTRL 寄存器
寄存器 GICD_CTRL 各个位的含义如下:
-------------------------------------
GICD_CTRL_reserved_0:保留。
-------------------------------------
EnableGrp1:Global enable for forwarding pending Group 1 interrupts from the Distributor to the CPU interfaces:
-------------------------------------
EnableGrp0:Global enable for forwarding pending Group 0 interrupts from the Distributor to the CPU interfaces:
-------------------------------------
个人见解:第一,EnableGrp1 和 EnableGrp0 是全局开关,第二,这是 Distributor 侧的开关,至于 CPU interfaces 侧继续参考其它寄存器。
-------------------------------------
(2)2 GICD_TYPER
-------------------------------------
GICD_TYPER_reserved_0:
个人见解:保留字段,不使用
-------------------------------------
LSPI:
If the GIC implements the Security Extensions, the value of this field is the maximum number of implemented lockable SPIs, from 0 (0b00000) to 31 (0b11111), see Configuration lockdown on page 4-82. If this field is 0b00000 then the GIC does not implement configuration lockdown. If the GIC does not implement the Security Extensions, this field is reserved.
个人见解:在不使能 ARM 安全体系的功能前提下,此处保留,安全体系的功能暂时不使用。
-------------------------------------
SecurityExtn:
Indicates whether the GIC implements the Security Extensions.0 Security Extensions not implemented.1 Security Extensions implemented.
个人见解:ARM 安全体系的功能,使能后,0 组和 1 组含义不一样,具体需要另外篇幅来阐述
-------------------------------------
GICD_TYPER_reserved_1:
-------------------------------------
CPUNumber:
Indicates the number of implemented CPU interfaces. The number of implemented CPUinterfaces is one more than the value of this field, for example if this field is 0b011, thereare four CPU interfaces. If the GIC implements the Virtualization Extensions, this is also the number of virtual CPU interfaces.
个人见解:the number of implemented CPU interfaces = CPUNumber+1,域值是从 0 开始,含义却是从 1 开始。
-------------------------------------
ITLinesNumber:
Indicates the maximum number of interrupts that the GIC supports. If ITLinesNumber=N, the maximum number of interrupts is 32(N+1). The interrupt ID range is from 0 to (number of IDs - 1).
For example: 0b00011 Up to 128 interrupt lines, interrupt IDs 0-127.
The maximum number of interrupts is 1020 (0b11111). See the text in this section for more information. Regardless of the range of interrupt IDs defined by this field, interrupt IDs 1020-1023 are reserved for special purposes.
个人见解:最大的中断数量是 32*(ITLinesNumber +1)
-------------------------------------
-------------------------------------
ProductID:
个人见解:值 0x020 意味着 GIC-400
-------------------------------------
GICD_IDDR_reserved_0:
个人见解:reserved
-------------------------------------
Variant:
An IMPLEMENTATION DEFINED variant number. Typically, this field is used to distinguishproduct variants, or major revisions of a product.
个人见解:产品主版本号,0x2 代表版本为 2.0
-------------------------------------
Revision:
An IMPLEMENTATION DEFINED revision number. Typically, this field is used to distinguish minor revisions of a product.
个人见解:产品小版本号,0x1 代表修订版为 r0p1。
-------------------------------------
Implementer:
Contains the JEP106 code of the company that implemented the GIC Distributor: Bits [11:8] The JEP106 continuation code of the implementer. For an ARM implementation, this field is 0x4. Bits [7] Always 0. Bits [6:0] The JEP106 identity code of the implementer. For an ARM implementation, bits[7:0] are 0x3B.
个人见解:实现 CPU 接口的公司代码,0x43B 代表 ARM 公司。
-------------------------------------
Group_Status_Bits:
The GICD_IGROUPR registers provide a status bit for each interrupt supported by the GIC.
Each bit controls whether the corresponding interrupt is in Group 0 or Group 1. Accessible by Secure accesses Only.
For each bit:
0 The corresponding interrupt is Group 0.
1 The corresponding interrupt is Group 1.
For interrupt ID m, when DIV and MOD are the integer division and modulo operations:
a. the corresponding GICD_IGROUPRn number, n, is given by n = m DIV 32
b. the offset of the required GICD_IGROUPR is (0x080 + (4n))
c. the bit number of the required group status bit in this register is m MOD 32.
个人见解:
① 组状态位,对于每个位:“0”表示相应的中断为 Group 0;“1”表示相应的中断为 Group 1。
② 问:对于一个中断,如何设置它的 Group?答:首先找到对应的 GICD_IGROUPRn 寄存器,即 n 是多少?还要确定使用这个寄存器里哪一位。对于 interrtups ID m,如下计算:n = m DIV 32,GICD_IGROUPRn 里的 n 就确定了;
③ GICD_IGROUPRn 在 GIC 内部的偏移地址是多少?答:0x080+(4n),这个信息是为了定位到具体寄存器。
④ 使用 GICD_IPRIORITYRn 中哪一位来表示?答:bit = m mod 32。
⑤ GICD_IGROUPn_0 只是 GICD_IGROUPn 寄存器组的第一个,总共有 4 个寄存器:GICD_IGROUPn_0 到 GICD_IGROUPn_3
(5)GICD_ISENABLER0
Set_Enable_Bits:
The GICD_ISENABLERs provide a Set-enable bit for each interrupt supported by the GIC. For SPIs and PPIs, each bit controls the forwarding of the corresponding interrupt from the Distributor to the CPU interfaces:
Reads:
0 Forwarding of the corresponding interrupt is disabled.
1 Forwarding of the corresponding interrupt is enabled.
Writes:
0 Has no effect.
1 Enables the forwarding of the corresponding interrupt.
After a write of 1 to a bit, a subsequent read of the bit returns the value1. For interrupt ID m, when DIV and MOD are the integer division and modulo operations:
a.the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32
b.the offset of the required GICD_ISENABLER is (0x100 + (4n))
c.the bit number of the required Set-enable bit in this register is m MOD 32.
个人见解:
① 对于 SPI 和 PPI 类型的中断,每一位控制对应中断的转发行为:从 Distributor 转发到 CPU interface:
读:“0”表示当前是禁止转发的;“1”表示当前是使能转发的;
写:“0”表示无效;“1”表示使能转发。
② 对于一个中断,如何找到 GICD_ISENABLERn 并确定相应的位?
答:对于 interrtups ID m,如下计算:n = m DIV 32,GICD_ISENABLERn 里的 n 就确定了;
GICD_ISENABLERn 在 GIC 内部的偏移地址是多少?计算方法为 0x100+(4n),因为一个寄存器大小为 4 个字节。
使用 GICD_ISENABLERn 中哪一位来表示?计算方法为?bit = m mod 32。
③ 这里的开关也是 Distributor 侧的开关,GICV 硬件设计思想和软件设计思想类似。
④ GICD_ISENABLER0 是此类寄存器组的第一个,总共是 4 个。
(6)GICD_ICFGR0
五、问题与建议
1、ARM 芯片设计了 FIQ 和 IRQ 的 2 个处理模式,移植过程中没有用到 FIQ。
2、虚拟中断相关的功能也没有用到。
3、安全相关的中断功能前期我们也没有用到,后续在可信执行环境中会用到。
4、如果代码对中断保护写得不好的话,会出现 Systick 发生多次中断,中断状态为 active &pending,在移植过程中 OpenHarmony 版本不支持嵌套中断。
5、中断处理分为边沿触发处理和电平触发处理。这就给我们不同的应用功能提供了选择,使得我们可以在不同个工作下选择适合的模式,边沿触发适用于对时间要求高的,比如中断中有计数之类的(GATE 门控位置 1 时),而电平触发则适合报警装置。
6、在基于 ARM 的嵌入式应用系统中,存储系统通常是通过系统控制协处理器 CP15 完成的。ARM 处理器使用协处理器 15(CP15)的寄存器来控制 cache、TCM 和存储器管理。CP15 包含 16 个 32 位的寄存器,其编号为 0~15,具体请参考 ARM 官方文档。
7、givc2 将中断分成了 group0 和 group1,默认情况下,所有中断都是组 0 中断,并使用 IRQ 向连接的处理器发送信号中断请求。个人理解如果都改为组 1 也是没有问题的,但是没有验证。
8、GIC-400 通用初始化流程:
(1)设置 distributor 和 CPU interface 寄存器组的基地址;
(2)读取 GICD_TYPER 寄存器,计算当前 GIC 最大支持多少个中断源;
(3)初始化 distributor:
a.disable distributor;
b.设置中断分组;
c.设置 SPI 中断的路由;
d.设置 SPI 中断的触发类型;
e.disactive 和 disable 所有中断源;
f.enable distributor;(
4)初始化 CPU Interface:
a.设置 GIC_CPU_PRIMASK,设置中断优先级 mask level;
b.enable CPU interface;
六、总结
本篇文章从 GIC 控制器概述、GIC400 原理、GIC400 寄存器等方面介绍了 OpenHarmony 内核之基础硬件——中断控制器 GIC400 的内容,希望开发者能从中学有所获,学以致用。
评论