GIC相关

来源:互联网 发布:python实现串口通信 编辑:程序博客网 时间:2024/06/06 14:11

原文地址:www.wowotech.net/irq_subsystem/gic_driver.html

GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1~V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64服务器系统结构)。目前在ARM官方网站只能下载到Version 2的GIC architecture specification,因此,本文主要描述符合V2规范的GIC硬件及其驱动。

具体GIC硬件的实现形态有两种,一种是在ARM vensor研发自己的SOC的时候,会向ARM公司购买GIC的IP,这些IP包括的型号有:PL390,GIC-400,GIC-500。其中GIC-500最多支持128个 cpu core,它要求ARM core必须是ARMV8指令集的(例如Cortex-A57),符合GIC architecture specification version 3。另外一种形态是ARM vensor直接购买ARM公司的Cortex A9或者A15的IP,Cortex A9或者A15中会包括了GIC的实现,当然,这些实现也是符合GIC V2的规格。

本文在进行硬件描述的时候主要是以GIC-400为目标,当然,也会顺便提及一些Cortex A9或者A15上的GIC实现。

本文主要分析了linux kernel中GIC中断控制器的驱动代码(位于drivers/irqchip/irq-gic.c和irq-gic-common.c)。 irq-gic-common.c中是GIC V2和V3的通用代码,而irq-gic.c是V2 specific的代码,irq-gic-v3.c是V3 specific的代码,不在本文的描述范围。本文主要分成三个部分:第二章描述了GIC V2的硬件;第三章描述了GIC V2的初始化过程;第四章描述了底层的硬件call back函数。

注:具体的linux kernel的版本是linux-3.17-rc3。

 

二、GIC-V2的硬件描述

1、GIC-V2的输入和输出信号

(1)GIC-V2的输入和输出信号示意图

要想理解一个building block(无论软件还是硬件),我们都可以先把它当成黑盒子,只是研究其input,output。GIC-V2的输入和输出信号的示意图如下(注:我们以GIC-400为例,同时省略了clock,config等信号):

gic-400 

(2)输入信号

上图中左边就是来自外设的interrupt source输入信号。分成两种类型,分别是PPI(Private Peripheral Interrupt)和SPI(Shared Peripheral Interrupt)。其实从名字就可以看出来两种类型中断信号的特点,PPI中断信号是CPU私有的,每个CPU都有其特定的PPI信号线。而SPI是所有CPU之间共享的。通过寄存器GICD_TYPER可以配置SPI的个数(最多480个)。GIC-400支持多少个SPI中断,其输入信号线就有多少个SPI interrupt request signal。同样的,通过寄存器GICD_TYPER也可以配置CPU interface的个数(最多8个),GIC-400支持多少个CPU interface,其输入信号线就提供多少组PPI中断信号线。一组PPI中断信号线包括6个实际的signal:

(a)nLEGACYIRQ信号线。对应interrupt ID 31,在bypass mode下(这里的bypass是指bypass GIC functionality,直接连接到某个processor上),nLEGACYIRQ可以直接连到对应CPU的nIRQCPU信号线上。在这样的设置下,该CPU不参与其他属于该CPU的PPI以及SPI中断的响应,而是特别为这一根中断线服务。

(b)nCNTPNSIRQ信号线。来自Non-secure physical timer的中断事件,对应interrupt ID 30。

(c)nCNTPSIRQ信号线。来自secure physical timer的中断事件,对应interrupt ID 29。

(d)nLEGACYFIQ信号线。对应interrupt ID 28。概念同nLEGACYIRQ信号线,不再描述。

(e)nCNTVIRQ信号线。对应interrupt ID 27。Virtual Timer Event,和虚拟化相关,这里不与描述。

(f)nCNTHPIRQ信号线。对应interrupt ID 26。Hypervisor Timer Event,和虚拟化相关,这里不与描述。

对于Cortex A15的GIC实现,其PPI中断信号线除了上面的6个,还有一个叫做Virtual Maintenance Interrupt,对应interrupt ID 25。

对于Cortex A9的GIC实现,其PPI中断信号线包括5根:

(a)nLEGACYIRQ信号线和nLEGACYFIQ信号线。对应interrupt ID 31和interrupt ID 28。这部分和上面一致。

(b)由于Cortext A9的每个处理器都有自己的Private timer和watch dog timer,这两个HW block分别使用了ID 29和ID 30

(c)Cortext A9内嵌一个global timer为系统内的所有processor共享,对应interrupt ID 27

关于private timer和global timer的描述,请参考时间子系统的相关文档。

关于一系列和虚拟化相关的中断,请参考虚拟化的系列文档。

 

(3)输出信号

所谓输出信号,其实就是GIC和各个CPU直接的接口,这些接口包括:

(a)触发CPU中断的信号。nIRQCPU和nFIQCPU信号线,熟悉ARM CPU的工程师对这两个信号线应该不陌生,主要用来触发ARM cpu进入IRQ mode和FIQ mode。

(b)Wake up信号。nFIQOUT和nIRQOUT信号线,去ARM CPU的电源管理模块,用来唤醒CPU的

