add 08_Interrupt/08,09,10

This commit is contained in:
weidongshan
2021-06-25 18:18:22 +08:00
parent fde61df879
commit 19005cf6f9
108 changed files with 1970 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
## 中断相关的其他驱动程序
### 1. 概述
有了中断之后,可以实现很多功能,或者说这些功能跟中断的关系比较密切。
比如:
* 休眠与唤醒
* POLL机制
* 异步通知
* 阻塞与非阻塞
* 定时器
* 中断下半部tasklet
* 工作队列
* 中断的线程化处理
这些视频,在**Linux驱动基础**的视频里都讲过视频可以在http://www.100ask.net看到
![image-20210625150027987](pic/08_Interrupt/01_interrupt_usages.png)
配套的源码、文档在GIT仓库里
```shell
git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git
```

View File

@@ -0,0 +1,271 @@
# 中断的硬件框架
## 1.1 中断路径上的3个部件
* 中断源
中断源多种多样比如GPIO、定时器、UART、DMA等等。
它们都有自己的寄存器,可以进行相关设置:使能中断、中断状态、中断类型等等。
* 中断控制器
各种中断源发出的中断信号,汇聚到中断控制器。
可以在中断控制器中设置各个中断的优先级。
中断控制器会向CPU发出中断信号CPU可以读取中断控制器的寄存器判断当前处理的是哪个中断。
中断控制器有多种实现,比如:
* STM32F103中被称为NVICNested vectored interrupt controller(嵌套向量中断控制器)
* ARM9中一般是芯片厂家自己实现的没有统一标准
* Cortex A7中使用GIC(Generic Interrupt Controller)
* CPU
CPU每执行完一条指令都会判断一下是否有中断发生了。
CPU也有自己的寄存器可以设置它来使能/禁止中断,这是中断处理的总开关。
![](pic\08_Interrupt\002_exception_on_arm.png)
## 1.2 STM32F103的GPIO中断
参考资料:`STM32F103数据手册.pdf``ARM Cortex-M3与Cortex-M4权威指南.pdf``PM0056.pdf`
对于GPIO中断STM32F103又引入了`External interrupt/event controller (EXTI)`
用来设置GPIO的中断类型如下图
![](pic\08_Interrupt\017_stm32f103_gpio_to_nvic.png)
EXTI可以给NVIC提供16个中断信号EXTI0~EXTI15。
那么某个EXTIx它来自哪些GPIO呢这需要设置GPIO控制器。
### 1.2.1 GPIO控制器
STM32F103的GPIO控制器中有AFIO_EXTICR1~AFIO_EXTICR4一共4个寄存器
名为External interrupt configuration register外部中断配置寄存器。
用来选择某个外部中断EXTIx的中断源示例如下
![](pic/08_Interrupt/016_stm32f103_gpio_interrtup.png)
**注意**从上图可知EXTI0只能从PA0、……、PG0中选择一个这也意味着PA0、……、PG0中只有一个引脚可以用于中断。这跟其他芯片不一样很多芯片的任一GPIO引脚都可以同时用于中断。
![](pic\08_Interrupt\025_external_int_gpio_mapping.png)
### 1.2.2 EXTI
在GPIO控制器中可以设置某个GPIO引脚作为中断源给EXTI提供中断信号。
但是,这个中断的触发方式是怎么的?高电平触发、低电平触发、上升沿触发、下降沿触发?
这需要进一步设置。
EXTI框图如下
![](pic\08_Interrupt/018_stm32f103_exti.png)
沿着上面框图中的红线,我们要设置:
* Falling trigger selection register是否选择下降沿触发
* Rising trigger selection register是否选择上升沿触发
* Interrupt mask register是否屏蔽中断
当发生中断时,可以读取下列寄存器判断是否发生了中断、发生了哪个中断:
* Pending reqeust register
要使用EXTI流程如下
![](pic\08_Interrupt/019_stm32f103_how_to_use_exti.png)
翻译如下:
* 配置EXTI_IMR允许EXTI发出中断
* 配置EXTI_RTSR、EXTI_FTSR选择中断触发方式
* 配置NVIC中的寄存器允许NVIC把中断发给CPU
### 1.2.3 NVIC
多个中断源汇聚到NVICNVIC的职责就是从多个中断源中取出优先级最高的中断向CPU发出中断信号。
处理中断时程序可以写NVIC的寄存器清除中断。
涉及的寄存器:
![](pic\08_Interrupt/020_stm32f103_nvic_registers.png)
我们暂时只需要关注ISER(中断设置使能寄存器)、ICPR(中断清除挂起寄存器)。
要注意的是这些寄存器有很多个比如ISER0、ISER1等等。里面的每一位对应一个中断。
ISER0中的bit0对应异常向量表中的第16项(向量表从第0项开始),如下图:
![image-20201115125952814](pic\08_Interrupt/021_stm32f103_nvic_iser_icer.png)
### 1.2.4 CPU
cortex M3/M4处理器内部有这几个寄存器
#### 1. PRIMASK
![](pic\08_Interrupt/022_cortex_m3_primask.png)
把PRIMASK的bit0设置为1就可以屏蔽所有**优先级可配置**的中断。
可以使用这些指令来设置它:
```
CPSIE I ; 清除PRIMASK使能中断
CPSID I ; 设置PRIMASK禁止中断
或者:
MOV R0, #1
MSR PRIMASK R0 ; 将1写入PRIMASK禁止所有中断
MOV R0, #0
MSR PRIMASK, R0 ; 将0写入PRIMASK使能所有中断
```
#### 2. FAULTMASK
![image-20201115132713862](pic\08_Interrupt/023_cortex_m3_faultmask.png)
FAULTMASK和PRIMASK很像它更进一步出来一般的中断外把HardFault都禁止了。
只有NMI可以发生。
可以使用这些指令来设置它:
```
CPSIE F ; 清除FAULTMASK
CPSID F ; 设置FAULTMASK
或者:
MOV R0, #1
MSR FAULTMASK R0 ; 将1写入FAULTMASK禁止中断
MOV R0, #0
MSR FAULTMASK, R0 ; 将0写入FAULTMASK使能中断
```
#### 3. BASEPRI
![](pic\08_Interrupt/024_cortex_m3_basemask.png)
BASEPRI用来屏蔽这些中断它们的优先级其值大于或等于BASEPRI。
可以使用这些指令来设置它:
```
MOVS R0, #0x60
MSR BASEPRI, R0 ; 禁止优先级在0x60~0xFF间的中断
MRS R0, BASEPRI ; 读取BASEPRI
MOVS R0, #0
MSR BASEPRI, R0 ; 取消BASEPRI屏蔽
```
## 1.3 STM32MP157的GPIO中断
STM32MP157的GPIO中断在硬件上的框架跟STM32F103是类似的。
它们的中断控制器不一样STM32MP157中使用的是GIC
![](pic\08_Interrupt/026_stm32mp157_gpio_o_gic.png)
### 1.3.1 GPIO控制器
对于STM32MP157除了把GPIO引脚配置为输入功能外GPIO控制器里没有中断相关的寄存器。
请参考前面的课程《01_使用按键控制LED(STM32MP157)》。
### 1.3.2 EXTI
GPIO引脚可以向CPU发出中断信号所有的GPIO引脚都可以吗
不是的需要在EXTI控制器中设置、选择。
GPIO引脚触发中断的方式是怎样的高电平触发、低电平触发、上升沿触发、下降沿触发
这需要进一步设置。
这些都是在EXTI中配置EXTI框图如下
![](pic\08_Interrupt/027_stm32mp157_exti.png)
沿着红线走:
#### 1. 设置`EXTImux`
选择哪些GPIO可以发出中断。
只有16个EXTI中断从EXTI0~EXTI15每个EXTIx中断只能从PAx、PBx、……中选择某个引脚如下图所示
![](pic\08_Interrupt/028_stm32mp157_external_int_gpio_mapping.png)
**注意**从上图可知EXTI0只能从PA0、……中选择一个这也意味着PA0、……中只有一个引脚可以用于中断。这跟其他芯片不一样很多芯片的任一GPIO引脚都可以同时用于中断。
通过EXTI_EXTICR1等寄存器来设置EXTIx的中断源是哪个GPIO引脚入下图所示
![](pic\08_Interrupt/029_stm32mp157_exti_exticr1.png)
#### 2. 设置`Event Trigger`
设置中断触发方式:
![image-20201115225316786](pic\08_Interrupt/030_stm32mp157_exti_rtsr1_ftsr1.png)
#### 3. 设置`Masking`
允许某个EXTI中断
![](pic\08_Interrupt/031_stm32mp157_exti_imr1.png)
#### 4. 查看中断状态、清中断
![](pic\08_Interrupt/032_stm32mp157_exti_rpr1_fpr1.png)
### 1.3.3 GIC
ARM体系结构定义了通用中断控制器GIC该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器可用于管理中断源和行为以及在多核系统中用于将中断路由到各个CPU核。它使软件能够屏蔽启用和禁用来自各个中断源的中断在硬件中对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生并可以发信号通知给它所连接的每个内核从而有可能导致IRQ或FIQ异常发生。
GIC比较复杂下一个视频再详细讲解。
### 1.3.4 CPU
CPU的CPSR寄存器中有一位I位用来使能/禁止中断。
![](pic\08_Interrupt/008_xpsr.png)
可以使用以下汇编指令修改I位
```
CPSIE I ; 清除I位使能中断
CPSID I ; 设置I位禁止中断
```
## 1.4 IMX6ULL的GPIO中断
IMX6ULL的GPIO中断在硬件上的框架跟STM32MP157是类似的。
IMX6ULL中没有EXTI控制器对GPIO的中断配置、控制都在GPIO模块内部实现
![image-20201116000539003](pic\08_Interrupt/033_imx6ull_gpio_gic.png)
### 1.4.1 GPIO控制器
#### 1. 配置GPIO中断
每组GPIO中都有对应的GPIOx_ICR1、GPIOx_ICR2寄存器(interrupt configuration register )。
每个引脚都可以配置为中断引脚,并配置它的触发方式:
![](pic\08_Interrupt/034_imx6ull_gpiox_icr1.png)
#### 2. 使能GPIO中断
![](pic\08_Interrupt/035_imx6ull_gpiox_imr.png)
#### 3. 判断中断状态、清中断
![image-20201116001853748](pic\08_Interrupt/036_imx6ull_gpiox_isr.png)
### 1.4.2 GIC
ARM体系结构定义了通用中断控制器GIC该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器可用于管理中断源和行为以及在多核系统中用于将中断路由到各个CPU核。它使软件能够屏蔽启用和禁用来自各个中断源的中断在硬件中对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生并可以发信号通知给它所连接的每个内核从而有可能导致IRQ或FIQ异常发生。
GIC比较复杂下一个视频再详细讲解。
### 1.4.3 CPU
CPU的CPSR寄存器中有一位I位用来使能/禁止中断。
![](pic\08_Interrupt/008_xpsr.png)
可以使用以下汇编指令修改I位
```
CPSIE I ; 清除I位使能中断
CPSID I ; 设置I位禁止中断
```

