ARM GIC 系列文章学习(转)
原文来自:骏的世界
ARM GIC(一) cortex-A 处理器中断简介
对于ARM的处理器,中断给处理器提供了触觉,使处理器能够感知到外界的变化,从而实时的处理。本系列博文,是以ARM cortex-A系列处理器,来介绍ARM的soc中,中断的处理。
ARM cortex-A系列处理器,提供了4个管脚给soc,实现外界中断的传递。分别是:
- nIRQ: 物理普通中断
- nFIQ: 物理快速中断
- nVIRQ: 虚拟普通中断
- nVFIQ: 虚拟快速中断
如下图所示:
其中虚拟中断,是为了实现虚拟化而加入的,在这个系列中,不讨论虚拟中断,只介绍物理中断的相关知识。
在arm的soc系统中,会有多个外设,均有可能会产生中断发送给arm cpu,等待cpu处理。
而arm cpu对中断,只提供了2根信号,一个nIRQ,一个是nFIQ。因此就需要有一个中断控制器来作为中间的桥接,收集soc的所有中断信号,然后仲裁选择合适的中断,再发送给CPU,等待CPU处理。
如下图所示:
这中间的桥接器件,就是arm公司推出大名鼎鼎的GIC,general interrupt controller。
GIC其实是一个架构,版本历经了GICv1(已弃用),GICv2,GICv3,GICv4。对于不同的GIC版本,arm公司设计了对应的GIC IP。
- GIC400,支持GICv2架构版本。
- GIC500,支持GICv3架构版本。
- GIC600,支持GICv3架构版本。
GIC的核心功能:对soc中外设的中断源的管理,并且提供给软件,配置以及控制这些中断源。
当对应的中断源有效时,GIC根据该中断源的配置,决定是否将该中断信号,发送给CPU。如果有多个中断源有效,那么GIC还会进行仲裁,选择最高优先级中断,发送给CPU。
当CPU接受到GIC发送的中断,通过读取GIC的寄存器,就可以知道,中断的来源来自于哪里,从而可以做相应的处理。
当CPU处理完中断之后,会告诉GIC(通过访问GIC的寄存器),该中断处理完毕。GIC接受到该信息后,就将该中断源取消,避免又重新发送该中断给cpu以及允许中断抢占。
之后,会先介绍下GICv2的相关知识,然后介绍目前主流使用的GICv3。
ARM GIC(二)中断术语
ARM在GIC中,对于中断,定义了如下的一些术语。
中断状态
对于每一个中断而言,有以下4个状态:
-
inactive:中断处于无效状态
-
pending:中断处于有效状态,但是cpu没有响应该中断
-
active:cpu在响应该中断
-
active and pending:cpu在响应该中断,但是该中断源又发送中断过来
以下是中断状态的转移图。至于图中的转移条件,在GIC架构文档中,有介绍。
中断触发方式
中断触发方式,包含以下两种方式:
- edge-triggered:边沿触发,当中断源产生一个边沿,中断有效
- level-sensitive:电平触发,当中断源为指定电平,中断有效
中断类型
中断类型分为以下几类:
- PPI:(private peripheral interrupt),私有外设中断,该中断来源于外设,但是该中断只对指定的core有效。
- SPI:(shared peripheral interrupt),共享外设中断,该中断来源于外设,但是该中断可以对所有的core有效。
- SGI:(software-generated interrupt),软中断,软件产生的中断,用于给其他的core发送中断信号
- virtual interrupt:虚拟中断,用于支持虚拟机
中断优先级
因为soc中,中断有很多,为了方便对中断的管理,对每个中断,附加了中断优先级。在中断仲裁时,高优先级的中断,会优于低优先级的中断,发送给cpu处理。
当cpu在响应低优先级中断时,如果此时来了高优先级中断,那么高优先级中断会抢占低优先级中断,而被处理器响应。
中断号
为了方便对中断的管理,GIC为每个中断,分配了一个中断号,也就是interrupt ID。对于中断号,GIC也进行了分配:
- ID0-ID15:分配给SGI,软中断
- ID16-ID31:分配给PPI,私有外设中断
- ID32-ID1019分配给SPI,共享外设中断
- 其他
在具体的arm的cpu中,对于PPI,又进行了详细的分配。这个,就得看arm cpu的TRM了。
中断生命周期
一个中断,是有生命周期的。以下是流程图:
Start=>start: Start,中断开始
generate=>operation: generate,中断源产生中断,发送给GIC
deliver=>operation: deliver,GIC将中断发送给cpu
activate=>operation: Activate,cpu响应该中断
deactivate=>operation: Deactivate,cpu响应完中断,告诉GIC,中断处理完毕,GIC更新该中断状态
end=>end: End,中断结束
Start->generate->deliver->activate->deactivate->end
banking
banking(不知翻译成啥比较合适)功能,包括以下两个:
中断banking
对于PPI和SGI,GIC可以有多个中断对应于同一个中断号。比如在soc中,有多个外设的中断,共享同一个中断号。
寄存器banking
对于同一个GIC寄存器地址,在不同的情况下,访问的是不同的寄存器。例如在secure和non-secure状态下,访问同一个GIC寄存器,其实是访问的不同的GIC的寄存器。
具体,更多的信息,得看GIC spec以及arm spec。
ARM GIC(三) GICv2架构
ARM的cpu,特别是cortex-A系列的CPU,目前都是多core的cpu,因此对于多core的cpu的中断管理,就不能像单core那样简单去管理,由此arm定义了GICv2架构,来支持多核cpu的中断管理。
GICv2架构
GICv2,支持最大8个core。其框图如下图所示:
在GICv2中,GIC由两个大模块组成:
-
distributor:实现中断分发,对于PPI,SGI是各个core独有的中断,不参与目的core的仲裁,SPI,是所有core共享的,根据配置决定中断发往的core。最后选择最高优先级中断发送给cpu interface。寄存器使用 GICD_ 作为前缀。一个GIC中,只有一个GICD。
-
cpu interface:将GICD发送的中断信息,通过IRQ,FIQ管脚,传输给core。寄存器使用 GICC_ 作为前缀。每一个core,有一个cpu interface。
-
virtual cpu interface:将GICD发送的虚拟中断信息,通过VIRQ,VFIQ管脚,传输给core。每一个core,有一个virtual cpu interface。而在这virtual cpu interface中,又包含以下两个组件:
-
- virtual interface control:寄存器使用 GICH_ 作为前缀
- virtual cpu interface:寄存器使用 GICV_ 作为前缀
图中的virtual interface,是用于支持虚拟中断,本系列不讨论虚拟中断。
GICv2支持中断旁路模式,也就是GIC外部的FIQ,IRQ直接接到core的FIQ,IRQ上,相当于GIC是不使能的。也就是CFGSDISABLE是有效的,将GIC给无效掉。
GICv2,定义了自己的一些寄存器,这些寄存器,都是使用memory-mapped的方式去访问的,也就是在soc中,会留有一片空间,给GIC。cpu通过访问这部分空间,来对GIC进行操作。
寄存器,分为以下:
- GICD_*: distributor的寄存器
- GICH_*: 虚拟interface的控制寄存器
- GICV_*:虚拟interface的控制寄存器
- GICC_*: 虚拟cpu interface的寄存器
中断分组
givc2,将中断,分成了group0和group1。使用寄存器GICD_IGROUPRn来对每个中断,设置组。
- group0:安全中断,由nFIQ驱动
- group1:非安全中断,由nIRQ驱动
中断号
GICv2,支持最大1020个中断。其中断号分配如下:
中断号 | 分配 | 中断来源 | 寄存器 |
---|---|---|---|
ID0-ID7 | 非安全软中断 | 软件 | GICD_SGIR |
ID8-ID15 | 安全软中断 | 软件 | GICD_SGIR |
ID16-ID31 | 私有中断 | 外设 | no |
ID32-ID1019 | 共享中断 | 外设 | no |
GIC结构
GIC主要包括以下两个组件:distributor、cpu interface。
distributor
中断分发器,用来收集所有的中断来源,并且为每个中断源设置中断优先级,中断分组,中断目的core。当有中断产生时,将当前最高优先级中断,发送给对应的cpu interface。
distributor对中断提供以下的功能:
- 全局中断使能
- 每个中断的使能
- 中断的优先级
- 中断的分组
- 中断的目的core
- 中断触发方式
- 对于SGI中断,传输中断到指定的core
- 每个中断的状态管理
- 提供软件,可以修改中断的pending状态
cpu interface
cpu interface,将GICD发送的中断信息,通过IRQ,FIQ管脚,发送给连接到该cpu接口的core。
cpu interface提供了一下的功能:
- 将中断请求发送给cpu
- 对中断进行认可(acknowledging an interrupt)
- 中断完成识别(indicating completion of an interrupt)
- 设置中断优先级屏蔽
- 定义中断抢占策略
- 决定当前处于pending状态最高优先级中断
中断认可
中断认可,是指cpu响应该中断。此时中断状态从pending状态,变为active状态。通过访问GICC_IAR寄存器,来对中断进行认可。
- GICC_IAR: 认可group0的中断
- GICC_AIAR:认可group1的中断
中断完成
中断完成,是指cpu处理完中断。此时中断状态从active状态,变为inactive状态。GIC中,对中断完成,定义了以下两个stage:
- 优先级重置(priority drop):将当前中断屏蔽的最高优先级进行重置,以便能够响应低优先级中断。group0中断,通过写GICC_EOIR寄存器,来实现优先级重置,group1中断,通过写 GICC_AEOIR 寄存器,来实现优先级重置。
- 中断无效(interrupt deactivation):将中断的状态,设置为inactive状态。通过写 GICC_DIR 寄存器,来实现中断无效。
这里为什么要对中断完成,定义2个stage,其实是有考虑的。对于中断来说,我们是希望中断处理程序越短越好,但是有些中断处理程序,就是比较长,在这种情况下,就会使其他中断得到相应,从而影响实时性。
比如当前cpu在响应优先级为4的中断A,但是这个中断A的中断处理程序比较长,此时如果有优先级为5的中断B到来,(优先级越小,优先级越高)那么cpu是不会响应这个中断的。
在软件上,会将中断处理程序分为两部分,分为上半部分,和下半部分。在上半部分,完成中断最紧急的任务,然后就可以通知GIC,降低当前的中断处理优先级,以便其他中断能够得到响应。在下半部分,处理该中断的其他事情。
在这种机制下,低优先级的中断,不用等待高优先级的中断,完全执行完中断处理程序后,就可以被cpu所响应,提高实时性。
为了实现上述机制,就将中断完成分成了2步。还是刚刚的例子,cpu在响应优先级为4的中断A,当中断A的上半部分完成后,通知GIC,优先级重置(drop priority),GIC将当前的最高优先级中断重置,重置到响应中断A之前的优先级,比如优先级6,那么此时优先级为5的中断B,就可以被cpu响应。最后中断A的下半部分完成后,通知GIC,将该中断A的状态,设置为inactive状态,此时中断A就真正的完成了。
当然,也可以不将中断完成分成2步(就1步)。通过控制 GICC_CTLR寄存器的EOImode比特,来决定是否将中断完成分成2步。
bypass功能
GICv2支持bypass功能,这样GIC就不起作用了,core的中断管脚,直接由soc的其他部门信号驱动。
如下图所示,通过控制 GICC_CLTR 寄存器的一些比特位,来实现bypass功能。不过这个功能一般不使用,不然何必要在arm的soc中,加入GIC呢?
中断处理流程
中断处理流程,包含了以下几步:
- GIC决定每个中断的使能状态,不使能的中断,是不能发送中断的
- 如果某个中断的中断源有效,GIC将该中断的状态设置为pending状态,然后判断该中断的目标core
- 对于每一个core,GIC将当前处于pending状态的优先级最高的中断,发送给该core的cpu interface
- cpu interface接收GIC发送的中断请求,判断优先级是否满足要求,如果满足,就将中断通过nFIQ或nIRQ管脚,发送给core。
- core响应该中断,通过读取 GICC_IAR 寄存器,来认可该中断。读取该寄存器,如果是软中断,返回源处理器ID,否则返回中断号。
- 当core认可该中断后,GIC将该中断的状态,修改为active状态
- 当core完成该中断后,通过写 EOIR (end of interrupt register)来实现优先级重置,写 GICC_DIR 寄存器,来无效该中断
中断使能和禁止
通过设置GICD_ISENABLERn寄存器,来使中断使能,通过设置GICD_ICENABLERn寄存器,来使中断禁止。
这两个寄存器,都是bit有效的寄存器,也就是一个bit,关联一个中断。
比如对于GICD_ISENABLER寄存器,描述如下:
中断pending
通过设置GICD_ISPENDRn或GICD_ICPENDRn寄存器,可以读取和修改中断的pending状态。这两个寄存器,也是bit有效的寄存器,一个bit,关联一个中断。
中断active
通过设置GICD_ISACTIVERn或GICD_ICACTIVERn寄存器,可以读取和修改中断的active状态。这两个寄存器,也是bit有效的寄存器,一个bit,关联一个中断。
产生软中断
通过写 GICD_SGIR 寄存器,来产生软中断。软中断,可以指定产生中断,发往执行的core,也可以发送多个core。
对于软中断,这个是软件产生的中断。比如软件,想给执行自己的core,发送一个中断,就可以通过软中断来产生。或者软件,想起其他的core,发送一个中断,也可以通过软中断来产生。
寄存器如下:
TargetListFileter
对于TargetListFileter:决定distributor将软中断,如何发送给cpu interface。
- 0b00:按照CPUTargetList的指定,来发送软中断
- 0b01:按照CPUTargetList的指定,来发送软中断,但是不能发送给自己
- 0b10:软中断,只能发送给自己
CPUTargetList
描述如下:
对于多core的系统,会给每个core一个编号。GICv2支持最多8个core,因此core的编号就是0-7,刚好8个bit,可以表示。
这样的CPUTargetList,就和8个cpu相对应。第0bit,表示core0,第7bit,表示core7。
如果想给core1,core2,core7发送软中断,那么此时这个位域要填入0x84。
NSATT
描述如下:
这个用来支持安全扩展,在GICv2中,将中断进行了分组
- group0:安全中断
- group1:非安全中断
这个bit,用来表示发送的软中断,是安全中断,还是非安全中断。而且这个bit,只有core处于安全状态的时候,才能写。如果core是处于非安全状态,那么这个bit被忽略,也就是只能发非安全的软中断。
SGIINTID
发送的软中断的中断号。
中断优先级
GICv2,支持最小16个,最大256个中断优先级,如下图所示:
如果实现的中断优先级小于256个,那么最低的几个bit,是为0的。
通过设置GICD_IPRIORITYRn寄存器,来设置中断的优先级。这个寄存器是字节有效的,也就是一个字节,对应一个中断的优先级。优先级数值越小,那么这个中断的优先级越高。
高优先级的中断,是可以抢占低优先级的中断。
GIC使用例子
下图是GIC的使用例子:
外部的中断,连接到GIC。由distributor进行中断分组。中断请求,由distributor发送给cpu interface,cpu interface再发送给处理器。
对于支持安全扩展,其应用如下:
安全中断,处于group0,非安全中断处于group1。
GIC寄存器
GIC寄存器,分为两部分,一部分是distributor的寄存器,另一部分是cpu interface的寄存器。
两部分的寄存器,均是通过memory-mapped的方式来访问。
下图是distributor的寄存器:
下图是cpu interface的寄存器:
总结
以上就是GICv2的介绍,更多的内容,需要查看GICv2的文档。
GICv2比较简单,最多只能支持8个core,超过了8个core,那么就不能使用GICv2了。不过这也不是大问题,对于手机的arm处理器来说,最多也就8个core。但是对于服务器,桌面级的arm处理器,那么就可能会超过8个core,此时GICv2就不适用了,所以ARM后面又加入GICv3,v4架构。
GICv2的寄存器,都是通过memory-mapped的方式访问。但是中断在一个soc系统中,是经常会产生的,那么处理器就会经常的读取GIC的寄存器,而使用memory-mapped的方式去访问,就会影响中断响应速度。在之后的GICv3,v3中,就加入了使用系统寄存器来进行访问,加快中断处理。
GICv2只是一个GIC的架构,其实现的对应的IP是GIC400。
ARM GIC(四) GICv3架构基础
GICv3架构是GICv2架构的升级版,增加了很多东西。变化在于以下:
- 使用属性层次(affinity hierarchies),来对core进行标识,使GIC支持更多的core
- 将cpu interface独立出来,用户可以将其设计在core内部
- 增加redistributor组件,用来连接distributor和cpu interface
- 增加了LPI,使用ITS来解析
- 对于cpu interface的寄存器,增加系统寄存器访问方式
GICv3结构
下图是GICv3的架构。
**
**
包含了以下的组件:
- 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协议,来实现通信。
属性层次
GICv3的一大变化,是对core的标识。对core不在使用单一数字来表示,而是使用属性层次来标识,和arm core,使用MPIDR_EL1系统寄存器来标识core一致。
每个core,根据属性层次的不同,使用不同的标号来识别。如下图所示,是一个4层结构,那么对于一个core来说,就可以用 xxx.xxx.xxx.xxx 来识别。
这种标识方式,和ARMv8架构的使用MPIDR_EL1寄存器,来标识core是一样的。
每个core,连接一个cpu interface,而cpu interface会连接GIC中的一个redistributor。redistributor的标识和core的标识一样。
中断分组
GICv3,将中断分成了2个大组,group0和group1。
-
group0:提供给EL3使用
-
group1:又分为2组,分别给安全中断和非安全中断使用
如下图所示:
以下是IRQ,FIQ与组的对应关系。
中断生命周期
中断生命周期,如下图所示:
-
generate:外设发起一个中断
-
distribute:distributor对收到的中断源进行仲裁,然后发送给对应的cpu interface
-
deliver:cpu interface将中断发送给core
-
activate:core通过读取 GICC_IAR 寄存器,来对中断进行认可
-
priority drop: core通过写 GICC_EOIR 寄存器,来实现优先级重置
-
deactivation:core通过写 GICC_DIR 寄存器,来无效该中断
这个中断生命周期,和GICv2的中断生命周期是一样的。
中断流程
下图是GIC的中断流程,中断分成2类:
- 一类是中断要通过distributor,比如SPI中断
- 一类是中断不通过distributor,比如LPI中断
中断要通过distributor的中断流程
-
外设发起中断,发送给distributor
-
distributor将该中断,分发给合适的re-distributor
-
re-distributor将中断信息,发送给cpu interface。
-
cpu interface产生合适的中断异常给处理器
-
处理器接收该异常,并且软件处理该中断
LPI中断的中断流程
-
外设发起中断,发送给ITS
-
ITS分析中断,决定将来发送的re-distributor
-
ITS将中断发送给合适的re-distributor
-
re-distributor将中断信息,发送给cpu interface。
-
cpu interface产生合适的中断异常给处理器
-
处理器接收该异常,并且软件处理该中断
中断处理
中断处理,分为边沿触发处理和电平触发处理
边沿触发处理
外部边沿中断到达,中断状态被置为pending状态。
软件读取IAR寄存器值,表示PE认可该中断,中断状态被置为active状态
软件中断处理完毕后,写EOIR寄存器,表示优先级重置。过一段时间后,写DIR寄存器,中断状态被置为idle状态。
电平触发处理
外部高电平中断到达,中断状态置为pending状态。
软件读取IAR寄存器,表示PE认可该中断。但中断依然为高,中断状态进入pending and active状态。
软件中断处理完毕后,写EOIR寄存器,表示优先级重置。过一段时间后,写DIR寄存器,中断状态被置为idle状态。
寄存器
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方式去访问。但是一般的实现中,是没有这样的实现的。
下图是ICC的系统寄存器,和memory-mepped方式寄存器的对应关系的一部分,更多的就要查看GICv3的spec。
那么,问题来了,GICv3中,为什么选择将cpu interface,从GIC中抽离,实现在core内部?为什么要将cpu interface的寄存器,增加系统寄存器访问方式,实现在core的内部?这样做,是有什么好处?
我认为,GICv3的上述安排,第一是为了软件编写能够简单,通用,第二是为了让中断响应能够更快。
首先要先了解,在GIC的寄存器中,哪一些寄存器,是会频繁被core所访问的,哪一些寄存器,是不会频繁被core所访问的。毫无疑问,cpu interface的寄存器,是会频繁被core所访问的,因为core需要访问cpu interface的寄存器,来认可中断,来中断完成,来无效中断。而其他的寄存器,是配置中断的,只有在core需要去配置中断的时候,才用访问得到。有了这个认识,那么理解之后我所讲述的,就比较容易了。
在GICv2中,cpu interface的寄存器,是实现在GIC内部的,因此当core收到一个中断时,会通过axi总线(假设memory总线是axi总线),去访问cpu interface的寄存器。而中断在一个soc系统中,是会频繁的产生的,这就意味着,core会频繁的去访问GIC的寄存器,这样会占用axi总线的带宽,总而会影响中断的实时响应。而且core通过axi总线去访问cpu interface寄存器,延迟,也比较大。
在GICv3中,将cpu interface从GIC中抽离出来,实现在core内部,而不实现在GIC中。core对cpu interface的访问,通过系统寄存器方式访问,也就是使用msr,mrs访问,那么core对cpu interface的寄存器访问,就加速了,而且还不占用axi总线带宽。这样core对中断的处理,就加速了。
cpu interface与GIC之间,是通过专用的AXI-stream总线,来传输信息的,这样也不会占用AXI总线的带宽。
GICv3架构的内容,比较多,我这边会拆成几个部分。
下一部分,会介绍GIC stream协议。也就是cpu interface与GIC之间的通信。
ARM GIC(五)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传递信息
如下图所示:
接口信号
接口信号,包含下行接口信号,和上行接口信号。无论是上行还是下行,都是基于AXI-stream协议,通过data信号,来传输数据,data信号的位宽,也是固定的,为16bit。
下行接口信号
下行接口信号如下表所示,接口协议是基于AXI-stream协议。
上行接口信号
上行接口信号如下表所示,接口协议是基于AXI-stream协议。
包
IRI与cpu interface通过GIC stream协议传输信息,传输的信息,是以包为单位。包,分为两类包:
- 命令包,分为redistributor命令包,cpu interface命令包
- 响应包,分为redistributor响应包,cpu interface响应包
AXI-stream协议,每次传输2个字节,多次传输,组成一个包。不同的包,大小不是一样的,比如有的是16个字节,有的是8个字节。包传输的第一个16bit数据,表示包的类型。
如果一个组件,发送命令包,那么另一个,需要回应响应包。
redistributor命令包
下图是下行redistributor命令包。
redistributor响应包
下图是上行redistributor响应包。
cpu interface命令包
下图是cpu interface命令包。
cpu interface响应包
下图是cpu interface响应包。
例子
以下是cpu interface的activate命令包,格式如下:
这个activate命令,由cpu interface发送给IRI,表示认可中断,中断号,由包中的INTID字域来表示。
IRI在收到cpu interface的activate命令后,会回发activate acknowledge响应包。表示,IRI接收到cpu interface的activate命令。
其格式,如下所示:
包在传输的过程中,先发第一个数据的低16bit数据,再发第一个数据的高16bit数据,如果还有下一个数据,按照上述流程发送。所以,命令的类型,是最先发送的。
包传输流程
中断发送
如下图,redistributor,要发送一个中断给cpu。包的传输的流程,如下:
- redistributor给cpu interface发送set命令,发送中断X请求,cpu interface接收到该命令后,如果该中断X符合当前优先级要求,CPU interface通过IRQ/FIQ给cpu发送中断。
- CPU响应CPU interface发送的中断,于是去读取ICC_IAR寄存器,表示认可该中断,得到中断号。之后cpu interface给redistributor发送activate响应。然后把IRQ/FIQ给取消掉。
中断取消
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会获取到一个假的中断号。
两个中断
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处理。
中断抢占
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响应。
电源断电
GIC给CPU发送中断,使CPU断电。
这个,就比较复杂了。就不解析了。
电源上电
redistributor,请求将CPU和cpu interface上电。
这个也比较复杂,这里不解析。
总结
GICv3中,IRI与cpu interface之间,是通过包,来传输信息。传输的接口协议,使用AXI-stream。通过包的各种组合,来实现GIC的中断操作与中断管理。
之后,会介绍GICv3中,引入的一种新的中断类型,消息中断。
ARM GIC(六)GICv3架构-LPI
在GICv3中,引入了一种新的中断类型。message based interrupts,消息中断。
消息中断
外设,不在通过专用中断线,向GIC发送中断,而是写GIC的寄存器,来发送中断。
这样的一个好处是,可以减少中断线的个数。
为了支持消息中断,GICv3,增加了LPI,来支持消息中断。并且为他分配了特别多的中断号,从8192开始,移植到16777216。
LPI,locality-specific peripheral interrupts。spec中,用了一章,来介绍这个LPI。
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中断状态表的基地址
这里,就涉及到两个表:
LPI中断配置表
该表,保存在memory中。基地址,由GICR_PROPBASER寄存器决定。
该寄存器描述如下:
其中的Physical_Address字段,指定了LPI中断配置表的基地址。
对于LPI配置表,每个LPI中断,占用1个字节,指定了该中断的使能和中断优先级。
当外部发送LPI中断给redistributor,redistributor首先要查该表,也就是要访问memory来获取LPI中断的配置。为了加速这过程,redistributor中可以配置cache,用来缓存LPI中断的配置信息。
因为有了cache,所以LPI中断的配置信息,就有了2份拷贝,一份在memory中,一份在redistributor的cache中。如果软件修改了memory中的LPI中断的配置信息,需要将redistributor中的cache信息给无效掉。
LPI中断状态表
该表,处于memory中,保存了LPI中断的状态,是否pending状态。
LPI中断的状态,不是保存在寄存器中,而是保存在memory中的pending表中。该状态表,由redistributor来进行更改。而该table的基地址,是由软件来设置的。
软件通过设置 GICR_PENDBASER 寄存器来设置。
该寄存器,设置LPI状态表的基地址,该状态表的memory的属性,如shareability,cache属性等。
每个LPI中断,占用一个bit空间
- 0: 该LPI中断,没有处于pending状态
- 1: 该LPI中断,处于pending状态
该状态表,由redistributor来设置。软件如果修改该表,会引发unpredictable行为。
LPI的实现方式
为了实现LPI,GICv3定义了以下两种方法来实现:
- 使用ITS,将外设发送到eventID,转换成LPI 中断号
- forwarding方式,直接访问redistributor的寄存器GICR_SERLPIR,直接发送LPI中断
forwarding方式
这种方式,比较简单,主要由下面几个寄存器来实现:
-
GICR_SERLPIR
-
GICR_CLRLPIR
-
GICR_INVLPIR
-
GICR_INVALLR
-
GICR_SYNCR
其GIC框图如下所示:
GICR_SERLPIR,将指定的LPI中断,设置为pending状态。
GICR_INVLPIR,将指定的LPI中断,清除pending状态。寄存器内容和GICR_SERLPIR一致。
**
**
GICR_INVLPIR,将缓存中,指定LPI的缓存给无效掉,使GIC重新从memory中载入LPI的配置。
GICR_INVALLR,将缓存中,所有LPI的缓存给无效掉,使GIC重新从memory中,载入LPI中断的配置。
GICR_SYNCR,对redistributor的操作是否完成。
寄存器,只有第0bit是有效的。如果为0,表示当前对redistributor的操作是完成的,如果为1,那么是没有完成的。
使用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。
ITS
ITS是一个组件,用来提供给外设,发送LPI中断的,然后将LPI中断,发送给redistributor。
ITS处理流程
ITS使用三类表格,实现LPI的转换和映射:
- device table: 映射deviceID到中断转换表
- interrupt translation table:映射EventID到INTID。以及INTID属于的collection组
- collection table:映射collection到redistributor
当外设往GITS_TRANSLATER寄存器中写数据后,ITS做如下操作:
- 使用DeviceID,从设备表(device table)中选择索引为DeviceID的表项。从该表项中,得到中断映射表的位置
- 使用EventID,从中断映射表中选择索引为EventID的表项。得到中断号,以及中断所属的collection号
- 使用collection号,从collection表格中,选择索引为collection号的表项。得到redistributor的映射信息
- 根据collection表项的映射信息,将中断信息,发送给对应的redistributor
以上是物理LPI中断的ITS流程。虚拟LPI中断的ITS流程与之类似。以下是处理流程图:
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手册获取更多。
以下是CLEAR命令。
ITS table
ITS包括很多个表,这些表均处于 non-secure区域。
GITS_BASER
其中的Physical_Address字段,就指定了表的基地址所在位置。
以下是各个表的基地址,对应的寄存器。
总结
GICv3中,引入了消息中断,并且为之,支持了LPI。分配了大量的中断号,用于LPI。对于LPI的实现,有2种方式,一种是访问redistributor提供的寄存器,一种是使用ITS。
不过对于手机arm cpu来说,其实是不需要LPI的,因为现有的中断,已经符合要求,加入了LPI,让GIC更复杂,让软件操作,也更复杂。但是对于服务器arm cpu,这个,就需要了,因为这个可以和PCIE相连,实现消息中断。个人感觉,这个LPI中断,是为arm服务器cpu,所使用的。
对于提供的GIC IP来说,比如GIC600,是有配置选项,决定,是否是否支持LPI中断,以及是否需要ITS。
ARM GIC(七)GICv3架构-power控制
从GIC3开始,cpu interface放到了PE中,因此cpu interface和PE是同一个power domain。而属于GIC的其他组件,如redistributor,distributor,是另外一个power domain。因此就有如下一种情况,PE和cpu interface的电源给断掉了,而GIC的电源并没有断掉。此时GIC给cpu interface发送数据,cpu interface是不会响应的。
在这种情况下,GIC提供了power管理功能。
GICR_WAKER寄存器
GIC中,提供了如下的 GICR_WAKER 寄存器,来支持power功能。
其寄存器描述如下:
断电cpu interface和PE
在cpu interface和PE要断电之前,软件要保证,通知redistributor,cpu interface和PE要进入low-power状态。软件,要往GICR_WAKER寄存器的ProcessorSleep字段,写入1,表示PE要进入到low-power状态。cpu interface之后将自己置为low-power状态之后,就将ChildrenAseep字段,设置为1。
当GICR_WAKER.ChildrenAsleep为1之后,redistributor,不会在将中断,发送给cpu interface,distributor,在中断仲裁时,也不会考虑该PE。
唤醒cpu interface和PE
当GIC要唤醒cpu interface和PE时,也是操作这个 GICR_WAKER寄存器。
将processorsleep,写入0,然后去唤醒该cpu,最后读取childrenasleep,判断PE是否唤醒成功,
此条目发表在ARM分类目录,贴了GIC标签。将固定链接加入收藏夹。
ARM GIC(八)总结
GIC,是arm为了实现复杂的中断控制,而定义的一套架构。版本也历经了多个变化,从最初的GICv1到现在最新的GICv4。每一个新的版本,都增加了一些新的功能。
目前最新的GIC-600 IP,支持GICv4。
不过从GICv3开始,架构就和之前的架构,变化就比较大了。
变化一:cpu interface
下图是GICv2架构,cpu interface是实现在GIC内部,而且GIC的寄存器,都是memory-mapped方式访问。
下图是GICv3架构,cpu interface从GIC内部剥离,实现在PE的内部。并且将cpu interface的寄存器,提供了系统寄存器访问方式,从而实现中断的快速响应。
变化二:core的标识
GICv3中,对于core的标识,使用了属性层次的方式,来进行标识,从而可以支持更多的core。
而GICv2中,支持最大8个core。
变化三:消息中断
GICv3中,加入了LPI中断类型,来实现消息中断。并且提供了ITS,来实现中断的转换。
变化四:SGI处理
对于SGI的处理,有如下的变化。
总结
GICv3/v4,架构,比GICv2架构,增加了很多的特性,从而支持更复杂的中断管理,支持更多的cpu。
自此,本系列博文到此就要结束了,基本上,除了虚拟中断的相关内容,我将GIC的内容都进行了介绍。希望大家看完这系列博文,能够对GIC有所认识。当初,自己也是看了很多的文档,外加上代码,才对这个理解的。
后面,如果我有去了解过虚拟中断,会在写一系列博文,来介绍虚拟中断。
ARM GIC(九) GICv3的中断分组
GICv3架构中,对中断进行了分组。分成了以下三个组:
-
group0,用于EL3处理的中断
-
secure group1:用于secure EL1处理的中断
-
non-secure group1:用于non-secure的EL2和non-secure的EL1。
对于redistributor的set命令,带有Mod和Grp参数。
Mod与Grp共同表示,中断所属的组。其组合如下图所示:
对于每一组中断,有一个系统寄存器,来控制该组中断是否有效。
- ICC_IGRPEN0_EL1:针对group0的中断
- ICC_IGRPEN1_EL1:针对group1的中断,该寄存器分为non-secure和secure访问,不同的secure下,是访问当前secure下的寄存器
而每个中断的分组,由以下两个寄存器来决定:
-
GICR_IGROUPR
: interrupt group registers -
GICR_IGRPMODR
:interrupt group modifier registers 每个中断,占寄存器中的1个bit,使用中断号进行索引。
当GIC给cpu interface通过set命令发送中断,cpu能够响应该组中断,会回发activate命令,认可该中断。
如果cpu不能响应该组中断,会回发release响应。如下图所示:
GIC给cpu interface通过set命令发送中断,中断号为93,优先级为0x40,Mod和Grp均为1,表示non-secure的group1。
cpu interface不能响应该中断,回发release响应。
ARM GIC(十) GICv3软中断
软中断(software generated interrupts),用来多个核之间的通信(inter-processor communication)。软件通过写SGI寄存器来产生。
-
软件写ICC_SGI1R_EL1产生对应当前secure状态的group1软中断
-
软件写ICC_ASGI1R_EL1产生secure状态的group1软中断
-
软件写ICC_SGI0R_EL1产生secure状态的group0软中断
这三个寄存器的位域是一样的,如下图:
- Aff3.Aff2.Aff1:表示软中断目的CPU的属性层次。
- TargetList:表示要发给哪些CPU。
- INTID:表示软中断号
- IRM:软中断发送,是按照属性层次发送,还是发起其他所有的cpu
- RS:range selector,和TargetList结合,表示发送哪些CPU
例如,IRM为0,INTID为1,Aff3.Aff2.Aff1为0.0.0,TargetList为0xf,RS为0,就表示,软中断发送给属性层次为0.0.0.[0-3]的cpu。
GICv3对软中断的中断号,进行了规定,只能是0-15。
cpu interface SGI命令
软件写SGI寄存器后,cpu interface会通过GIC stream接口,发送SGI命令。
-
SGT:表示写的哪一个SGI寄存器
0b00: ICC_SGI0R_EL1
0b01: ICC_SGI1R_EL1
0b10: ICC_ASGI1R_EL1
0b11: Reserved
-
NS: 当前的secure状态,0表示secure,1表示non-secure
-
IRM: SGI寄存器的IRM bit
-
A3V: aff3是否有效,如果有效,需要发送A3数据。
-
RSV: range selector域是否有效
-
SGInum: 软中断中断号
-
A3,A2,A1: 对应Aff3.Aff2.Aff1
-
TargetList: 对应SGI寄存器中的TargetList
-
Range Selecto: 对应SGI寄存器中的RS域
比如,往ICC_SGI0R_EL1寄存器写0xffffef_ffffffff,那么cpu interface会发送如下波形:
A3V
GICv3中,使用属性层次对CPU进行编号,属性层次最多有4层,最高层为Aff3,这一层可以通过cpu interface系统寄存器来控制,是否使能。
ICC_CTLR_EL3和ICC_CTLR_EL1的A3V表示cpu interface是否支持Aff3,而GICD_TYPER.A3V表示GIC ip是否支持Aff3。
关于range selector的理解。
GICv3的spec对range selector的解释如下:
GIC3中,使用属性层次,来对CPU进行标识,这样可以精确的将中断发送指定的cpu。属性层次最高有4层,为aff3.aff2.aff1.aff0。每一个属性层次,用8bit来表示。如下图所示:
SGI中断,是可以同时发送给多个cpu的中断,通过TargetList来表示要发送给哪一些CPU。而TargetList只有16个bit,也就是只能发送给16个cpu,但是Aff0最多可以表示256个cpu,那怎么发送给其他的cpu的了?
这个时候range selector就派上用场了,RS域共4个bit,可以表示16个范围,16×16=256,刚好表示256个cpu。所以spec里面,说TargetList[n]表示的aff0的值为RS*16 + n。
比如要给65-68号CPU发送软中断,首先判断这些cpu属于的range,为5,那么给这些CPU发送软中断,RS为5,TargetList为0x1E。
GIC的SGI认可响应
当GIC收到cpu interface发的SGI命令,需要回SGI认可响应。
此条目发表在ARM分类目录,贴了GIC标签。将固定链接加入收藏夹。
ARM GIC(十一) GICv3架构-two secure state
GICv3中,引入了支持2种安全状态(secure state),也就是对于中断,根据secure状态,分为安全中断和非安全中断。当然也可以只支持一种安全状态。。
这里的2种安全状态和1种安全状态,主要是影响中断分组,所使用IRQ和FIQ管脚的映射,以及GIC中的寄存器访问。
中断线的映射
当GIC架构,使用GICv3后,中断的传递,和GICv2有所区别。
GICv3中,将cpu interface从GIC中抽离,放入到了cpu中,cpu interface通过GIC stream接口,与GIC进行通信。
当GIC要发送中断,GIC通过GIC stream接口,给cpu interface发送中断命令,cpu interface收到中断命令后,根据中断线映射配置,决定是通过IRQ还是FIQ管脚,向cpu发送中断。
而中断线映射配置,要根据中断的分组以及当前cpu所处的EL以及seucre状态,来决定。
2种安全状态中断线映射
当GIC支持2种安全状态,EL3是AArch64和AArch32,映射情况不同
EL3是AArch64
当EL3是AArch64时,映射如下:
◾对于group0中断,中断线均映射到FIQ
◾对于group1安全中断,secure EL1或EL0,中断线映射到IRQ,其他EL映射到FIQ
◾对于group1非安全中断,secure EL1或EL0以及EL3,中断线映射到FIQ,其他EL映射到IRQ
EL3是AArch32
当EL3是AArch32时,映射如下:
◾对于group0中断,中断线均映射到FIQ
◾对于group1安全中断,secure EL0和EL3,中断线映射到IRQ,其余EL映射到FIQ
◾对于group1非安全中断,secure EL0和EL3,中断线映射到FIQ,其余EL映射到IRQ
1种安全状态中断线映射
映射如下:
◾group0中断线,直接映射到FIQ
◾group1中断线,直接映射到IRQ
GICD寄存器
在GICD中的GICR_CTLR寄存器的DS bit,表示是否支持2种安全模式。
该bit描述如下,如果0,表示支持2种安全状态,为1,表示不支持。
支持2种安全模式下GICD_CTLR
在支持2种安全模式下,GICD中寄存器会进行备份成2份,一份提供给secure访问,。一份提供给non-secure访问。
比如对于GICD_CTLR寄存器,secure访问,寄存器描述如下:
而如果是non-secure访问,其寄存器描述如下:
支持1种安全模式下GICD_CTLR
在1种安全模式下,寄存器描述如下,此时不论是non-secure访问,还是secure访问,都访问的同一个寄存器。
系列其他篇
ARM GIC(十二) 中断bypass
在GICv2架构中,GIC与core之间,是直接通过irq,fiq管脚,传递中断信号。但是在GICv3架构中,GIC通过GIC stream接口向cpu interface传递中断信息,然后由cpu interface向core传递中断信息,而且,cpu interface被设计在了core当中。
GICv3支持中断bypass功能,以下是我画的一个框图:
对于core而言,中断有2种(不考虑虚拟中断),分别是IRQ和FIQ。
在ICC_SRE_ELx寄存器,有DFB和DIB bit,分别控制FIQ,IRQ是否bypass。
对于DIB bit的描述:
对于DFB bit的描述:
这2个bit,复位值都是0,表示系统在复位后,中断bypass功能是开启的,此时外部的中断信号,是直接输入到core中的。
当软件,关闭中断bypass后,此时,才是由cpu interface给core发送中断,而忽略掉外部发送的中断。
在GICv3架构描述中,提到了中断bypass功能,会受到下面几个配置的影响:
一个是,是否运行系统寄存器访问icc寄存器,也就是ICC_CTLR.SRE bit是否为高,如果允许系统寄存器访问icc寄存器,那么中断bypass功能是禁止的。
一个是,ICC_SRE寄存器,由当前设计中支持的最高EL级的ICC_SRE寄存器的DFB和DIB bit来控制。
最后一个,就是中断分组使能,也就是ICC_IGPREN0_EL1和ICC_IGRPEN1_EL1的enable bit。
如果需要开启中断bypass功能,那么需要将中断分组使能给关掉。
以下是FIQ中断bypass的伪代码:
IRQ的处理,和FIQ是一致的。
ARM GIC(十三) 波形为例,介绍GIC600与cpu interface通信
以下以GIC600与cpu interface之间传递的包,来说明,他们之间是如何通信的。这里以波形进行介绍。这样比较直观。
downstream control命令包
GIC600发送downstream control命令包给cpu interface。命令包的数据为0。
downstream control包格式如下:
解释如下:
从表中可以解析,GIC600告诉cpu interface做如下配置:
- VL设置为0,表示vINTID位宽是16bit
- PL设置为0,表示pINTID位宽是16bit
- RSS设置为1,表示SGI的aff0,支持0-15
- DS设置为0,表示支持两种secure模式
cpu interface接收到该命令包后,回一个downstream control acknowledge响应包。包格式如下:
upstream control命令包,设置PMR
cpu interface给GIC600发送upstream control命令包,数据为f8。设置PMR。
该包的格式如下:
对于f8,解析如下:
cpu interface告诉GIC600,当前的ICC_PMR_EL1值设置为f8,也就是将来优先级比这个低的,GIC600就不要发送中断给cpu interface。
GIC600收到upstream control命令包后,回upstream control acknowledge包,格式如下:
upstream control命令包,设置group enable
cpu interface给GIC600发送upstream control命令包,数据为1。设置group enable。
对于数据1,解析如下:
cpu interface告诉GIC600,将grou0的中断使能,group1的secure和non-secure中断不使能。
GIC600收到upstream control命令包后,回upstream control acknowledge包,格式如下:
set命令包,发送中断
GIC600发送set命令包给cpu interface。数据为20。
set命令包格式如下:
GIC600向cpu interface发送了一个中断:
-
Grp为0,Mod为0,表示中断属于group0中断组
-
ID length为0,表示INTID位宽为16bit
-
Priority为0x48,表示中断优先级
-
INTID为0x20,表示中断号为32(SPI中断从32开始)
cpu interface收到该set命令后,发现不能处理该中断,因此发送release响应包,并且INTID指定刚刚GIC600发送的中断号。
该release响应包的各个参数解释如下:
GIC600收到cpu interface的release响应包后,知道之前发送的中断,不能得到cpu interface的响应,在之后,会重新发送该中断。
这里为什么cpu interface不能处理该中断,而是要回release响应包。原因在于中断优先级不符合要求。
对于ICC_PMR_EL1寄存器,GICv3 spec描述如下:
对于中断,GIC架构,最多使用8个bit,来表示中断优先级。当然8个bit可以全用,也可以只用一部分,所以架构提供了5种配置方式。设计可以根据自己的需要,选择一种配置即可。
对于使用的bit不一样,对应的中断优先级值不一样。比如对于使用5个bit,那么就是[7:3],共32种中断优先级。这个选择多少个bit,是硬件决定好了,软件是不能更改的。
软件可以设置这个寄存器,来表示,符合要求的优先级,可以被cpu响应。优先级值越小,表示优先级越高。
假设这个时候,软件往这个寄存器写了8,表示优先级高于8的,都不能被core响应。
假设设计,使用[7:3],表示中断优先级。
此时,发送了优先级为0x48的中断,按照[7:3]bit,进行选择,得到中断优先级为9,高于设置的8,因此这个中断不能被core响应,所以cpu interface直接给GIC600回release响应。
set命令包,发送中断
GIC600发送set命令包给cpu interface。数据为21。
GIC600向cpu interface发送了一个中断:
- Grp为0,Mod为0,表示中断属于group0中断组
- ID length为0,表示INTID位宽为16bit
- Priority为0x38,表示中断优先级
- INTID为0x21,表示中断号为33
cpu interface接收到set命令包,将中断发送给core,core响应中断,读取icc_iar寄存器,表示对该中断认可。中断被core认可后,cpu interface就会给GIC600回activate命令包,表示core认可了该中断。GIC600就可以把该中断状态,更新为active或者active and pending状态。
activate就命令包格式如下:
各个域解析如下:
deactivate命令包,无效中断
core在处理完中断后,会写ICC_EOIR0_EL1或者ICC_EOIR1_EL1寄存器,来无效中断,cpu interface接受core的这个写操作,会向GIC600发送deactivate命令包,表示core对中断处理完毕。
deactivate命令包,格式如下;
各个域解析如下:
GIC600收到cpu interfae发送的deactivate命令后,将自己内部对该中断维护的状态,修改为pending 或者 inactive。
ARM GIC(十四)GICv3架构-power控制详解
在带有GICv3的soc架构中,其框图如下所示:
GICv3中的redistributor与core中的cpu interface通过AXI-Stream进行通信。
connection
当core上电之后,需要将core中cpu interface与GIC中的redistributor进行connect,这样将来GIC才可以将中断发送给core。
connection的流程如下所示:
描述如下:
- 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,表示要connect redistributor
- redistributor在完成connect之后,将GICR_WAKER.ChildrenAsleep位给置低,表示connect完成
- 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,如果不是,表示redistributor还没有完成connect操作,就继续查询
- 如果查询到0,表示connect完成,接着做之后的初始化工作
其汇编代码如下:
波形如下:
首先写GICR_WAKER寄存器,将ProcessorSleep位给置低。
然后读取GICR_WAKER寄存器,判断ChildAsleep位是否为0。在波形中,有读取到0,表示connect成功。
connect成功后,GIC600会给cpu interface发送downstream包,设置vINTID,pINTID,RSS,DS这几个域。
downstream包,对于identifier为0,length为1的解释如下:
disconnection
当core下电之后,需要将core中cpu interface与GIC中的redistributor进行disconnect,这样将来GIC才不会将中断发送给core。
disconnection的流程如下所示:
描述如下:
-
执行在core的程序,先将cpu interface的中断组使能给disable
-
执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,表示要disconnect redistributor
-
redistributor给cpu interface发送 Quiesce包
-
cpu interface清掉内部所有pending的中断
-
清除完毕后,cpu interface回发Quiesce Acknowledge包给redistibutor
-
redistributor收到cpu interface回发的响应之后,将GICR_WAKER.ChildrenAsleep位给置高,表示disconnect完成
-
执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,如果不是,表示redistributor还没有完成connect操作,就继续查询
-
如果查询到1,表示disconnect完成
其汇编代码如下:
其波形如下:
首先写GICR_WAKER寄存器,将ProcessorSleep位给置高,表示要disconect。
然后读取GICR_WAKER寄存器,判断ChildAsleep位是否为0。在波形中,有读取到0,表示disconnect不成功,需要再次读取判断。
GIC600给cpu interface发送Quiesce包。
cpu interface收到该命令包,完成内部的操作后,回发quiesce acknowledge响应包。
至此,完成了disconnect操作,此时再读取GICR_WAKER寄存器,ChildAsleep位就为1了。
中断唤醒core
当core下电之后,GIC就不再会给core发送中断。如果此时有一个中断是唤醒core的,那么其处理流程应该如何了?
在GICv3,为每一个redistributor,提供了WakeRequest输出信号。当GICR_WAKER的ProcessorSleep为1,此时外部有唤醒该core的中断请求,那么WakeRequest信号会被置高。
WakeRequest信号,会被连接到SCP或者PMU,也就是下图中的红色连线。当SCP或者PMU接收到WakeReqeust请求,就会将对应core给上电,然后core再connect redistributor,GIC在将中断发送给该core,core再响应中断。
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/