linux kernel 中断子系统之(一)-- ARM GIC 硬件


一个系统中,中断是很重要的组成部分之一,有了中断,系统才可以不用一直轮询(polling)是否有事件发生,系统效率才得以提高,而且对中断的控制又通常分散在各个地方,不同的部分由不同功能的程序控制,做到了各司其职,配合无误,系统才能正常工作。一般系统中,中断控制分为三个地方:模块、中断控制器、处理器,模块通常有寄存器可以控制是否使能中断功能,中断触发条件等;中断控制器可以管理中断的优先级等,而处理所有中断的处理器则有寄存器设置是否响应中断。

1. 全景

作为 ARM 系统中通用中断控制器的是 GIC(Generic Interrupt Controller) ,目前有四个版本,V1~V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64系统结构)。目前在ARM官方网站只能下载到Version 2的GIC architecture specification。其中GIC-500最多支持128个 cpu core,它要求ARM core必须是ARMV8指令集的(例如Cortex-A57),符合GIC architecture specification version 3。本文主要描述的是GIC-400,它更适合嵌入式系统,符合version 2的GIC architecture specification。GIC-400通过AMBA(Advanced Microcontroller Bus Architecture)这样的片上总线连接到一个或者多个ARM processor上。先看一张 GIC 在系统中的作用全景图(overview)。

 技术分享

从这张图中可以看出, GIC 是联系外设中断和 CPU 的桥梁,也是各 CPU 之间中断互联的通道(也带有管理功能),它负责检测、管理、分发中断,可以做到:

使能或禁止中断;

把中断分组到组 还是组 (安全系统中使用 FIQ , Group0 , Group1 作为非安全系统使用,连接 IRQ );

多核系统中将中断分配到不同处理器上;

设置电平触发还是边沿触发方式(不等于外设的触发方式);

虚拟化扩展;

ARM CPU 对外的连接只有 个中断,一个是 IRQ ,一个是 FIQ ,相对应的处理模式分别是一般中断( IRQ )处理模式和快速中断( FIQ )处理模式。所以 GIC 最后要把中断汇集成 条线,与 CPU 对接。

2. 详细设计

介绍完总体的印象,下面要介绍详细的部分了,在介绍前,先了解 GIC 中涉及的几个概念, GIC 中中断类型有3种:SGI(Software-generated interrupt)PPI(Private peripheral interrupt )SPI(Shared peripheral interrupt)

SGI: SGIs are generated by writing to the Software Generated Interrupt Register, GICD_SGIR. Each CPU interface can generate a maximum of 16 SGIs, ID0-ID15, for each target processor. SGI为软件可以触发的中断,统一编号为0~15,用于各个core之间的通信。

PPI: A PPI is an interrupt that is specific to a single processor. All PPI signals are active-LOW level-sensitive. Table 2-3shows the PPIs that are available for each processor. PPI为每个 core 的私有外设中断,统一编号为 16-31 ,例如每个 CPU 的 local timer 即 Arch Timer 产生的中断就是通过 PPI 发送给 CPU 的(安全为 29, 非安全为 30)。

SPI: SPIs are triggered by events generated on associated interrupt input lines. The GIC-400 can support up to 480 SPIs corresponding to the external IRQS[479:0]signal. The number of SPIs available depends on the implemented configuration of the GIC-400. The permitted values are 0-480, in steps of 32. SPIs start at ID32.You can configure whether each SPI is edge-triggered on a rising edge or is active-HIGH level-sensitive. SPI 是系统的外设产生的中断,为各个 core 公用的中断,统一编号为 32~1019 ,如 global timer 、 uart 、 gpio 产生的中断。

还是先看图:

 技术分享

从图中可以看出, GIC 可以清晰的划分成两个部分:分发器( Distributor )和 CPU 接口(CPU Interface )CPU interface有两种,一种就是和普通processor接口,另外一种是和虚拟机接口的。Virtual CPU interface在本文中不会详细描述。分发器其实应该叫汇聚器,在 IC 的后端设计中, layout 会把各个模块引过来的中断线(就是上面说的三种中断)混接到 GIC 上,然后把混聚合的中断接到 CPU 的 IRQ 和 FIQ 线上,这样 CPU 就有触觉了。

