arm GIC介绍之二

本文摘自 sunsissy 的《arm GIC介绍之一》:https://blog.csdn.net/sunsissy/article/details/73842533

        GIC是ARM架构中及其重要的部分,本文只在公开ARM对应资料基础上,以MTK开发板为基础整理。个人理解之后记录,巩固和加深认识,仅此而已,如果有错误,欢迎指出,转载只为方便整理,请大家支持原创。

连接"arm GIC介绍之一",我们补充对应的GIC 寄存器描述,和主要的用法说明,仅供参考。 

3 . GIC_V3寄存器介绍

我们把完整的图放在这里:



 从这里看到,GIC分为3个主要的部分,要完成其作用,而且给用户进行预先配置的方法,GIC就提供了一寄存器,这些寄存器的设计就是围绕着这些主要的功能来实现的

3.1Distributor Registers

在前面介绍过,Distributor主要完成对应的IRQ状态的记录,对应的IRQ分发,那么围绕这样的功能,对应的寄存器主要有:



这里面有Distributor控制寄存器,控制Distributor使能和关闭,以及对应的具体IRQ的状态寄存器,比如GICD_ISACTIVER,这里记录某一个IRQ是否ACTIVE,还有逻辑功能寄存器,比如GICD_IPRIORITY,这里记录对应

IRQ的优先级别。这些都以GICD_*为前缀打头。我们来仔细说明。


GICD_CTLR




这个是GICD_CTLR寄存器,名字和对应的位功能来看是对Distributor控制使能,比如BIT_1,就是使能GROUP_1,


并且是NON-SECURE,如果是LINUX系统,就是KERNEL空间。一般来说对这个寄存器每个位都是打开的。BIT_31是等待回应,当配置对应寄存器了,写入到硬件,需要生效,那么可以读取这个位来看是否已经写入配置信息并生效。ARE是对应的AFFINITY,其实就是分配到哪个具体的CPU,我们在ROUTER寄存器说明时候来补充。

GICD_TYPER


这个是GICD_TYPER寄存器,其主要作用是获取SOC平台厂商提供的GIC的具体信息,现在一般提供信息是该GIC能够支持最多多少个IRQ硬件中断源。


 



ITLinesNumber:

SPI numbers in specification, butthis should include (SGI, PPI actually)

比如我们现在X20平台上都出来并且换算出来ITLinesNumber: 0xb,那么每个数字表示有32IRQ      

12* 32-1= 383 IRQ numbers.

支持384IRQ,从0383,为最大数字。

最大支持

(0x1f+1)*32-1 =1023 

1024个中断,这个GIC现在通用的说明是一致的,如果不考虑LPI的话。

GICD_IGROUP&GICD_IGRPMODR

这两个在一起使用,一个IRQ对应一个BITGICD_IGROUP,以及一个BITGICD_IGRPMODR

所以如果支持384IRQ,那么真个GIC就需要384BIT来表示384GICD_IGRPMODR,同理其它对应的

BIT方式表示的也是如此。


这两个结合起来表示某个具体的IRQ应该送到哪个Ex,是安全域还是非安全域,现在一般只支持3个,如上图表示。


GICD_IROUTER


这个寄存器的名字不好理解,其实,如果我们修改下GICD_TARGET,这样,就容易理解了。简单来说,该寄存器控制一个IRQ发到哪个CPU进行处理。在现在的ARM中,cpu的拓扑结构用AFFINITY来表示。



CPUCLUSTER为单位进行管理,比如X20上有10CPU,其中4个小核为一个CLUSTER_2,另外4CPU

CLUSTER_1,两个大核为CLUSTER_0,这样有利于对CPU的上电,频率等管理控制,尤其是CPUOFF时候,当一个CLUSTER里面的CPUOFF了,那么就以CLUSTER为单位把对用的电压给关闭或做其它处理,时钟进行对应的GATE,当然这只是考虑的部分因素,这部分需要参考Big.Little进行参考。我们在这里需要知道对应的管理结构,或者说

