1. Basic concepts
GIC (Generic Interrupt Controller) is used to manage interrupts; in R52, the GIC version is GIC v3, which supports the following functions:
- Interrupt priority management
- Route interruption to core or output to port
- Interrupt preemption
- Disrupt virtualization
In R52, GIC has the following composition:
- A GIC Distributor, each Distributor has a Redistributor corresponding to each core;
- One CPU interface corresponds to each core
To sum up, it is assumed that the current R52 uses the highest configuration and has 4 cores. Note that they are all single-core clusters. This processor contains 1 Distributor, 4 Redistributors, and 4 CPU interfaces. The structure is as follows:
So for a system, there is usually only one GIC, and the memory map of this GIC is as follows:
For the interface with the CPU, each core can only be configured individually, and a coprocessor must be used for configuration. as follows:
What does each of these parts do? According to the Arm® Generic Interrupt Controller Architecture Specification
GIC architecture version 3 and version 4 description, we further analyze:
As you can see in the picture above, a total of SPI, PPI, SGI and LPI appear for GIC. Let’s first look at their respective paths:
- SPI is distributed to different Redistributors through Distributor, and then distributed to CPU interface by redistributor, and finally handed over to PE (Process element) for processing;
- PPI is distributed directly to the redistributor;
- SGI is generated by a PE and is directly transmitted to the Distributor and distributed to different PEs;
- LPI specifies routing to a certain PE based on affinity, which is not currently used in R52;
Now that we understand the path, let’s look at the abbreviations of these nouns,
noun | explain | ID in R52 | Remark |
SPI | Shared Peripheral Interrupt | INTID32(SPI[0])-INTID991(SPI[959]) (Up to 960SPI can be configured) Rising edge trigger or high level trigger |
There is a hardware mechanism in R52 that can route SPI to other PEs with extremely low latency; INTID(32x+32)~INTID(32x+63) For example, x=1, then for core1, there are SPIs of INTID64~95 which are low-latency routes. |
PPI | Private Peripheral Interrupts | INTID16~INTID31 for each core Rising edge trigger or low level trigger |
1. It means that each core has its own INTID16-31 PPI 2. PPI has a fixed allocation, as follows Unallocated PPI can be triggered as SPI |
SGI | Software Generated Interrupts | INTID0~INTID15 for each core Software triggers edge triggering similar to peripheral interrupts |
Generate an interrupt by writing to the SGI generation system register |
Through the above description, assuming that there are 4 cores now, each core has 16 SGIs, 16 PPIs, and SPI960 shares at the same time, of which INTID (32x+32)~INTID (32x+63) are low for the corresponding cores delayed.
2. First introduction to GIC registers
We learned above that GIC consists of three parts, so each corresponding part should have registers to configure, especially the functions of GIC. So let's look at the GIC structure and see which registers need to be processed.
2.1 Distributor
In R52 TRM, the abbreviation of Distributor related registers is GICD_xxx, and the ones belonging to RW type are:
name | describe | effect |
GICD_CTLR 0x0000 |
Distributor Control Register | 1. Enable Group0 and Group1 In R52, Group0 handles FIQ and Group1 handles IRQ 2. CICD_CTLR and ICENABLER status register write operation pending instructions |
GICD_IGROUPR1-30 0x0084-0x00F8 |
Interrupt Group Register |
Used to control whether SPI belongs to Group0 or Group1 Total 30 registers, 30*32 = 960 |
GICD_ISENABLER1-30 0x0104-0x0178 |
Interrupt Set-Enable Registers | Enable SPI routing function. It is turned off by default, so it must be turned on during configuration. Write 1 to open, write 0 to have no effect Reading out 0 means it is closed. Reading out 1 means it is enabled. |
GICD_ICENABLER1-30 0x0184-0x01F8 |
Interrupt Clear-Enable Registers | Turn off SPI routing function, Write 1 to turn off, write 0 to have no effect Reading out 0 means closed, reading out 1 means open. |
GICD_ISPENDR1-30 0x0204-0x0278 |
Interrupt Set-Pending Registers | Set the pending flag of SPI (need to understand the life cycle of an interrupt) Writing 1 sets pending, writing 0 has no effect. Reading 1 means pending, reading 0 means there is no pending |
GICD_ICPENDR1-30 0x0284-0x02F8 |
Interrupt Clear-Pending Registers | Clear SPI pending flag As above |
GICD_ISACTIVER1-30 0x0304-0x0378 |
Interrupt Set-Active Registers | Set the SPI activation flag As above |
GICD_ICACTIVER1-30 0x0384-0x03F8 |
Interrupt Clear-Active Registers | Clear the active flag of the SPI As above |
GICD_IPRIORITYR8-247 0x0420-0x07DF |
Interrupt Priority Registers | Set the priority of SPI, the priority of 5-bit bit field (0-31). The smaller the value, the higher the priority. For interrupts of the same priority, the lower the ID, the earlier it will be processed. 一共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的偏移是基于这个基地址来的。