本篇参考 arm 官网公开材料 《Arm® Generic Interrupt Controller
Architecture SpecificationGIC architecture version 3 and version 4》学习笔记及https://zhuanlan.zhihu.com/p/259854898 卢俊
armv8-armv9中断系列详解-硬件基础篇
armv8/armv9中断系列详解-中断示例展示
armv8/armv9不同特权程序之间的跳转模型
1 About the Generic Interrupt Controller (GIC)
The GIC is an architected resource that supports and controls interrupts. It provides:
•Registers for managing interrupt sources, interrupt behavior, and the routing of interrupts to one or more PEs.
•Support for:
—The Armv8 architecture.
—Locality-specific Peripheral Interrupts (LPIs).
—Private Peripheral Interrupts (PPIs).
—Software Generated Interrupts (SGIs).
—Shared Peripheral Interrupts (SPIs).
—Interrupt masking and prioritization.
—Uniprocessor and multiprocessor systems.
—Wakeup events in power management environments.
For each PE, the GIC architecture describes how IRQ and FIQ interrupts can be generated from different types of interrupts within the system. The Armv8-A Exception model describes how the PE handles these IRQ and FIQ interrupts
1.1 GIC 的核心功能
- 对 SOC 中外设的中断源的管理,并且提供给软件配置以及控制这些中断源。
- 当对应的中断源有效时,GIC 根据该中断源的配置,决定是否将该中断信号发送给 CPU。如果有多个中断源有效,那么 GIC 还会进行仲裁,选择最高优先级中断,发送给 CPU。
- 当 CPU 接受到 GIC 发送的中断,通过读取 GIC 的寄存器,就可以知道,中断的来源来自于哪里,从而可以做相应的处理。
- 当 CPU 处理完中断之后,会告诉 GIC ,其实就是访问 GIC 的寄存器,该中断处理完毕。 GIC 接受到该信息后,就将该中断源取消,避免又重新发送该中断给 CPU 以及允许中断抢占。
1.2 中断状态
对于每一个中断而言,有以下4个状态:
inactive: 中断处于无效状态
pending:中断处于有效状态,但是cpu没有响应该中断
active: cpu在响应该中断
active and pending:cpu在响应该中断,但是该中断源又发送中断过来
1.3 中断触发方式
中断触发方式,包含以下两种方式:
edge-triggered: 边沿触发,当中断源产生一个边沿,中断有效
level-sensitive:电平触发,当中断源为指定电平,中断有效
1.4 中断类型
中断类型分为以下几类:
1) PPI:(private peripheral interrupt),私有外设中断,该中断来源于外设,但是该中断只对指定的core有效。
2) SPI:(shared peripheral interrupt),共享外设中断,该中断来源于外设,但是该中断可以对所有的core有效。
3) SGI:(software-generated interrupt),软中断,软件产生的中断,用于给其他的core发送中断信号
4) virtual interrupt: 虚拟中断,用于支持虚拟机
1.5 中断优先级
因为soc中,中断有很多,为了方便对中断的管理,对每个中断,附加了中断优先级。在中断仲裁时,高优先级的中断,会优于低优先级的中断,发送给cpu处理。
当cpu在响应低优先级中断时,如果此时来了高优先级中断,那么高优先级中断会抢占低优先级中断,而被处理器响应。
1.6 中断号
为了方便对中断的管理,gic为每个中断,分配了一个中断号,也就是interrupt ID。对于中断号,gic也进行了分配:
ID0-ID15, 分配给SGI
ID16-ID31, 分配给PPI
ID32-ID1019: 分配给SPI
其他
在具体的arm的cpu中,对于PPI,又进行了详细的分配。这个,就得看arm cpu的TRM了。
1.7 中断生命周期
一个中断,是有生命周期的。以下是流程图:
start: 中断开始
generate: 中断源产生中断,发送给gic
deliver: gic将中断发送给cpu
activate: cpu响应该中断
deactivate: cpu响应完中断,告诉gic,中断处理完毕,gic更新该中断状态
end: 中断结束
1.8 banking
banking功能,包括以下两个:
1、中断banking
对于PPI和SGI,gic可以有多个中断对应于同一个中断号。比如在soc中,有多个外设的中断,共享同一个中断号。
2、寄存器banking
对于同一个gic寄存器地址,在不同的情况下,访问的是不同的寄存器。例如在secure和non-secure状态下,访问同一个gic寄存器,其实是访问的不同的gic的寄存器。
1.9 中断分组
givc2,将中断,分成了group0和group1。使用寄存器GICD_IGROUPRn来对每个中断,设置组。
group0:安全中断, 由nFIQ驱动
group1:非安全中断,由nIRQ驱动
gicv2,支持最大1020个中断。其中断号分配如下
2 GIC 结构
GIC 主要包括以下两个组件:
distributor
cpu interface
2.1 distributor
中断分发器,用来收集所有的中断来源,并且为每个中断源设置中断优先级,中断分组,中断目的core。当有中断产生时,将当前最高优先级中断,发送给对应的cpu interface。
distributor 对中断提供以下的功能:
全局中断使能
每个中断的使能
中断的优先级
中断的分组
中断的目的core
中断触发方式
对于SGI中断,传输中断到指定的core
每个中断的状态管理
提供软件,可以修改中断的pending状态
2.2 cpu interface
cpu interface,将GICD发送的中断信息,通过IRQ,FIQ管脚,发送给连接到该 cpu 接口的core。
cpu interface提供了一下的功能:
将中断请求发送给cpu
对中断进行认可(acknowledging an interrupt)
中断完成识别(indicating completion of an interrupt)
设置中断优先级屏蔽
定义中断抢占策略
决定当前处于pending状态最高优先级中断
2.3 中断认可
中断认可,是指cpu响应该中断。此时中断状态从pending状态,变为active状态。通过访问GICC_IAR寄存器,来对中断进行认可。
GICC_IAR: 认可group0的中断
GICC_AIAR: 认可group1的中断
2.4 中断完成
中断完成,是指cpu处理完中断。此时中断状态从active状态,变为inactive状态。gic中,对中断完成,定义了以下两个stage:
优先级重置(priority drop):将当前中断屏蔽的最高优先级进行重置,以便能够响应低优先级中断。group0中断,通过写GICC_EOIR寄存器,来实现优先级重置,group1中断,通过写 GICC_AEOIR 寄存器,来实现优先级重置。
中断无效(interrupt deactivation):将中断的状态,设置为inactive状态。通过写 GICC_DIR 寄存器,来实现中断无效。
2.5 bypass功能
gicv2支持bypass功能,这样gic就不起作用了,core的中断管脚,直接由soc的其他部门信号驱动。
通过控制 GICC_CLTR 寄存器的一些比特位,来实现bypass功能。不过这个功能一般不使用,不然何必要在arm的soc中,加入gic呢?
2.6 中断处理流程
中断处理流程,包含了以下几步:
1)GIC决定每个中断的使能状态,不使能的中断,是不能发送中断的
2)如果某个中断的中断源有效,GIC将该中断的状态设置为pending状态,然后判断该中断的目标core
3)对于每一个core,GIC将当前处于pending状态的优先级最高的中断,发送给该core的cpu interface
4)cpu interface接收GIC发送的中断请求,判断优先级是否满足要求,如果满足,就将中断通过nFIQ或nIRQ管脚,发送给core。
5)core响应该中断,通过读取 GICC_IAR 寄存器,来认可该中断。读取该寄存器,如果是软中断,返回源处理器ID,否则返回中断号。
6)当core认可该中断后,GIC将该中断的状态,修改为active状态
7)当core完成该中断后,通过写 EOIR (end of interrupt register)来实现优先级重置,写 GICC_DIR 寄存器,来无效该中断
2.7 中断使能和禁止
通过设置GICD_ISENABLERn寄存器,来使中断使能,通过设置GICD_ICENABLERn寄存器,来使中断禁止。
这两个寄存器,都是bit有效的寄存器,也就是一个bit,关联一个中断
2.8 中断pending
通过设置GICD_ISPENDRn或GICD_ICPENDRn寄存器,可以读取和修改中断的pending状态。这两个寄存器,也是bit有效的寄存器,一个bit,关联一个中断。
2.9 中断active
通过设置GICD_ISACTIVERn或GICD_ICACTIVERn寄存器,可以读取和修改中断的active状态。这两个寄存器,也是bit有效的寄存器,一个bit,关联一个中断。
2.10 产生软中断
通过写 GICD_SGIR 寄存器,来产生软中断。软中断,可以指定产生中断,发往执行的core,也可以发送多个core。
对于软中断,这个是软件产生的中断。比如软件,想给执行自己的core,发送一个中断,就可以通过软中断来产生。或者软件,想起其他的core,发送一个中断,也可以通过软中断来产生
2.11 中断优先级
gicv2,支持最小16个,最大256个中断优先级,如下图所示:
如果实现的中断优先级小于256个,那么最低的几个bit,是为0的。
通过设置GICD_IPRIORITYRn寄存器,来设置中断的优先级。这个寄存器是字节有效的,也就是一个字节,对应一个中断的优先级。优先级数值越小,那么这个中断的优先级越高。
高优先级的中断,是可以抢占低优先级的中断。
2.12 GIC使用例子
下图是GIC的使用例子:
外部的中断,连接到gic。由distributor进行中断分组。中断请求,由distributor发送给cpu interface,cpu interface再发送给处理器。
2.13 GIC寄存器
gic寄存器,分为两部分,一部分是distributor的寄存器,另一部分是cpu interface的寄存器。
两部分的寄存器,均是通过 memory-mapped 的方式来访问。
3. Gicv3架构基础
3.1 中断号排布
Distributor:SPI中断的管理,将中断发送给 Redistributor
Redistributor:PPI,SGI,LPI中断的管理,将中断发送给 Cpu interface
Cpu interface:传输中断给core
ITS:用来解析LPI中断
其中,cpu interface是实现在core内部的,distributor,redistributor,ITS是实现在gic内部的。
cpu interface和gic的redistributor通信,通过AXI-Stream协议,来实现通信。
3.2 中断生命周期
Generate:外设发起一个中断
Distribute:Distributor对收到的中断源进行仲裁,然后发送给对应的cpu interface
Deliver: Cpu interface将中断发送给core
Activate: Core通过读取 GICC_IAR 寄存器,来对中断进行认可
Priority drop: Core通过写 GICC_EOIR 寄存器,来实现优先级重置
Deactivation:Core通过写 GICC_DIR 寄存器,来无效该中断
这个中断生命周期,和 gicv2 的中断生命周期是一样的。
3.3 中断流程
中断要通过distributor的中断流程
1)外设发起中断,发送给 GICD
2)GICD将该中断,分发给合适的 GICR
3)GICR将中断信息,发送给cpu interface。
cpu interface产生合适的中断异常给处理器
处理器接收该异常,并且软件处理该中断
LPI中断的中断流程
1)外设发起中断,发送给ITS
2)ITS分析中断,决定将来发送的 GICR
3)ITS将中断发送给合适的 GICR
4)GICR 将中断信息,发送给 Cpu interface。
5)Cpu interface产生合适的中断异常给处理器
处理器接收该异常,并且软件处理该中断
3.4 中断处理
1、边沿触发处理
外部边沿中断到达,中断状态被置为 pending 状态。
软件读取IAR寄存器值,表示PE认可该中断,中断状态被置为 active 状态
软件中断处理完毕后,写EOIR寄存器,表示优先级重置。过一段时间后,写DIR寄存器,中断状态被置为idle状态
2、电平触发处理
外部高电平中断到达,中断状态置为pending状态。
软件读取IAR寄存器,表示PE认可该中断。但中断依然为高,中断状态进入pending and active状态。
软件中断处理完毕后,写EOIR寄存器,表示优先级重置。过一段时间后,写DIR寄存器,中断状态被置为idle状态。
3.5 寄存器
gicv3中,多了很多寄存器。而且对寄存器,提供了2种访问方式,一种是memory-mapped的访问,一种是系统寄存器访问:
memory-mapped访问的寄存器:
GICC: cpu interface寄存器
GICD: distributor寄存器
GICH: virtual interface控制寄存器,在hypervisor模式访问
GICR: redistributor寄存器
GICV: virtual cpu interface寄存器
GITS: ITS寄存器
系统寄存器访问的寄存器:
ICC: 物理 cpu interface 系统寄存器
ICV: 虚拟 cpu interface 系统寄存器
ICH: 虚拟 cpu interface 控制系统寄存器
下图是gicv3中,各个寄存器,所在的位置。
对于系统寄存器访问方式的gic寄存器,是实现在core内部的。而memory-mapped访问方式的gic寄存器,是在gic内部的。
gicv3架构中,没有强制,系统寄存器访问方式的寄存器,是不能通过memory-mapped方式访问的。也就是ICC, ICV, ICH寄存器,也是可以实现在gic内部,通过memory-mapped方式去访问。但是一般的实现中,是没有这样的实现的。
4 Gicv3 – gic stream协议
gic stream协议,是基于AXI-stream协议。用于gic的IRI组件(interrupt routing infrastructure),和cpu interface之间,传输信息。 distributor,redistributor和ITS,统称为IRI组件。 gic stream协议,包含以下2个接口:
下行AXI-stream接口:用于IRI向cpu interface传递信息,连接
上行AXI-stream接口:用于cpu interface向IRI传递信息
4.1 接口信号
一、接口信号 接口信号,包含下行接口信号,和上行接口信号。无论是上行还是下行,都是基于AXI-stream协议,通过data信号,来传输数据,data信号的位宽,也是固定的,为16bit。 1、下行接口信号 下行接口信号如下表所示,接口协议是基于AXI-stream协议。
2、上行接口信号 上行接口信号如下表所示,接口协议是基于AXI-stream协议。
二、包 IRI与cpu interface通过gic stream协议传输信息,传输的信息,是以包为单位。包,分为两类包:
命令包,分为redistributor命令包,cpu interface命令包
响应包,分为redistributor响应包,cpu interface响应包
AXI-stream协议,每次传输2个字节,多次传输,组成一个包。不同的包,大小不是一样的,比如有的是16个字节,有的是8个字节。包传输的第一个16bit数据,表示包的类型。 如果一个组件,发送命令包,那么另一个,需要回应响应包。 1、redistributor命令包 下图是下行redistributor命令包。
2、redistributor响应包 下图是上行redistributor响应包。
3、cpu interface命令包 下图是cpu interface命令包。
4、cpu interface响应包 下图是cpu interface响应包
5、例子 以下是cpu interface的activate命令包,格式如下
这个activate命令,由cpu interface发送给IRI,表示认可中断,中断号,由包中的INTID字域来表示。 IRI在收到cpu interface的activate命令后,会回发activate acknowledge响应包。表示,IRI接收到cpu interface的activate命令。 其格式,如下所示:
包在传输的过程中,先发第一个数据的低16bit数据,再发第一个数据的高16bit数据,如果还有下一个数据,按照上述流程发送。所以,命令的类型,是最先发送的。 三、包传输流程 1、中断发送 如下图,redistributor,要发送一个中断给cpu。
4.2 包的传输的流程
redistributor给cpu interface发送set命令,发送中断X请求,cpu interface接收到该命令后,如果该中断X符合当前优先级要求,CPU interface通过IRQ/FIQ给cpu发送中断。
CPU响应CPU interface发送的中断,于是去读取ICC_IAR寄存器,表示认可该中断,得到中断号。之后cpu interface给redistributor发送activate响应。然后把IRQ/FIQ给取消掉。
2. 中断取消
CPU在读取ICC_IAR寄存器前,redistributor取消中断
redistributor给cpu interface发送set命令,cpu interface接收到该命令后,通过IRQ/FIQ给cpu发送中断。
redistributor给cpu interface发送clear命令,清除该中断,cpu interface将IRQ/FIQ拉低。然后回release响应。
cpu interface给redistributor,回clear acknowledge响应。
如果此时,cpu读取IAR寄存器,CPU会获取到一个假的中断号。
3. 两个中断 redistributor给cpu interface发送两个中断
redistributor,首先发送set x命令,发送中断x。cpu interface接收该命令,将IRQ/FIQ拉高,向CPU发送中断请求。
cpu读取ICC_IAR寄存器,认可该中断x,开始处理该中断x。cpu interface给redistributor回activate x响应。
之后,redistributor给CPU interface发送set y命令,发送中断y。在cpu interface中,如果y的优先级符合要求,那么IRQ/FIQ会一直有效。等待CPU处理。
4、中断抢占 redistributor给cpu interface发送2个中断,第二个中断抢占第一个中断。
redistributor,首先发送set x命令,发送中断x。cpu interface接收该命令,将IRQ/FIQ拉高,向CPU发送中断请求。
在cpu读取ICC_IAR寄存器之前,redistributor,又给cpu interface发送了set y命令,发送中断y。并且y的优先级比x高。
cpu interface给redistributor回release x响应,表示cpu interface暂时不处理中断x,中断x,将来重新发送。
CPU读取ICC_IAR寄存器,认可中断y。cpu interface给redistributor回activate y响应。
5、电源断电 gic给CPU发送中断,使CPU断电。
-----------------------------------------------分割线--------------------------------------
5 LPI
https://zhuanlan.zhihu.com/p/264870895
在 Gicv3 中,引入了一种新的中断类型。message based interrupts,消息中断。
5.1 消息中断
外设,不在通过专用中断线,向gic发送中断,而是写gic的寄存器,来发送中断。
这样的一个好处是,可以减少中断线的个数。
为了支持消息中断,gicv3,增加了LPI,来支持消息中断。并且为他分配了特别多的中断号,从8192开始,移植到16777216。
LPI,locality-specific peripheral interrupts。spec中,用了一章,来介绍这个LPI。
5.2 LPI介绍
LPI是一种基于消息的边沿中断。也就是,中断信息,不在通过中断线,进行传递,而是通过memory。gic内部,提供一个寄存器,当外设往这个地址,写入数据时,就往gic发送了一个中断。
在soc系统中,外设想要发送中断给gic,是需要一根中断线的。如果现在一个外设,需要增加一个中断,那么就要增加一根中断线,然后连接到gic。这样,就需要修改设计。而引入了LPI之后,当外设需要增加中断,只需要使用LPI方式,传输中断即可,不需要修改soc设计。
引入了LPI之后,gicv3中,还加入了ITS组件,interrupt translation service。ITS将接收到的LPI中断,进行解析,然后发送到对应的 Redistributor,再由 Redistributor将中断信息,发送给 Cpu interface。
以下是带LPI的框图。外设,通过写 GITS_TRANSLATER 寄存器,来传递消息中断。
LPI,和SPI,PPI,SGI有些差别,LPI的中断的配置,以及中断的状态,是保存在memory的表中,而不是保存在gic的寄存器中的。
GICR_PROPBASER:保存LPI中断配置表的基地址
GICR_PENDBASER: 保存LPI中断状态表的基地址
这里,就涉及到两个表:
5.2.1 LPI中断配置表
该表,保存在memory中。基地址,由GICR_PROPBASER寄存器决定。
当外部发送LPI中断给redistributor,redistributor首先要查该表,也就是要访问memory来获取LPI中断的配置。为了加速这过程,redistributor中可以配置cache,用来缓存LPI中断的配置信息。
因为有了cache,所以LPI中断的配置信息,就有了2份拷贝,一份在memory中,一份在redistributor的cache中。如果软件修改了memory中的LPI中断的配置信息,需要将redistributor中的cache信息给无效掉。
5.2.2 LPI中断状态表
该表,处于memory中,保存了LPI中断的状态,是否pending状态。
LPI中断的状态,不是保存在寄存器中,而是保存在memory中的pending表中。该状态表,由redistributor来进行更改。而该table的基地址,是由软件来设置的。
软件通过设置 GICR_PENDBASER 寄存器来设置。
5.3 LPI的实现方式
为了实现LPI,gicv3定义了以下两种方法来实现:
1) 使用 ITS,将外设发送到 eventID,转换成LPI 中断号
2) Forwarding 方式,直接访问 Redistributor 的寄存器 GICR_SERLPIR,直接发送 LPI 中断
5.3.1 forwarding方式
这种方式,比较简单,主要由下面几个寄存器来实现:
GICR_SERLPIR
GICR_CLRLPIR
GICR_INVLPIR
GICR_INVALLR
GICR_SYNCR
其 GIC 框图如下所示:
5.3.2 使用ITS方式
理解了 forwarding 方式,那么理解 ITS 方式,就要容易了。forwarding方式,是直接得到了 LPI 的中断号。但是对于ITS方式,是不知道 LPI 的中断号的。需要将外设发送的 DeviceID,eventID,通过一系列查表,得到 LPI 的中断号以及该中断对应的 target redistributor,然后将LPI中断,发送给对应的redistributor。
下图是带有 ITS 的 gic 框图:
外设,通过写GITS_TRANSLATER寄存器,发起LPI中断。写操作,给ITS提供2个信息:
EventID:值保存在GITS_TRANSLATER寄存器中,表示外设发送中断的事件类型
DeviceID:表示哪一个外设发起LPI中断。该值的传递,是实现自定义,例如,可以使用AXI的user信号来传递。
ITS将DeviceID和eventID,通过一系列查表,得到LPI中断号,再使用LPI中断号查表,得到该中断的目标cpu。
ITS将LPI中断号,LPI中断对应的目标cpu,发送给对应的redistributor。redistributor再将该中断信息,发送给CPU。
6 ITS
ITS是一个组件,用来提供给外设发送 LPI 中断的,然后将 LPI 中断,发送给 Redistributor。
6.1 ITS处理流程
ITS使用三类表格,实现LPI的转换和映射:
device table: 映射deviceID到中断转换表
interrupt translation table:映射EventID到INTID。以及INTID属于的collection组
collection table: 映射collection到redistributor
当外设往GITS_TRANSLATER寄存器中写数据后,ITS做如下操作:
1)使用DeviceID,从设备表(device table)中选择索引为DeviceID的表项。从该表项中,得到中断映射表的位置
2)使用EventID,从中断映射表中选择索引为EventID的表项。得到中断号,以及中断所属的collection号
3)使用collection号,从collection表格中,选择索引为collection号的表项。得到redistributor的映射信息
4)根据collection表项的映射信息,将中断信息,发送给对应的redistributor
6.2 ITS命令
ITS操作,会涉及到很多表,而这些表的创建,维护是通过ITS命令,来实现的。虽然这些表,是在内存中的,但是GICv3和GICv4,不支持直接访问这些表,而是要通过ITS命令,来配置这些表。
ITS的操作,是通过命令,来控制的。外部通过发送命令给ITS,ITS然后去执行命令,每个命令,占32字节。
ITS有command队列,命令写在这个队列里面。ITS会自动的按照队列顺序,一一执行。
每个命令占32个字节。
命令,存放在内存中,GITS_CBASE,保存命令的首地址。GITS_CREADR,是由ITS控制,表示下一个命令的地址。GITS_CWRITER,是下一个待写命令的地址。软件往GITS_CWRITER地址处,写入命令,之后ITS就会执行这个命令。
ITS提供的命令,有很多,可以查阅GIC手册获取更多。
7 总结
gicv3中,引入了消息中断,并且为之,支持了LPI。分配了大量的中断号,用于LPI。对于LPI的实现,有2种方式,一种是访问redistributor提供的寄存器,一种是使用ITS。
不过对于手机arm cpu来说,其实是不需要LPI的,因为现有的中断,已经符合要求,加入了LPI,让gic更复杂,让软件操作,也更复杂。但是对于服务器arm cpu,这个,就需要了,因为这个可以和PCIE相连,实现消息中断。个人感觉,这个LPI中断,是为arm服务器cpu,所使用的。
对于提供的gic IP来说,比如gic600,是有配置选项,决定,是否是否支持LPI中断,以及是否需要ITS。