拓扑结构就可以了,在ARM中有对应MIPDR寄存器表示对应的CPU处于哪个SYSTEM,哪个CLUSTER,以及在CLUSTER里面的对应的编号,如图中的右上角说明。

所以,对于一个IRQ,需要知道发送给哪个CPU处理,就靠GICD_IROUTER里面的信息来决策。


现在一般只使用AFFINITY 0~2,如果系统过于复杂,可能使用到AFFINITY 3,BIT 31其实是个特殊开关,如果设置1,那么来了一个IRQ,这个IRQ就给所有的CPU发送。在1*N模型中,一个IRQ可以发送给多个CPU但是只能由其中一个处理,至于是具体哪个CPU先发现并处理,并通知其它CPU,这个在ARM的文档中说明是自定义实现,也就是SOC厂商可以在硬件上自己决策使用具体的策略和方式,这里不说明,另外一个原因目前无法获取到具体SOC厂商在这块的具体策略和实现方法的细节。另外一个是软件上可以进行处理互斥,保证当前CPU处理一个IRQ,另外的CPU不处理。另外一个模型是N*N,也就是一个IRQ信号可以发送给多个CPU,这多个CPU都可以处理,现在大多厂商没有采取这种方式。而是用前者。

如果BIT 31没有设置为1,那么具体发送到哪个CPU就由AFFINITY 0/1/2来决定了,具体的对应关系见图中不同颜色的连接线。


GICD_IPRIORITYR

优先级寄存器



对于IRQ,如果只有一个到来,送给CPU处理,这个比较简单。但是实际情况远比这种场景复杂,比如某一个中断在PENDING状态,等CPU处理,这个时候来了另外一个IRQ,而这个IRQ的紧迫性更高,实际上我们想打断之前的等待流程,让这个新的IRQ插队,那么就需要给不同IRQ设置不同优先级,来表示对用的处理优先级。

在现在的GIC_V3中,一个IRQ优先级可以从0255,用8BIT来表示,见上图中红色圈出来的部分,所以一个32位的寄存器,其实可以表示4IRQ的优先级。那么数字越小,级别越高,0表示最高,255最低,我们图中给出例子。

IRQ_34的优先级别值为0b:10100000IRQ_33的优先级别值为0b:10100001IRQ_34的值比IRQ_331,所以级别就高。

但是在实际中,我们不需要这么多的级别范围255,我们需要32个级别,或者64个级别就可以了,这就需要对级别LEVEL进行优化,可以忽略其低位的BIT,现在使用的X20上每个LEVELSTEP8,忽略低3位,这是ARM寄存器设计的写入忽略原则,以及读取为0原则。途中右下部分,我们做的简单实验,对优先级寄存器里面写入0b:11111111,但是都出来的是0b:11111000,也就是最低3位被忽略了。那么LEVEL_0是从0~7LEVEL_1是从8~15

所以:原先IRQ_34的优先级别值为0b:10100000IRQ_33的优先级别值为0b:10100001IRQ_34的值比IRQ_331

但是实际能够配置到寄存器里面的值都是0b:10100000。优先级别是一样的。总的原则不变,还是值低的优先级高。所以设置时候需要使用者注意。

GICD_SGIR

这个寄存器作用是产生软件IRQ,现在GIC_V3以及之后的版本,放到CPU INTERFACE侧,这里先提示下。

3.2Redistributor Registers

对于Redistributor对应的寄存器,以GICR*为前缀打头。这里面涉及到的一部分是功能和开关寄存器,比如GICR_TYPER,GICR_CTLR。另外有针对SGIPPI的状态优先级寄存器,由于GIC_V3后这32IRQ的状态和对应的设置挪到Redistributor,所以增加了对应的GICR_ISACTIVER, GICR_IGROUPER0等,作用和Distributor里面对应的SPIIRQ)寄存器一样,这里不重复,我们只说明几个特殊的寄存器,来解释Redistributor里特殊的功能。


 GICR_CTLR


