- ARM体系结构定义了通用中断控制器(GIC),该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。
- GIC提供了内存映射寄存器,可用于管理中断源和行为,以及(在多核系统中)用于将中断路由到各个CPU核。
- 它使软件能够屏蔽,启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。
- GIC接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致IRQ或FIQ异常发生。
理解GIC,要有多处理器的概念
- 在GIC内部可以分为两部分:分发器(distributor)和CPU接口(interface)
- 对于每个处理器(CPU核),都有一个CPU interface
- 分发器取出最高优先级的中断,发给某一个CPU的interface
- CPU interface再把中断发给具体的处理器
① 分发器(Distributor) 系统中的所有中断源都连接到该单元。可以通过仲裁单元的寄存器来控制各个中断源的属性,例如优先级、状态、安全性、路由信息和使能状态。分发器把中断输出到“CPU接口单元”,后者决定将哪个中断转发给CPU核。 ② CPU接口单元(CPU Interface) CPU核通过控制器的CPU接口单元接收中断。CPU接口单元寄存器用于屏蔽,识别和控制转发到CPU核的中断的状态。系统中的每个CPU核心都有一个单独的CPU接口。 中断在软件中由一个称为中断ID的数字标识。中断ID唯一对应于一个中断源。软件可以使用中断ID来识别中断源并调用相应的处理程序来处理中断。呈现给软件的中断ID由系统设计确定,一般在SOC的数据手册有记录。
对于某个中断,可以设置发给某个CPU,也可以设置发给所有的CPU
- 从外部(相对CPU)进入GIC的中断称为SPI共享外设中断
GIC除了处理SPI,还可以处理SGI软件触发中断、PPI私有外设中断
中断可以有多种不同的类型: ① 软件触发中断(SGI,Software Generated Interrupt) 这是由软件通过写入专用仲裁单元的寄存器即软件触发中断寄存器(ICDSGIR)显式生成的。它最常用于CPU核间通信。SGI既可以发给所有的核,也可以发送给系统中选定的一组核心。中断号0-15保留用于SGI的中断号。用于通信的确切中断号由软件决定。 ② 私有外设中断(PPI,Private Peripheral Interrupt) 这是由单个CPU核私有的外设生成的。PPI的中断号为16-31。它们标识CPU核私有的中断源,并且独立于另一个内核上的相同中断源,比如,每个核的计时器。 ③ 共享外设中断(SPI,Shared Peripheral Interrupt) 这是由外设生成的,中断控制器可以将其路由到多个核。中断号为32-1020。SPI用于从整个系统可访问的各种外围设备发出中断信号。
两个处理器之间想要通信怎么办?
- 处理器之间也可以发信号,这个信号要通过GIC分发给另一个处理器
- 处理器内部除了计算单元外,实际上还有些设备:如systick滴答时钟(每个处理器上都可以有一个定时器);systick可以发出周期性的信号给内核(CPU核)使用
- systick发出的信号应该只给它对应的CPU核使用
- systick发出的信号也是通过GIC再由GIC发回给处理器
- systick就称为CPU核私有的外设
- GIC中对应不同类型的中断,都规定了其中断号
- process0和process7看到的32-1019号中断都是一样的;而16-31号私有外设中断则是不一样的
- 一个中断产生后可以处于多种不同状态:
① 非活动状态(Inactive)–这意味着该中断未触发。
② 挂起(Pending)–这意味着中断源已被触发,但正在等待CPU核处理。待处理的中断要通过转发到CPU接口单元,然后再由CPU接口单元转发到内核。
③ 活动(Active)–描述了一个已被内核接收并正在处理的中断。
④ 活动和挂起(Active and pending)–描述了一种情况,其中CPU核正在为中断服务,而GIC又收到来自同一源的中断。
中断的优先级和可接收中断的核都在分发器(distributor)中配置。外设发给分发器的中断将标记为pending状态(或Active and Pending状态,如触发时果状态是active)。distributor确定可以传递给CPU核的优先级最高的pending中断,并将其转发给内核的CPU interface。通过CPU interface,该中断又向CPU核发出信号,此时CPU核将触发FIQ或IRQ异常。 作为响应,CPU核执行异常处理程序。异常处理程序必须从CPU interface寄存器查询中断ID,并开始为中断源提供服务。完成后,处理程序必须写入CPU interface寄存器以报告处理结束。然后CPU interface准备转发distributor发给它的下一个中断。 在处理中断时,中断的状态开始为pending,active,结束时变成inactive。中断状态保存在distributor寄存器中。
- 对于分发器distributor有一系列的寄存器(GICD_xxx);对于CPU接口interface也有一系列的寄存器(GICC_xxx)
- 对于GIC的代码并不用我们从头开始写,一般厂家都会提供很好的示例(如:nxp的相关SDK中的gic_init)
- 对于每个芯片,GIC的基地址可能都不一样,基地址可以使用协处理命令__asm volatile (“mrc p15, 4, %0, c15, c0, 0” : “=r” (dst)) 从CPU里面读出来,这样我们就不需要去阅读芯片手册来确定了
GIC初始化函数init ```c GIC_Type get_gic_base(void) { GIC_Type dst;
__asm volatile (“mrc p15, 4, %0, c15, c0, 0” : “=r” (dst));
return dst; }
void gic_init(void) { u32 i, irq_num;
GIC_Type *gic = get_gic_base();
/* the maximum number of interrupt IDs that the GIC supports */
/* 读出GIC支持的最大的中断号 */
/* 注意: 中断个数 = irq_num * 32 */
irq_num = (gic->D_TYPER & 0x1F) + 1;
/* Disable all PPI, SGI and SPI */
/* 禁止所有的PPI、SIG、SPI中断 */
for (i = 0; i < irq_num; i++)
gic->D_ICENABLER[i] = 0xFFFFFFFFUL;
/* all set to group0 */
/* 这些中断, 都发给group0 */
for (i = 0; i < irq_num; i++)
gic->D_IGROUPR[i] = 0x0UL;
/* all spi interrupt target for cpu interface 0 */
/* 所有的SPI中断都发给cpu interface 0 */
for (i = 32; i < (irq_num << 5); i++)
gic->D_ITARGETSR[i] = 0x01UL;
/* all spi is level sensitive: 0-level, 1-edge */
/* it seems level and edge all can work */
/* 设置GIC内部的中断触发类型 */
for (i = 2; i < irq_num << 1; i++)
gic->D_ICFGR[i] = 0x01010101UL;
/* The priority mask level for the CPU interface. If the priority of an
* interrupt is higher than the value indicated by this field,
* the interface signals the interrupt to the processor.
*/
/* 把所有中断的优先级都设为最高 */
gic->C_PMR = (0xFFUL << (8 - 5)) & 0xFFUL;
/* No subpriority, all priority level allows preemption */
/* 没有"次级优先级" */
gic->C_BPR = 7 - 5;
/* Enables the forwarding of pending interrupts from the Distributor to the CPU interfaces.
* Enable group0 distribution
*/
/* 使能: Distributor可以给CPU interfac分发中断 */
gic->D_CTLR = 1UL;
/* Enables the signaling of interrupts by the CPU interface to the connected processor
* Enable group0 signaling
*/
/* 使能: CPU interface可以给processor分发中断 */
gic->C_CTLR = 1UL;
} ```
- Distributor和CPU interface在复位时均被禁用。复位后,必须初始化GIC,才能将中断传递给CPU核。
参考资料:ARM® Generic Interrupt Controller Architecture Specification Architecture version 2.0(IHI0048B_b_gic_architecture_specification_v2).pdf