一、基本概念
GIC(Generic Interrupt Controller)用于管理中断;在R52中,GIC版本为GIC v3,支持如下功能:
- 中断优先级管理
- 路由中断到core 或者输出到port
- 中断抢占
- 中断虚拟化
在R52中,GIC有如下构成:
- 一个GIC Distributor,每个Distributor中有一个Redistributor对应每个核;
- 一个CPU interface对应每个核
总结下来,即假设当前R52使用最高配置,有4个核,注意都是单核簇。这个处理器就包含了1个Distributor、4个Redistributor、4个CPU interface,结构如下:
所以对于一个系统来说,通常只有一个GIC,这个GIC的memory map如下:
针对与CPU interface,只能是每个核单独去配置,必须使用协处理器进行配置。如下:
这每个部件是干啥的呢?根据Arm® Generic Interrupt Controller Architecture Specification
GIC architecture version 3 and version 4描述,我们进一步分析:
上图可以看到,总共出现了SPI、PPI、SGI和LPI给了GIC,我们首先看下各自路径:
- SPI通过Distributor分发给不同的Redistributor,再由redistributor分发给CPU interface,最后交由PE(Process element)处理;
- PPI直接交由redistributor分发;
- SGI是由一个PE产生直接透传给Distributor,分发到不同的PE里去;
- LPI 根据亲和度指定路由到某个PE上,R52中暂时没有用到;
了解了路径,我们来看这些名词的缩写,
名词 | 解释 | 在R52中的ID | 备注 |
SPI | Shared Peripheral Interrupt | INTID32(SPI[0])-INTID991(SPI[959]) (最多960SPI可配) 上升沿触发 or 高电平触发 |
在R52有种硬件机制可以极低延迟将SPI路由到其他PE;INTID(32x+32)~INTID(32x+63) 例如,x=1,那么对于core1,就有INTID64~95的SPI是低延迟的路由 |
PPI | Private Peripheral Interrupts | INTID16~INTID31每个核 上升沿触发 or 低电平触发 |
1、意味着每个核都有自己的INTID16-31 PPI 2、PPI有固定分配,如下 未分配的PPI可作为SPI的进行触发 |
SGI | Software Generated Interrupts | INTID0~INTID15每个核 软件触发类似外设中断的边沿触发 |
通过写SGI生成系统寄存器产生中断 |
通过上述描述,假设现在有4个核,那么每个核都有16个SGI、16个PPI,同时SPI960个共享,其中INTID(32x+32)~INTID(32x+63)对于对应核都是低延迟的。
二、GIC寄存器初识
上面我们了解到GIC由三大部分构成,那么对应的每个部分应该都有寄存器要配置,特别是GIC的功能。那么我们根据GIC结构来看,有哪些寄存器要处理。
2.1 Distributor
在R52 TRM里,Distributor 的相关寄存器简写为GICD_xxx,属于RW类型的有:
名字 | 描述 | 作用 |
GICD_CTLR 0x0000 |
Distributor Control Register | 1、使能Group0、Group1 在R52中,Group0 处理 FIQ,Group1处理IRQ 2、CICD_CTLR和ICENABLER状态寄存器写操作pending指示 |
GICD_IGROUPR1-30 0x0084-0x00F8 |
Interrupt Group Register |
用于控制SPI属于Group0还是Group1 总共30个寄存器,30*32 = 960 |
GICD_ISENABLER1-30 0x0104-0x0178 |
Interrupt Set-Enable Registers | 使能SPI路由功能,默认是关闭的,因此配置时要打开, 写1打开,写0无效 读出得到0表示关闭 读出得到1表示使能 |
GICD_ICENABLER1-30 0x0184-0x01F8 |
Interrupt Clear-Enable Registers | 关闭SPI路由功能, 写1关闭,写0无效 读出得到0表示关闭,读出得到1表示打开 |
GICD_ISPENDR1-30 0x0204-0x0278 |
Interrupt Set-Pending Registers | 设置SPI的pending标志位(需要了解一个中断的生命周期) 写1设置pending,写0无效 读1表示pending,读0表示没有pending |
GICD_ICPENDR1-30 0x0284-0x02F8 |
Interrupt Clear-Pending Registers | 清除SPI pending标志 方式如上 |
GICD_ISACTIVER1-30 0x0304-0x0378 |
Interrupt Set-Active Registers | 设置SPI的激活标志 方式如上 |
GICD_ICACTIVER1-30 0x0384-0x03F8 |
Interrupt Clear-Active Registers | 清除SPI的激活标志 方式如上 |
GICD_IPRIORITYR8-247 0x0420-0x07DF |
Interrupt Priority Registers | 设置SPI的优先级,5bit位域的优先级(0-31),值越小优先级越高,相同优先级的中断,越低的ID越先处理 一共240个寄存器,一个寄存器可以配置4个SPI的优先级,依次对应;例如reg8对应SPI32、33、34、35的优先级配置 |
GICD_ICFGR2-61 0x0C08-0x0CF4 |
Interrupt Configuration Registers | 配置SPI触发方式,2bit,高位有效,地位保留 0x :高电平触发 1x:上升沿触发 60*16 = 960个中断配置 |
GICD_IROUTER32-991 0x6100-0x7EF8 |
Interrupt Routing Registers | 根据affinity提供路由信息 |
上面这个表,很晕,主要还是因为有960个SPI,对每个SPI的配置需要多个寄存器来完成,例如优先级、中断激活、pending、使能等,最重要的还有一个路由,我们可以看到,路由寄存器总共有960个,意味着每个SPI都有一个寄存器来处理。
除此之外,我们可以看到寄存器之间的memmap不是连续的,例如GICD_ISENABLER寄存器是从1开始的,那GICD_ISENABLER0是表示啥呢?思考一下,这里面还有SGI和PPI的配置啊,由于GICD没办法对Redistributor操作,因此,在TRM Redistributor那一章,我们可以看到GICR_ISENABLER0的地址为gicr_base + 0x0100。而这里地址不连续是为了代码中更方便定位寄存器?
2.2 Redistributor
Redistributor在系统里是全局的,意味着在core0的程序里可以配置core1的PPI使能。
同理,我们来以Redistributor0为例,来看哪些寄存器可以配置
名字 | 描述 | 作用 |
GICR_WAKER 0x0014 |
Redistributor Wake Register | 1、设置Redistributor的休眠唤醒, bit2:ChildrenAsleep:0没有休眠,1休眠,默认值 bit1:ProcessorSleep:写0 关闭休眠状态,影响bit2;写1进入procesor sleep,默认值 |
GICD_IGROUPR0 0x0080 |
Interrupt Group Register |
用于控制SGI、PPI属于Group0还是Group1 总共1个寄存器,32bit 对应32个中断 |
GICD_ISENABLER0 0x0100 |
Interrupt Set-Enable Registers | 使能SPI路由功能,默认是关闭的,因此配置时要打开, 写1打开,写0无效 读出得到0表示关闭 读出得到1表示使能 |
GICD_ICENABLER0 0x0180 |
Interrupt Clear-Enable Registers | 关闭SPI路由功能, 写1关闭,写0无效 读出得到0表示关闭,读出得到1表示打开 |
GICD_ISPENDR0 0x0200 |
Interrupt Set-Pending Registers | 设置SPI的pending标志位(需要了解一个中断的生命周期) 写1设置pending,写0无效 读1表示pending,读0表示没有pending |
GICD_ICPENDR0 0x0280 |
Interrupt Clear-Pending Registers | 清除SPI pending标志 方式如上 |
GICD_ISACTIVER0 0x0300 |
Interrupt Set-Active Registers | 设置SPI的激活标志 方式如上 |
GICD_ICACTIVER0 0x0380 |
Interrupt Clear-Active Registers | 清除SPI的激活标志 方式如上 |
GICD_IPRIORITYR 0x0400-0x041C |
Interrupt Priority Registers | 设置SPI的优先级,5bit位域的优先级(0-31),值越小优先级越高,相同优先级的中断,越低的ID越先处理 一共8个寄存器,一个寄存器可以配置4个SPI的优先级,依次对应 |
GICD_ICFGR 0x0C00 |
Interrupt Configuration Registers | 配置SPI触发方式,2bit,高位有效,地位保留 0x :高电平触发 1x:上升沿触发 |
GICD_ICFGR1 0x0C04 |
Interrupt Configuration Registers | 配置SPI触发方式,2bit,高位有效,地位保留 0x :高电平触发 1x:上升沿触发 |
2.3 CPU interface
cpu interface的寄存器是每个核的,从memmap上是看不到地址的,因此用协处理器进行访问
这个太多了,就不多说了,看TRM吧
三、总结
GIC对于软件开发的难点在于理解GICD\GICR之间的映射关系;例如GICD有很多寄存器地址是不连续的,如果看GICR的寄存器地址就很容易误解GICR寄存器穿插在GICD里,其实不然,GICR在GIC的memmap中共有5个,分别不同的基地址,而GICR的偏移是基于这个基地址来的。