因为是每个CPU都对应一个Redistributor,所以这些寄存器上面的大多数设置都对应单个CUP,比如DPG0,表示对应的CPU是否要接受送到G0的中断,同理DPG1NSDPG1S,这些并不影响其它CPU接受并在安全域和非安全域处理对应的中断。

UWP这个其实是个Distributor通信状态的确认,这在不同芯片厂商是自己定义的,比如使用一组硬件上的信号来表示不同的状态,从一般的使用者层面来说感知不到具体的两者之间的通信交互,所以大多数开发者可以不关心这个位的设置。

 

GICR_TYPER


GICR_TYPER提供了对应的Redistributor信息,比如是否支持PLPISVLPIS,这些可能大家都用不到,可以先不用去管它。其中processor number,并且之前介绍的复杂的AFFINITY管理方式关闭后,从看到的资料理解上来说,可以直接用这里面编号来作为目标CPU,比如这里的编号15,就表示第16CPU。目前这个寄存器我们没有看到对应的使用,都是默认使能的。


GICR_WAKER


在介绍这个寄存器之前,我们介绍下WAKE UP方式。


举个例子,当设置IRQ_100这个中断固定发送到CPU_N上处理之后,实际中来个一个对应的中断信号,而CPU_N在之前因为无任务处理,进入了省电模式,其对应的TIMERGATED,对应的电压也被POWER DOMAIN给处理降低了或者关闭了。而新的架构(GIC_V3)后CPU INTERFACE以及对应的寄存器部分都和CPU在同一个电压DOMAIN中,那么,这个时后明显是不能把IRQ直接发送到CPU INTERFACE侧,然后再给CPUIRQ信号线发送信号的。那么Redistributor就需要发送一个Signal wakerequest,请求对应的POWERDOMAIN CONTROLLER先把电压给加上,TIMER给开启,CPU和对应的INTERFACE能够工作,这个时候再发送IRQ信号给其处理。这就是WAKER的开关作用。


ChildrenAsleep是读取出来的,其中的值表示1的话,说明不能发送IRQ,对应的INTERFACE休眠,发了对方也不会理会的,0就是相反作用,这个一般是读信息,不写,使用者可以读出来为0的就表示可以和这个CPU正常交互。

ProcessorSleep,就是我们介绍的WAKER方式,如果为0,表示设置CPU没有处于低电模式,1表示处于进入模式或处于低电。可以发送WakeRequest信号。

其实,这两个定义看起来比较模糊,并不明确,在ARM的资料上介绍的话那么是提到设计的理念,也就是唤醒后再处理,具体实现时候,还是IMPDEF,也就似乎IMPLEMENTED DEFINE,芯片厂家自己定义实现。

我们看下具体使用:


其实这里代码实现很简洁,不管CPU是否处于低电模式(这里第一次开启,一般是出于低电),那么设置对应PROCESSORSLEEP0,以后就可以发送WAKE REQUEST去唤起,这应该会有硬件信号去唤醒INTERFACE,等待一段时间之后查看CHILDRENASLEEP来判断INTERFACE是否已经唤醒起来了。

以上介绍Redistributor对应的寄存器。

3.3 CPUInterface


说到INTERFACE,我们看下一个IRQ的流程:


IRQ100到来,其中断优先级别为0xA0,如果优先级别不冲突,那么久经过Redistributor,送到对应的CPU Interface侧,和GIC_PMR寄存器里面设定的门槛优先级比较。高于对应的优先级,那么和正在处理的IRQ的优先级GIC_RPR寄存器值比较,也高于正在处理的IRQ,那么就抢断,如果没有正在处理的IRQ,那就不存在抢断。IRQ放到GIC_IAR寄存器里面,产生一个电信号给CPUIRQ_input引脚, CPU读取GIC_IAR里面的中断号,并处理,之后向GIC_EOI寄存器里面写入IRQ,表示IRQ处理结束。

至此,一个中断处理完成。

