[ARM]GIC相关知识

ARM GIC

GICv2-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的中断分组

  • group0,用于EL3处理的中断
  • secure group1:用于secure EL1处理的中断
  • non-secure group1:用于non-secure的EL2和non-secure的EL1

每一组中断,有一个系统寄存器,来控制该组中断是否有效。

  • 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,使用中断号进行索引。

中断

中断状态

对于每一个中断而言,有以下4个状态:

  • inactive:中断处于无效状态
  • pending:中断处于有效状态,但是cpu没有响应该中断
  • active:cpu在响应该中断
  • active and pending:cpu在响应该中断,但是该中断源又发送中断过来
中断触发方式

中断触发方式,包含以下两种方式:

  • 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:中断开始
  • generate:中断源产生中断,发送给gic
  • deliver:gic将中断发送给cpu
  • activate:cpu响应该中断
  • deactivate: cpu响应完中断,告诉gic,中断处理完毕,gic更新该中断状态
  • end:中断结束
banking

banking(不知翻译成啥比较合适)功能,包括以下两个:

1、中断banking:对于PPI和SGI,gic可以有多个中断对应于同一个中断号。比如在soc中,有多个外设的中断,共享同一个中断号。

2、寄存器banking: 对于同一个gic寄存器地址,在不同的情况下,访问的是不同的寄存器。例如在secure和non-secure状态下,访问同一个gic寄存器,其实是访问的不同的gic的寄存器。

cortex-A 处理器中断

ARM cortex-A系列处理器,提供了4个管脚给soc,实现外界中断的传递。分别是:

  • nIRQ: 物理普通中断
  • nFIQ: 物理快速中断
  • nVIRQ: 虚拟普通中断
  • nVFIQ: 虚拟快速中断

LPI

gicv3中,引入了消息中断,并且为之,支持了LPI。分配了大量的中断号,用于LPI。对于LPI的实现,有2种方式,一种是访问redistributor提供的寄存器,一种是使用ITS

消息中断

外设,不在通过专用中断线,向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,和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。
外设,通过写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,指定ITS表的基地址和大小。软件,在使用ITS之前,必须要配置。

其中的Physical_Address字段,就指定了表的基地址所在位置。
以下是各个表的基地址,对应的寄存器。

GIC电源管理

gic3开始,cpu interface放到了PE中,因此cpu interface和PE是同一个power domain。而属于gic的其他组件,如redistributor,distributor,是另外一个power domain。因此就有如下一种情况,PE和cpu interface的电源给断掉了,而gic的电源并没有断掉。此时gic给cpu interface发送数据,cpu interface是不会响应的。

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。

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完成
唤醒cpu interface和PE

当gic要唤醒cpu interface和PE时,也是操作这个 GICR_WAKER寄存器。
将processorsleep,写入0,然后去唤醒该cpu,最后读取childrenasleep,判断PE是否唤醒成功,

connection
  • 当core上电之后,需要将core中cpu interface与gic中的redistributor进行connect,这样将来gic才可以将中断发送给core
  • 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,表示要connect redistributor
  • redistributor在完成connect之后,将GICR_WAKER.ChildrenAsleep位给置低,表示connect完成
  • 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,如果不是,表示redistributor还没有完成connect操作,就继续查询
  • 如果查询到0,表示connect完成,接着做之后的初始化工作
中断唤醒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再响应中断。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值