其中 Distribute 只有一套,故基地址也只有一个,而 Interface 有多套,因为每个 CPU 都对应一套 interface 。所以严格来说, GIC 最后输出的线的个数应该是 CPU 的个数 * 2 ,但是通常设计时,保证了每个 CPU 看到的 interface 的基地址也是相同的,程序上看到的基地址也是相同的。

其中各个子中断使能,设置触发方式,优先级排序,分发到哪个 CPU 上这些功能由 Distribute 负责,总的中断的使能,状态的维护由 Interface 负责。

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。

 

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的中断,那么CPU interface就会deassert nIRQCPU和nFIQCPU信号线。如果在这个过程中又产生了新的中断,那么Distributor就会把该中断的状态从pending状态修改成pending and active。这时候,CPU interface仍然会保持nIRQ或者nFIQ信号的asserted状态,也就是向processor signal下一个中断。

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

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

(e)设定preemption的策略

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


不同系统设置中断的亲和性是不同的,比如网络服务器中,总是希望尽快的响应网络请求,功耗不是很在乎,因此可以将不同中断分配到不同 CPU 上,以尽快响应中断,而嵌入式系统中,对功耗很在乎,通常要把中断绑定到 CPU0 上,这样其他 CPU 空闲时,可以进入 IDLE 状态,以节省功耗,ARM CPU 正常运行时,只使用 Arch Timer,不会使用 Global Timer ,即系统的 Timer 不会运行。但 CPU 电源被关闭后,此 CPU 的 Arch Timer 也会被关闭,因此, CPU 进入关电的 IDLE 状态时,要先将其 Timer 切换至 Global Timer ,这样在下一个 Timer 到来时,如果是非启动 CPU ,会有响应 Global Timer 中的 CPU 发送的 SGI 中断,有中断后,会有其他模块将其从断电状态中带出,如果是 CPU0 ,同样收到中断后,其他模块将其带出断电状态,不过此时的中断不再是 SGI ,而是 Global Timer 的中断。

通常 Distribute 属于系统电源域,而interface属于 CPU 电源域,所以每次 CPU 断电重新上电(系统不断电)后, Interface 都要恢复原值或重技术分享

新初始化。另外如果要让 CPU 一直在 WFI 状态,则需要设置 Interface 的 GICC_CTLR 寄存器为 0 ,否则CPU会从 WFI 状态中退出。

寄存器在安全和非安全模式读到值是不同的,这并不是说明安全和非安全状态下,同一个地址的寄存器存在备份(就像 IRQ 和 FIQ 模式下,有些寄存器存在备份),而是因为寄存器的不同 bit 的属性不同,有些属于安全寄存器的 bit 在非安全状态下读到的为 ,所以 CPU 复位后,在未切换到非安全系统前,要先设置 GIC ,否则安全系统不能响应中断,即安全系统不能使用, CPU 就会停在哪里静静的休息,此时看到的现象是: CPU 在 WFI 状态,系统的时钟也是停止的, 因为 CPU 的 Arch-Timer 得不到响应, tick 计数不会改变。

3. 中断的处理流程

中断的处理流程是:distributor会把收集来的中断,先缓存一下,依次把优先级最高的那个interrupt请求送往CPU interfaceCPU读取一个中断,其实就是读取interface的一个寄存器,只不过这个寄存器存放的是中断号,这时候中断的状态由pending转为activecpu处理完了以后,将中断号写入GICinterface,告诉GIC处理完了,可以将这个中断清理了。

 技术分享

 

查看系统上的中断是怎么分配在 CPU 情况和响应次数的命令

# cat /proc/interrupts

 

 

4. 寄存器

  

 技术分享

技术分享


参考:

DDI0471B_gic400_r0p1_trm.pdf

IHI 0048B, ARM Generic Interrupt Controller Architecture Specification, Ver 2.0.pdf

http://www.wowotech.net/linux_kenrel/gic_driver.html

http://blog.csdn.net/velanjun/article/details/8757862

http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/index.html

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。