那么这里面就涉及到了CPU Interface侧的对应的寄存器,我们主要列举如下:

    1. 分别介绍。

      CPU Interface  对应的寄存器以GICC*为前缀。

       

      GICC_CTLR


      这个寄存器功能简单,控制FIQ/IRQBYPASS,并且EOI的配置。这个有点特殊,按照一般的解释,当IRQCPU处理完后,向GICC_EOIR里面写入IRQ号时候,那么表示中断流程完成,同时,状态要变成DE-ACTIVE,注意,是自动变成。但是EOIMODE这个配置有意思,可以决定这个“自动”的变化,如果取消“自动变为DE-ACTIVE”,那么需要使用者向GICC_DIR里面写入,这样才能把一个IRQ的最终状态变为DE-ATCIVE。目前看,这个没有使用到,也不需要手动写入GICC_DIR,从流程的逻辑性来看,这个手动写入具体用在什么样的特殊场景,还没有看到,也是个疑惑的地方。

       

       

      GICC_PMR


      这个寄存器从字面看,简单理解,中断只有优先级比这里面设置的高,才能继续向CPU侧传递,其中级别也是优化后的,只用32LEVEL表示。之前在描述Distributor不再重复说明。

       

      GICC_RPR


      这个存器记录正在处理的IRQ的优先级别,主要是用来中断抢占使用的.



    2. 正常情况下,我们看上图,低电平表示中断在处理,当IRQ_AIRQ_B两个中断到来,先后处理即可。并不会产生冲突,即使IRQ_B的优先级别更高。


    3. 即使说,当IRQ_A先到来在PENDING状态并且已经送到CPUINTERFACE侧等待CPU确认并处理,我们如果关闭抢占的话,这个时候IRQ_B到来,也不会造成一项,IRQ_B优先级别再高也得等待之前的IRQ_A处理后再进行处理。

      那么,如果抢占打开了,怎样呢?GICC_RPR就是用来做这样的信息记录和比较的。


    4. IRQ_A先到来,并且优先级高于GICC_PMR,那么得到处理,此时GICC_RPR里面的优先级从默认的0XFF变为对应的优先级,假如是0XB0;那么处理中,IRQ_B到来,优先级别也满足GICC_PMR的要求,并且值0XA0,高于GICC_RPR里面记录的IRQ_A的优先级,那么就抢占处理,执行IRQ_B对应的服务,结束之后,再继续完成对应的IRQ_A的服务。如果IRQ_A也服务完成了,那么GICC_RPR里面恢复成默认的OXFF值。

       

      GICC_IAR

       

      GICC_IAR寄存器,里面记录着等待处理的中断号,CPU从这里面读取获知到来的硬件中断号,并处理,随机对应的GIC中记录的IRQ状态从PENDING变为ACTIVE或者PENDING& ACTIVE

       

      GICC_SGI

      SGI产生软件IRQ

    5. INITD表示对应IRQ号,这里一般从IRQ0IRQ15

      IRM 0时候,需要依靠TARGETLIST来确定目标CPU,如果为1的时候,那么就是广播发送,但是不给自身发送软中断。

      TARGETLIST这里不在重复说明,只是注意,TARGETLIST里面只是表示在一个具体的CLUSTER里面的CPU编号,CLUSTER号由AFF1表示,这个TARGETLIST相当于AFF0

      在使用时候也比较简单:


      在系统INIT时候,图左侧红色框内,会使用set_smp_cross_call来注册系统的IPI消息发送函数,这里是gic_raise_softirq,那么右侧红色框内,发送一个特定IPI消息时候,当调用到__smp_cross_all时候就会链到绿色线所指的gic_raise_softirq,来产生一个IPI消息(SGI),见下面代码。

       

      GICC_EOIR

      最后介绍EOI,这个之前提到过,中断处理完成,写入对应的终端号,表示IRQ处理结束,IRQ状态变为DE-ACTIVE

猜你喜欢

转载自blog.csdn.net/wenfei11471/article/details/80303750