View File

@@ -0,0 +1,451 @@
# GIC介绍与编程
参考资料:
* GIC的官方文档GIT仓库
```shell
doc_and_source_for_drivers\IMX6ULL\doc_pic\08_Interrupt:
doc_and_source_for_drivers\STM32MP157\doc_pic\08_Interrupt:
ARM® Generic Interrupt Controller Architecture Specification Architecture version 2.0(IHI0048B_b_gic_architecture_specification_v2).pdf
```
* 源码GIT仓库
```shell
doc_and_source_for_drivers\IMX6ULL\source\08_Interrupt\02_gic
doc_and_source_for_drivers\STM32MP157\source\A7\08_Interrupt\02_gic
```
## 1.1 GIC介绍
ARM体系结构定义了通用中断控制器GIC该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器可用于管理中断源和行为以及在多核系统中用于将中断路由到各个CPU核。它使软件能够屏蔽启用和禁用来自各个中断源的中断在硬件中对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生并可以发信号通知给它所连接的每个内核从而有可能导致IRQ或FIQ异常发生。
**从软件角度来看GIC具有两个主要功能模块简单画图如下**
![](pic\08_Interrupt\37_imx6ull_gic.png)
① 分发器(Distributor)
系统中的所有中断源都连接到该单元。可以通过仲裁单元的寄存器来控制各个中断源的属性,例如优先级、状态、安全性、路由信息和使能状态。
分发器把中断输出到“CPU接口单元”后者决定将哪个中断转发给CPU核。
② CPU接口单元CPU Interface
CPU核通过控制器的CPU接口单元接收中断。CPU接口单元寄存器用于屏蔽识别和控制转发到CPU核的中断的状态。系统中的每个CPU核心都有一个单独的CPU接口。
中断在软件中由一个称为中断ID的数字标识。中断ID唯一对应于一个中断源。软件可以使用中断ID来识别中断源并调用相应的处理程序来处理中断。呈现给软件的中断ID由系统设计确定一般在SOC的数据手册有记录。
**中断可以有多种不同的类型:**
① 软件触发中断SGISoftware Generated Interrupt
这是由软件通过写入专用仲裁单元的寄存器即软件触发中断寄存器ICDSGIR显式生成的。它最常用于CPU核间通信。SGI既可以发给所有的核也可以发送给系统中选定的一组核心。中断号0-15保留用于SGI的中断号。用于通信的确切中断号由软件决定。
② 私有外设中断PPIPrivate Peripheral Interrupt
这是由单个CPU核私有的外设生成的。PPI的中断号为16-31。它们标识CPU核私有的中断源并且独立于另一个内核上的相同中断源比如每个核的计时器。
③ 共享外设中断SPIShared Peripheral Interrupt
这是由外设生成的中断控制器可以将其路由到多个核。中断号为32-1020。SPI用于从整个系统可访问的各种外围设备发出中断信号。
中断可以是边沿触发的(在中断控制器检测到相关输入的上升沿时认为中断触发,并且一直保持到清除为止)或电平触发(仅在中断控制器的相关输入为高时触发)。
**中断可以处于多种不同状态:**
① 非活动状态Inactive这意味着该中断未触发。
② 挂起Pending这意味着中断源已被触发但正在等待CPU核处理。待处理的中断要通过转发到CPU接口单元然后再由CPU接口单元转发到内核。
③ 活动Active描述了一个已被内核接收并正在处理的中断。
④ 活动和挂起Active and pending描述了一种情况其中CPU核正在为中断服务而GIC又收到来自同一源的中断。
中断的优先级和可接收中断的核都在分发器(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发给它的下一个中断。
在处理中断时中断的状态开始为pendingactive结束时变成inactive。中断状态保存在distributor寄存器中。
下图是GIC控制器的逻辑结构
![](pic\08_Interrupt\38_imx6ull_gic.png)
### 1.1.1 配置
GIC作为内存映射的外围设备被软件访问。所有内核都可以访问公共的distributor单元但是CPU interface是备份的也就是说每个CPU核都使用相同的地址来访问其专用CPU接口。一个CPU核不可能访问另一个CPU核的CPU接口。
**Distributor拥有许多寄存器可以通过它们配置各个中断的属性。这些可配置属性是**
* 中断优先级Distributor使用它来确定接下来将哪个中断转发到CPU接口。
* 中断配置:这确定中断是对电平触发还是边沿触发。
* 中断目标这确定了可以将中断发给哪些CPU核。
* 中断启用或禁用状态只有Distributor中启用的那些中断变为挂起状态时才有资格转发。
* 中断安全性确定将中断分配给Secure还是Normal world软件。
* 中断状态。
Distributor还提供优先级屏蔽可防止低于某个优先级的中断发送给CPU核。
每个CPU核上的CPU interface专注于控制和处理发送给该CPU核的中断。
### 1.1.2 初始化
Distributor和CPU interface在复位时均被禁用。复位后必须初始化GIC才能将中断传递给CPU核。
在Distributor中软件必须配置优先级、目标核、安全性并启用单个中断随后必须通过其控制寄存器使能。
对于每个CPU interface软件必须对优先级和抢占设置进行编程。每个CPU接口模块本身必须通过其控制寄存器使能。
在CPU核可以处理中断之前软件会通过在向量表中设置有效的中断向量并清除CPSR中的中断屏蔽位来让CPU核可以接收中断。
可以通过禁用Distributor单元来禁用系统中的整个中断机制可以通过禁用单个CPU的CPU接口模块或者在CPSR中设置屏蔽位来禁止向单个CPU核的中断传递。也可以在Distributor中禁用或启用单个中断。
为了使某个中断可以触发CPU核必须将各个中断Distributor和CPU interface全部使能
将CPSR中断屏蔽位清零如下图
![](pic\08_Interrupt\39_imx6ull_gic.png)
### 1.1.3 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被称为伪中断IDspurious interrupt ID
伪中断ID是保留值不能分配给系统中的任何设备。
## 1.2 GIC的寄存器
GIC分为两部分Distributor和CPU interface它们的寄存器都有相应的前缀“GICD_”、“GICC_”。这些寄存器都是映射为内存接口(memery map)CPU可以直接读写。
### 1.2.1 Distributor 寄存器描述
#### 1. **Distributor Control Register, GICD_CTLR**
![](pic\08_Interrupt\40_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ---------- | ---- | ------------------------------------------------------------ |
| 1 | EnableGrp1 | R/W | 用于将pending Group 1中断从Distributor转发到CPU interfaces 0group 1中断不转发 1根据优先级规则转发Group 1中断 |
| 0 | EnableGrp0 | R/W | 用于将pending Group 0中断从Distributor转发到CPU interfaces 0group 0中断不转发 1根据优先级规则转发Group 0中断 |
#### 2. **Interrupt Controller Type Register, GICD_TYPER**
![](pic\08_Interrupt\41_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ----- | ------------- | ---- | ------------------------------------------------------------ |
| 15:11 | LSPI | R | 如果GIC实现了安全扩展则此字段的值是已实现的可锁定SPI的最大数量范围为00b00000到310b11111。 如果此字段为0b00000则GIC不会实现配置锁定。 如果GIC没有实现安全扩展则保留该字段。 |
| 10 | SecurityExtn | R | 表示GIC是否实施安全扩展 0未实施安全扩展 1实施了安全扩展 |
| 7:5 | CPUNumber | R | 表示已实现的CPU interfaces的数量。 已实现的CPU interfaces数量比该字段的值大1。 例如如果此字段为0b011则有四个CPU interfaces。 |
| 4:0 | ITLinesNumber | R | 表示GIC支持的最大中断数。 如果ITLinesNumber = N则最大中断数为32*(N+1)。 中断ID的范围是0到ID的数量 1。 例如0b00011最多128条中断线中断ID 0-127。 中断的最大数量为10200b11111。 无论此字段定义的中断ID的范围如何都将中断ID 1020-1023保留用于特殊目的 |
#### 3. **Distributor Implementer Identification Register, GICD_IIDR**
![](pic\08_Interrupt\42_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ----- | ----------- | ---- | ------------------------------------------------------------ |
| 31:24 | ProductID | R | 产品标识ID |
| 23:20 | 保留 | | |
| 19:16 | Variant | R | 通常是产品的主要版本号 |
| 15:12 | Revision | R | 通常此字段用于区分产品的次版本号 |
| 11:0 | Implementer | R | 含有实现这个GIC的公司的JEP106代码 [11:8]JEP106 continuation code对于ARM实现此字段为0x4 [7]始终为0 [6:0]实现者的JEP106code对于ARM实现此字段为0x3B |
#### 4. **Interrupt Group Registers, GICD_IGROUPRn**
![](pic\08_Interrupt\43_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ------------------ | ---- | ------------------------------------------------------------ |
| 31:0 | Group status bits | R/W | 组状态位,对于每个位: 0相应的中断为Group 0 1相应的中断为Group 1。 |
对于一个中断如何设置它的Group 首先找到对应的GICD_IGROUPRn寄存器即n是多少还要确定使用这个寄存器里哪一位。
对于interrtups ID m如下计算
```
n = m DIV 32GICD_IGROUPRn里的n就确定了
GICD_IGROUPRn在GIC内部的偏移地址是多少0x080+(4*n)
使用GICD_IPRIORITYRn中哪一位来表示interrtups ID m
bit = m mod 32。
```
#### 5. **Interrupt Set-Enable Registers, GICD_ISENABLERn**
![](pic\08_Interrupt\44_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ---------------- | ---- | ------------------------------------------------------------ |
| 31:0 | Set-enable bits | R/W | 对于SPI和PPI类型的中断每一位控制对应中断的转发行为从Distributor转发到CPU interface 读: 0表示当前是禁止转发的 1表示当前是使能转发的 写: 0无效 1使能转发 |
**对于一个中断如何找到GICD_ISENABLERn并确定相应的位**
```
对于interrtups ID m如下计算
n = m DIV 32GICD_ISENABLERn里的n就确定了
GICD_ISENABLERn在GIC内部的偏移地址是多少0x100+(4*n)
使用GICD_ISENABLERn中哪一位来表示interrtups ID m
bit = m mod 32。
```
#### 6. **Interrupt Clear-Enable Registers, GICD_ICENABLERn**
![](pic\08_Interrupt\45_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ------------------ | ---- | ------------------------------------------------------------ |
| 31:0 | Clear-enable bits | R/W | 对于SPI和PPI类型的中断每一位控制对应中断的转发行为从Distributor转发到CPU interface 读: 0表示当前是禁止转发的 1表示当前是使能转发的 写: 0无效 1禁止转发 |
对于一个中断如何找到GICD_ICENABLERn并确定相应的位
```
对于interrtups ID m如下计算
n = m DIV 32GICD_ICENABLERn里的n就确定了
GICD_ICENABLERn在GIC内部的偏移地址是多少0x180+(4*n)
使用GICD_ICENABLERn中哪一位来表示interrtups ID m
bit = m mod 32。
```
#### 7. **Interrupt Set-Active Registers, GICD_ISACTIVERn**
![](pic\08_Interrupt\46_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ---------------- | ---- | ------------------------------------------------------------ |
| 31:0 | Set-active bits | R/W | 读: 0表示相应中断不是active状态 1表示相应中断是active状态 写: 0无效 1把相应中断设置为active状态如果中断已处于Active状态则写入无效 |
对于一个中断如何找到GICD_ISACTIVERn并确定相应的位
```
对于interrtups ID m如下计算
n = m DIV 32GICD_ISACTIVERn里的n就确定了
GICD_ISACTIVERn在GIC内部的偏移地址是多少0x300+(4*n)
使用GICD_ISACTIVERn 中哪一位来表示interrtups ID m
bit = m mod 32。
```
#### 8. **Interrupt Clear-Active Registers, GICD_ICACTIVERn**
![](pic\08_Interrupt\47_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ---- | ------------------ | ---- | ------------------------------------------------------------ |
| 31:0 | Clear-active bits | R/W | 读: 0表示相应中断不是active状态 1表示相应中断是active状态 写: 0无效 1把相应中断设置为deactive状态如果中断已处于dective状态则写入无效 |
对于一个中断如何找到GICD_ICACTIVERn并确定相应的位
```
对于interrtups ID m如下计算
n = m DIV 32GICD_ICACTIVERn里的n就确定了
GICD_ICACTIVERn 在GIC内部的偏移地址是多少0x380+(4*n)
使用GICD_ICACTIVERn中哪一位来表示interrtups ID m
bit = m mod 32。
```
#### 9. **Interrupt Priority Registers, GICD_IPRIORITYRn**
![](pic\08_Interrupt\48_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ----- | ------------------------ | ---- | ------------------------------------------------------------ |
| 31:24 | Priority, byte offset 3 | R/W | 对于每一个中断都有对应的8位数据用来描述它的优先级。 每个优先级字段都对应一个优先级值,值越小,相应中断的优先级越高 |
| 23:16 | Priority, byte offset 2 | R/W | |
| 15:8 | Priority, byte offset 1 | R/W | |
| 7:0 | Priority, byte offset 0 | R/W | |
对于一个中断,如何设置它的优先级(Priority)首先找到对应的GICD_IPRIORITYRn寄存器即n是多少还要确定使用这个寄存器里哪一个字节。
```
对于interrtups ID m如下计算
n = m DIV 4GICD_IPRIORITYRn里的n就确定了
GICD_IPRIORITYRn在GIC内部的偏移地址是多少0x400+(4*n)
使用GICD_IPRIORITYRn中4个字节中的哪一个来表示interrtups ID m的优先级
byte offset = m mod 4。
byte offset 0对应寄存器里的[7:0]
byte offset 1对应寄存器里的[15:8]
byte offset 2对应寄存器里的[23:16]
byte offset 3对应寄存器里的[31:24]。
```
#### 10. **Interrupt Processor Targets Registers, GICD_ITARGETSRn**
![](pic\08_Interrupt\49_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ----- | -------------------------- | ---- | ------------------------------------------------------------ |
| 31:24 | CPU targets, byte offset 3 | R/W | 对于每一个中断都有对应的8位数据用来描述这个中断可以发给哪些CPU。 处理器编号从0开始8位数里每个位均指代相应的处理器。 例如值0x3表示将中断发送到处理器0和1。 当读取GICD_ITARGETSR0GICD_ITARGETSR7时读取里面任意字节返回的都是执行这个读操作的CPU的编号。 |
| 23:16 | CPU targets, byte offset 2 | R/W | |
| 15:8 | CPU targets, byte offset 1 | R/W | |
| 7:0 | CPU targets, byte offset 0 | R/W | |
对于一个中断如何设置它的目杯CPU优先级(Priority)首先找到对应的GICD_ITARGETSRn寄存器即n是多少还要确定使用这个寄存器里哪一个字节。
```
对于interrtups ID m如下计算
n = m DIV 4GICD_ITARGETSRn里的n就确定了
GICD_ITARGETSRn在GIC内部的偏移地址是多少0x800+(4*n)
使用GICD_ITARGETSRn中4个字节中的哪一个来表示interrtups ID m的目标CPU
byte offset = m mod 4。
byte offset 0对应寄存器里的[7:0]
byte offset 1对应寄存器里的[15:8]
byte offset 2对应寄存器里的[23:16]
byte offset 3对应寄存器里的[31:24]。
```
#### 11. **Interrupt Configuration Registers, GICD_ICFGRn**
![](pic\08_Interrupt\50_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------------- | --------------------- | ---- | ------------------------------------------------------------ |
| [2*F*+1:2*F*] | Int_config, field *F* | R/W | 对于每一个中断都有对应的2位数据用来描述它的边沿触发还是电平触发。 对于Int_config [1],即高位[2F + 1],含义为: 0相应的中断是电平触发 1相应的中断是边沿触发。 对于Int_config [0],即低位[2F],是保留位。 |
对于一个中断如何找到GICD_ICFGRn并确定相应的位域F
```
对于interrtups ID m如下计算
n = m DIV 16GICD_ICFGRn里的n就确定了
GICD_ICACTIVERn 在GIC内部的偏移地址是多少0xC00+(4*n)
F = m mod 16。
```
#### 12. **Identification registers: Peripheral ID2 Register, ICPIDR2**
![](pic\08_Interrupt\51_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------ | ------- | ---- | --------------------------------------------------------- |
| [31:0] | - | R/W | 由实现定义 |
| [7:4] | ArchRev | R | 该字段的值取决于GIC架构版本 0x1GICv1 0x2GICv2。 |
| [3:0] | - | R/W | 由实现定义 |
### 1.2.2 CPU interface寄存器描述
#### 1. **CPU Interface Control Register, GICC_CTLR**
此寄存器用来控制CPU interface传给CPU的中断信号。对于不同版本的GIC这个寄存器里各个位的含义大有不同。以GICv2为例有如下2种格式
![](pic\08_Interrupt\52_imx6ull_gic.png)
![](pic\08_Interrupt\53_imx6ull_gic.png)
以`GIC2 with Security Extensions, Non-secure copy` 为例GICC_CTLR中各个位的定义如下
| 位域 | 名 | 读写 | 描述 |
| ------- | ------------- | ---- | ------------------------------------------------------------ |
| [31:10] | - | | 保留 |
| [9] | EOImodeNS | R/W | 控制对GICC_EOIR和GICC_DIR寄存器的非安全访问 0GICC_EOIR具有降低优先级和deactivate中断的功能 对GICC_DIR的访问是未定义的。 1GICC_EOIR仅具有降低优先级功能 GICC_DIR寄存器具有deactivate中断功能。 |
| [8:7] | - | | 保留 |
| [6] | IRQBypDisGrp1 | R/W | 当CPU interface的IRQ信号被禁用时该位控制是否向处理器发送bypass IRQ信号 0将bypass IRQ信号发送给处理器 1将bypass IRQ信号不发送到处理器。 |
| [5] | FIQBypDisGrp1 | R/W | 当CPU interface的FIQ信号被禁用时该位控制是否向处理器发送bypass FIQ信号 0将bypass FIQ信号发送给处理器 1旁路FIQ信号不发送到处理器 |
| [4:1] | - | | 保留 |
| [0] | - | R/W | 使能CPU interface向连接的处理器发出的组1中断的信号: 0禁用中断信号 1使能中断信号 |
#### 2. **Interrupt Priority Mask Register, GICC_PMR**
提供优先级过滤功能优先级高于某值的中断才会发送给CPU。
![](pic\08_Interrupt\54_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------ | ---- | ---- | ------------------------------------- |
| [31:8] | - | | 保留 |
| [7:0] | - | R/W | 优先级高于这个值的中断才会发送给CPU |
`[7:0]`共8位可以表示256个优先级。但是某些芯片里的GIC支持的优先级少于256个则某些位为RAZ / WI如下所示
```
如果有128个级别则寄存器中bit[0] = 0b0即使用[7:1]来表示优先级;
如果有64个级别则寄存器中bit[1:0] = 0b00即使用[7:2]来表示优先级;
如果有32个级别则寄存器中bit[2:0] = 0b000即使用[7:3]来表示优先级;
如果有16个级别则寄存器中bit[3:0] = 0b0000即使用[7:4]来表示优先级;
```
注意:**imx6ull最多为32个级别**
#### 3. **Binary Point Register, GICC_BPR**
此寄存器用来把8位的优先级字段拆分为组优先级和子优先级组优先级用来决定中断抢占。
![](pic\08_Interrupt\55_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------ | ------------- | ---- | ------------------------------------------------------------ |
| [31:3] | - | | 保留 |
| [2:0] | Binary point | R/W | 此字段的值控制如何将8bit中断优先级字段拆分为组优先级和子优先级组优先级用来决定中断抢占。 更多信息还得看看GIC手册。 |
#### 4. **Interrupt Acknowledge Register, GICC_IAR**
CPU读此寄存器获得当前中断的interrtup ID。
![](pic\08_Interrupt\56_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------- | ------------ | ---- | ------------------------------------------------------------ |
| [31:13] | - | | 保留 |
| [12:10] | CPUID | R | 对于SGI类中断它表示谁发出了中断。例如值为3表示该请求是通过对CPU interface 3上的GICD_SGIR的写操作生成的。 |
| [9:0] | Interrupt ID | R | 中断ID |
#### 5. **Interrupt Register, GICC_EOIR**
写此寄存器表示某中断已经处理完毕。GICC_IAR的值表示当前在处理的中断把GICC_IAR的值写入GICC_EOIR就表示中断处理完了。
![](pic\08_Interrupt\57_imx6ull_gic.png)
| 位域 | 名 | 读写 | 描述 |
| ------- | -------- | ---- | ---------------------------------------------- |
| [31:13] | - | | 保留 |
| [12:10] | CPUID | W | 对于SGI类中断它的值跟GICD_IAR. CPUID的相同。 |
| [9:0] | EOIINTID | W | 中断ID它的值跟GICD_IAR里的中断ID相同 |
## 1.3 GIC编程
使用cortex A7处理器的芯片一般都是使用GIC v2的中断控制器。
处理GIC的基地址不一样外对GIC的操作都是一样的。
在NXP官网可以找到[IMX6ULL的SDK包](https://www.nxp.com.cn/products/processors-and-microcontrollers/arm-processors/i-mx-applications-processors/i-mx-6-processors/i-mx-6ull-single-core-processor-with-arm-cortex-a7-core:i.MX6ULL?tab=Design_Tools_Tab)。
下载后可以参考这个文件core_ca7.h里面含有GIC的初始化代码。
```
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,116 @@
#include "gic.h"
GIC_Type * get_gic_base(void)
{
GIC_Type *dst;
__asm volatile ("mrc p15, 4, %0, c15, c0, 0" : "=r" (dst));
return dst;
}
void gic_init(void)
{
u32 i, irq_num;
GIC_Type *gic = get_gic_base();
/* the maximum number of interrupt IDs that the GIC supports */
/* 读出GIC支持的最大的中断号 */
/* 注意: 中断个数 = irq_num * 32 */
irq_num = (gic->D_TYPER & 0x1F) + 1;
/* Disable all PPI, SGI and SPI */
/* 禁止所有的PPI、SIG、SPI中断 */
for (i = 0; i < irq_num; i++)
gic->D_ICENABLER[i] = 0xFFFFFFFFUL;
/* all set to group0 */
/* 这些中断, 都发给group0 */
for (i = 0; i < irq_num; i++)
gic->D_IGROUPR[i] = 0x0UL;
/* all spi interrupt target for cpu interface 0 */
/* 所有的SPI中断都发给cpu interface 0 */
for (i = 32; i < (irq_num << 5); i++)
gic->D_ITARGETSR[i] = 0x01UL;
/* all spi is level sensitive: 0-level, 1-edge */
/* it seems level and edge all can work */
/* 设置GIC内部的中断触发类型 */
for (i = 2; i < irq_num << 1; i++)
gic->D_ICFGR[i] = 0x01010101UL;
/* The priority mask level for the CPU interface. If the priority of an
* interrupt is higher than the value indicated by this field,
* the interface signals the interrupt to the processor.
*/
/* 把所有中断的优先级都设为最高 */
gic->C_PMR = (0xFFUL << (8 - 5)) & 0xFFUL;
/* No subpriority, all priority level allows preemption */
/* 没有"次级优先级" */
gic->C_BPR = 7 - 5;
/* Enables the forwarding of pending interrupts from the Distributor to the CPU interfaces.
* Enable group0 distribution
*/
/* 使能: Distributor可以给CPU interfac分发中断 */
gic->D_CTLR = 1UL;
/* Enables the signaling of interrupts by the CPU interface to the connected processor
* Enable group0 signaling
*/
/* 使能: CPU interface可以给processor分发中断 */
gic->C_CTLR = 1UL;
}
void gic_enable_irq(uint32_t nr)
{
GIC_Type *gic = get_gic_base();
/* The GICD_ISENABLERs provide a Set-enable bit for each interrupt supported by the GIC.
* Writing 1 to a Set-enable bit enables forwarding of the corresponding interrupt from the
* Distributor to the CPU interfaces. Reading a bit identifies whether the interrupt is enabled.
*/
gic->D_ISENABLER[nr >> 5] = (uint32_t)(1UL << (nr & 0x1FUL));
}
void gic_disable_irq(uint32_t nr)
{
GIC_Type *gic = get_gic_base();
/* The GICD_ICENABLERs provide a Clear-enable bit for each interrupt supported by the
* GIC. Writing 1 to a Clear-enable bit disables forwarding of the corresponding interrupt from
* the Distributor to the CPU interfaces. Reading a bit identifies whether the interrupt is enabled.
*/
gic->D_ICENABLER[nr >> 5] = (uint32_t)(1UL << (nr & 0x1FUL));
}
int get_gic_irq(void)
{
int nr;
GIC_Type *gic = get_gic_base();
/* The processor reads GICC_IAR to obtain the interrupt ID of the
* signaled interrupt. This read acts as an acknowledge for the interrupt
*/
nr = gic->C_IAR;
return nr;
}
int clear_gic_irq(int nr)
{
GIC_Type *gic = get_gic_base();
/* write GICC_EOIR inform the CPU interface that it has completed
* the processing of the specified interrupt
*/
gic->C_EOIR = nr;
}

View File

@@ -0,0 +1,114 @@
#ifndef __GIC_H__
#define __GIC_H__
/*form core_ca7.h*/
#define __I volatile const /*!< defines 'read only' permissions */
#define __O volatile /*!< defines 'write only' permissions*/
#define __IO volatile /*!< defines 'read / write' permissions*/
/* form core_ca7.h, following defines should be used for structure members */
#define __IM volatile const /*! Defines 'read only' structure member permissions */
#define __OM volatile /*! Defines 'write only' structure member permissions */
#define __IOM volatile /*! Defines 'read / write' structure member permissions */
typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long long s64;
typedef unsigned long long u64;
typedef u8 uint8_t;
typedef s8 int8_t;
typedef u16 uint16_t;
typedef s16 int16_t;
typedef u32 uint32_t;
typedef s32 int32_t;
typedef u64 uint64_t;
typedef s64 int64_t;
typedef struct
{
uint32_t RESERVED0[1024];
__IOM uint32_t D_CTLR; /*!< Offset: 0x1000 (R/W) Distributor Control Register */
__IM uint32_t D_TYPER; /*!< Offset: 0x1004 (R/ ) Interrupt Controller Type Register */
__IM uint32_t D_IIDR; /*!< Offset: 0x1008 (R/ ) Distributor Implementer Identification Register */
uint32_t RESERVED1[29];
__IOM uint32_t D_IGROUPR[16]; /*!< Offset: 0x1080 - 0x0BC (R/W) Interrupt Group Registers */
uint32_t RESERVED2[16];
__IOM uint32_t D_ISENABLER[16]; /*!< Offset: 0x1100 - 0x13C (R/W) Interrupt Set-Enable Registers */
uint32_t RESERVED3[16];
__IOM uint32_t D_ICENABLER[16]; /*!< Offset: 0x1180 - 0x1BC (R/W) Interrupt Clear-Enable Registers */
uint32_t RESERVED4[16];
__IOM uint32_t D_ISPENDR[16]; /*!< Offset: 0x1200 - 0x23C (R/W) Interrupt Set-Pending Registers */
uint32_t RESERVED5[16];
__IOM uint32_t D_ICPENDR[16]; /*!< Offset: 0x1280 - 0x2BC (R/W) Interrupt Clear-Pending Registers */
uint32_t RESERVED6[16];
__IOM uint32_t D_ISACTIVER[16]; /*!< Offset: 0x1300 - 0x33C (R/W) Interrupt Set-Active Registers */
uint32_t RESERVED7[16];
__IOM uint32_t D_ICACTIVER[16]; /*!< Offset: 0x1380 - 0x3BC (R/W) Interrupt Clear-Active Registers */
uint32_t RESERVED8[16];
__IOM uint8_t D_IPRIORITYR[512]; /*!< Offset: 0x1400 - 0x5FC (R/W) Interrupt Priority Registers */
uint32_t RESERVED9[128];
__IOM uint8_t D_ITARGETSR[512]; /*!< Offset: 0x1800 - 0x9FC (R/W) Interrupt Targets Registers */
uint32_t RESERVED10[128];
__IOM uint32_t D_ICFGR[32]; /*!< Offset: 0x1C00 - 0xC7C (R/W) Interrupt configuration registers */
uint32_t RESERVED11[32];
__IM uint32_t D_PPISR; /*!< Offset: 0x1D00 (R/ ) Private Peripheral Interrupt Status Register */
__IM uint32_t D_SPISR[15]; /*!< Offset: 0x1D04 - 0xD3C (R/ ) Shared Peripheral Interrupt Status Registers */
uint32_t RESERVED12[112];
__OM uint32_t D_SGIR; /*!< Offset: 0x1F00 ( /W) Software Generated Interrupt Register */
uint32_t RESERVED13[3];
__IOM uint8_t D_CPENDSGIR[16]; /*!< Offset: 0x1F10 - 0xF1C (R/W) SGI Clear-Pending Registers */
__IOM uint8_t D_SPENDSGIR[16]; /*!< Offset: 0x1F20 - 0xF2C (R/W) SGI Set-Pending Registers */
uint32_t RESERVED14[40];
__IM uint32_t D_PIDR4; /*!< Offset: 0x1FD0 (R/ ) Peripheral ID4 Register */
__IM uint32_t D_PIDR5; /*!< Offset: 0x1FD4 (R/ ) Peripheral ID5 Register */
__IM uint32_t D_PIDR6; /*!< Offset: 0x1FD8 (R/ ) Peripheral ID6 Register */
__IM uint32_t D_PIDR7; /*!< Offset: 0x1FDC (R/ ) Peripheral ID7 Register */
__IM uint32_t D_PIDR0; /*!< Offset: 0x1FE0 (R/ ) Peripheral ID0 Register */
__IM uint32_t D_PIDR1; /*!< Offset: 0x1FE4 (R/ ) Peripheral ID1 Register */
__IM uint32_t D_PIDR2; /*!< Offset: 0x1FE8 (R/ ) Peripheral ID2 Register */
__IM uint32_t D_PIDR3; /*!< Offset: 0x1FEC (R/ ) Peripheral ID3 Register */
__IM uint32_t D_CIDR0; /*!< Offset: 0x1FF0 (R/ ) Component ID0 Register */
__IM uint32_t D_CIDR1; /*!< Offset: 0x1FF4 (R/ ) Component ID1 Register */
__IM uint32_t D_CIDR2; /*!< Offset: 0x1FF8 (R/ ) Component ID2 Register */
__IM uint32_t D_CIDR3; /*!< Offset: 0x1FFC (R/ ) Component ID3 Register */
__IOM uint32_t C_CTLR; /*!< Offset: 0x2000 (R/W) CPU Interface Control Register */
__IOM uint32_t C_PMR; /*!< Offset: 0x2004 (R/W) Interrupt Priority Mask Register */
__IOM uint32_t C_BPR; /*!< Offset: 0x2008 (R/W) Binary Point Register */
__IM uint32_t C_IAR; /*!< Offset: 0x200C (R/ ) Interrupt Acknowledge Register */
__OM uint32_t C_EOIR; /*!< Offset: 0x2010 ( /W) End Of Interrupt Register */
__IM uint32_t C_RPR; /*!< Offset: 0x2014 (R/ ) Running Priority Register */
__IM uint32_t C_HPPIR; /*!< Offset: 0x2018 (R/ ) Highest Priority Pending Interrupt Register */
__IOM uint32_t C_ABPR; /*!< Offset: 0x201C (R/W) Aliased Binary Point Register */
__IM uint32_t C_AIAR; /*!< Offset: 0x2020 (R/ ) Aliased Interrupt Acknowledge Register */
__OM uint32_t C_AEOIR; /*!< Offset: 0x2024 ( /W) Aliased End Of Interrupt Register */
__IM uint32_t C_AHPPIR; /*!< Offset: 0x2028 (R/ ) Aliased Highest Priority Pending Interrupt Register */
uint32_t RESERVED15[41];
__IOM uint32_t C_APR0; /*!< Offset: 0x20D0 (R/W) Active Priority Register */
uint32_t RESERVED16[3];
__IOM uint32_t C_NSAPR0; /*!< Offset: 0x20E0 (R/W) Non-secure Active Priority Register */
uint32_t RESERVED17[6];
__IM uint32_t C_IIDR; /*!< Offset: 0x20FC (R/ ) CPU Interface Identification Register */
uint32_t RESERVED18[960];
__OM uint32_t C_DIR; /*!< Offset: 0x3000 ( /W) Deactivate Interrupt Register */
} GIC_Type;
void gic_init(void);
void gic_enable_irq(unsigned int nr);
void gic_disable_irq(unsigned int nr);
int get_gic_irq(void);
int clear_gic_irq(int nr);
#endif