(c)AXI slave interface signals。AXI(Advanced eXtensible Interface)是一种总线协议,属于AMBA规范的一部分。通过这些信号线,ARM CPU可以和GIC硬件block进行通信(例如寄存器访问)。

 

(4)中断号的分配

GIC-V2支持的中断类型有下面几种:

(a)外设中断(Peripheral interrupt)。有实际物理interrupt request signal的那些中断,上面已经介绍过了。

(b)软件触发的中断(SGI,Software-generated interrupt)。软件可以通过写GICD_SGIR寄存器来触发一个中断事件,这样的中断,可以用于processor之间的通信。

(c)虚拟中断(Virtual interrupt)和Maintenance interrupt。这两种中断和本文无关,不再赘述。

为了标识这些interrupt source,我们必须要对它们进行编码,具体的ID分配情况如下:

(a)ID0~ID31是用于分发到一个特定的process的interrupt。标识这些interrupt不能仅仅依靠ID,因为各个interrupt source都用同样的ID0~ID31来标识,因此识别这些interrupt需要interrupt ID + CPU interface number。ID0~ID15用于SGI,ID16~ID31用于PPI。PPI类型的中断会送到其私有的process上,和其他的process无关。SGI是通过写GICD_SGIR寄存器而触发的中断。Distributor通过processor source ID、中断ID和target processor ID来唯一识别一个SGI。

(b)ID32~ID1019用于SPI。 这是GIC规范的最大size,实际上GIC-400最大支持480个SPI,Cortex-A15和A9上的GIC最多支持224个SPI。

 

2、GIC-V2的内部逻辑

(1)GIC的block diagram

GIC的block diagram如下图所示:

 gic

GIC可以清晰的划分成两个block,一个block是Distributor(上图的左边的block),一个是CPU interface。CPU interface有两种,一种就是和普通processor接口,另外一种是和虚拟机接口的。Virtual CPU interface在本文中不会详细描述。

 

(2)Distributor 概述

Distributor的主要的作用是检测各个interrupt source的状态,控制各个interrupt source的行为,分发各个interrupt source产生的中断事件分发到指定的一个或者多个CPU interface上。虽然Distributor可以管理多个interrupt source,但是它总是把优先级最高的那个interrupt请求送往CPU interface。Distributor对中断的控制包括:

(1)中断enable或者disable的控制。Distributor对中断的控制分成两个级别。一个是全局中断的控制(GIC_DIST_CTRL)。一旦disable了全局的中断,那么任何的interrupt source产生的interrupt event都不会被传递到CPU interface。另外一个级别是对针对各个interrupt source进行控制(GIC_DIST_ENABLE_CLEAR),disable某一个interrupt source会导致该interrupt event不会分发到CPU interface,但不影响其他interrupt source产生interrupt event的分发。

(2)控制将当前优先级最高的中断事件分发到一个或者一组CPU interface。当一个中断事件分发到多个CPU interface的时候,GIC的内部逻辑应该保证只assert 一个CPU。

(3)优先级控制。

(4)interrupt属性设定。例如是level-sensitive还是edge-triggered

(5)interrupt group的设定

Distributor可以管理若干个interrupt source,这些interrupt source用ID来标识,我们称之interrupt ID。

 

(3)CPU interface

CPU interface这个block主要用于和process进行接口。该block的主要功能包括:

(a)enable或者disable CPU interface向连接的CPU assert中断事件。对于ARM,CPU interface block和CPU之间的中断信号线是nIRQCPU和nFIQCPU。如果disable了中断,那么即便是Distributor分发了一个中断事件到CPU interface,但是也不会assert指定的nIRQ或者nFIQ通知processor。

(b)ackowledging中断。processor会向CPU interface block应答中断(应答当前优先级最高的那个中断),中断一旦被应答,Distributor就会把该中断的状态从pending状态修改成active或者pending and active(这是和该interrupt source的信号有关,例如如果是电平中断并且保持了该asserted电平,那么就是pending and active)。processor ack了中断之后,CPU interface就会deassert nIRQCPU和nFIQCPU信号线。

(c)中断处理完毕的通知。当interrupt handler处理完了一个中断的时候,会向写CPU interface的寄存器从而通知GIC CPU已经处理完该中断。做这个动作一方面是通知Distributor将中断状态修改为deactive,另外一方面,CPU interface会priority drop,从而允许其他的pending的interrupt向CPU提交。

(d)设定priority mask。通过priority mask,可以mask掉一些优先级比较低的中断,这些中断不会通知到CPU。

(e)设定preemption的策略

(f)在多个中断事件同时到来的时候,选择一个优先级最高的通知processor


(4)实例

我们用一个实际的例子来描述GIC和CPU接口上的交互过程,具体过程如下:

xxx

(注:图片太长,因此竖着放,看的时候有点费劲,就当活动一下脖子吧)

首先给出前提条件:

(a)N和M用来标识两个外设中断,N的优先级大于M

(b)两个中断都是SPI类型,level trigger,active-high

(c)两个中断被配置为去同一个CPU

(d)都被配置成group 0,通过FIQ触发中断

下面的表格按照时间轴来描述交互过程:

时间交互动作的描述T0时刻Distributor检测到M这个interrupt source的有效触发电平T2时刻Distributor将M这个interrupt source的状态设定为pendingT17时刻大约15个clock之后,CPU interface拉低nFIQCPU信号线,向CPU报告M外设的中断请求。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会修改成M interrupt source对应的IDT42时刻Distributor检测到N这个优先级更高的interrupt source的触发事件T43时刻Distributor将N这个interrupt source的状态设定为pending。同时,由于N的优先级更高,因此Distributor会标记当前优先级最高的中断T58时刻大约15个clock之后,CPU interface拉低nFIQCPU信号线,向CPU报告N外设的中断请求。当然,由于T17时刻已经assert CPU了,因此实际的电平信号仍然保持asserted。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会被更新成N interrupt source的IDT61时刻软件通过读取ack寄存器的内容,获取了当前优先级最高的,并且状态是pending的interrupt ID(也就是N interrupt source对应的ID),通过读该寄存器,CPU也就ack了该interrupt source N。这时候,Distributor将N这个interrupt source的状态设定为pending and active(因为是电平触发,只要外部仍然有asserted的电平信号,那么一定就是pending的,而该中断是正在被CPU处理的中断,因此状态是pending and active)
注意:T61标识CPU开始服务该中断T64时刻3个clock之后,由于CPU已经ack了中断,因此GIC中CPU interface模块 deassert nFIQCPU信号线,解除发向该CPU的中断请求T126时刻由于中断服务程序操作了N外设的控制寄存器(ack外设的中断),因此N外设deassert了其interrupt request signalT128时刻Distributor解除N外设的pending状态,因此N这个interrupt source的状态设定为activeT131时刻软件操作End of Interrupt寄存器(向GICC_EOIR寄存器写入N对应的interrupt ID),标识中断处理结束。Distributor将N这个interrupt source的状态修改为idle
注意:T61~T131是CPU服务N外设中断的的时间区域,这个期间,如果有高优先级的中断pending,会发生中断的抢占(硬件意义的),这时候CPU interface会向CPU assert 新的中断。T146时刻大约15个clock之后,Distributor向CPU interface报告当前pending且优先级最高的interrupt source,也就是M了。漫长的pending之后,M终于迎来了春天。CPU interface拉低nFIQCPU信号线,向CPU报告M外设的中断请求。这时候,CPU interface的ack寄存器(GICC_IAR)的内容会修改成M interrupt source对应的IDT211时刻CPU ack M中断(通过读GICC_IAR寄存器),开始处理低优先级的中断。

 FIQIRQ的区别:

FIQIRQ是两种不同类型的中断,ARM为了支持这两种不同的中断,提供了对应的叫做FIQIRQ处理器模式(ARM7种处理模式)。

一般的中断控制器里我们可以配置与控制器相连的某个中断输入是FIQ还是IRQ,所以一个中断是可以指定为FIQ或者IRQ的,为了合理,要求系统更快响应,自身处理所耗时间也很短的中断设置为FIQ,否则就设置了IRQ

如果该中断设置为了IRQ,那么当该中断产生的时候,中断处理器通过IRQ请求线告诉ARMARM就知道有个IRQ中断来了,然后ARM切换到IRQ模式运行。类似的如果该中断设置为FIQ,那么当该中断产生的时候,中断处理器通过FIQ请求线告诉ARMARM就知道有个FIQ中断来了,然后切换到FIQ模式运行。

简单的对比的话就是FIQIRQ快,为什么快呢?

1ARMFIQ模式提供了更多的banked寄存器,r8r14还有SPSR,而IRQ模式就没有那么多,R8,R9,R10,R11,R12对应的banked的寄存器就没有,这就意味着ARMIRQ模式下,中断处理程序自己要保存R8R12这几个寄存器,然后退出中断处理时程序要恢复这几个寄存器,而FIQ模式由于这几个寄存器都有banked寄存器,模式切换时CPU自动保存这些值到banked寄存器,退出FIQ模式时自动恢复,所以这个过程FIQIRQ快。

2FIQIRQ有更高优先级,如果FIQIRQ同时产生,那么FIQ先处理。

3FIQ的中断向量地址在0x0000001C,而IRQ的在0x00000018(也有的在FFFF001C以及FFFF0018),写过完整汇编系统的都比较明白这点的差别,18只能放一条指令,为了不与1C处的FIQ冲突,这个地方只能跳转,而FIQ不一样,1C以后没有任何中断向量表了,这样可以直接在1C处放FIQ的中断处理程序,由于跳转的范围限制,至少少了一条跳转指令。

4IRQFIQ的响应延迟有区别

IRQ的响应并不及时,从Verilog仿真来看,IRQ会延迟几个指令周期才跳转到中断向量处,看起来像是在等预取的指令执行完。FIQ的响应不清楚,也许比IRQ快。 

 中断延迟:从外部中断请求信号发出到执行对应的中断服务程序ISR的第一条指令所需要的时间。通过软件程序设计来缩短中断延迟的方法有:中断优先级和中断嵌套。



原创粉丝点击