• 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

    • image.png
    • 从外部(相对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中对应不同类型的中断,都规定了其中断号
    • image.png
    • 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)
    • image.png
    • 对于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;

    1. GIC_Type *gic = get_gic_base();
    2. /* the maximum number of interrupt IDs that the GIC supports */
    3. /* 读出GIC支持的最大的中断号 */
    4. /* 注意: 中断个数 = irq_num * 32 */
    5. irq_num = (gic->D_TYPER & 0x1F) + 1;
    6. /* Disable all PPI, SGI and SPI */
    7. /* 禁止所有的PPI、SIG、SPI中断 */
    8. for (i = 0; i < irq_num; i++)
    9. gic->D_ICENABLER[i] = 0xFFFFFFFFUL;
    10. /* all set to group0 */
    11. /* 这些中断, 都发给group0 */
    12. for (i = 0; i < irq_num; i++)
    13. gic->D_IGROUPR[i] = 0x0UL;
    14. /* all spi interrupt target for cpu interface 0 */
    15. /* 所有的SPI中断都发给cpu interface 0 */
    16. for (i = 32; i < (irq_num << 5); i++)
    17. gic->D_ITARGETSR[i] = 0x01UL;
    18. /* all spi is level sensitive: 0-level, 1-edge */
    19. /* it seems level and edge all can work */
    20. /* 设置GIC内部的中断触发类型 */
    21. for (i = 2; i < irq_num << 1; i++)
    22. gic->D_ICFGR[i] = 0x01010101UL;
    23. /* The priority mask level for the CPU interface. If the priority of an
    24. * interrupt is higher than the value indicated by this field,
    25. * the interface signals the interrupt to the processor.
    26. */
    27. /* 把所有中断的优先级都设为最高 */
    28. gic->C_PMR = (0xFFUL << (8 - 5)) & 0xFFUL;
    29. /* No subpriority, all priority level allows preemption */
    30. /* 没有"次级优先级" */
    31. gic->C_BPR = 7 - 5;
    32. /* Enables the forwarding of pending interrupts from the Distributor to the CPU interfaces.
    33. * Enable group0 distribution
    34. */
    35. /* 使能: Distributor可以给CPU interfac分发中断 */
    36. gic->D_CTLR = 1UL;
    37. /* Enables the signaling of interrupts by the CPU interface to the connected processor
    38. * Enable group0 signaling
    39. */
    40. /* 使能: CPU interface可以给processor分发中断 */
    41. gic->C_CTLR = 1UL;

    } ```

    • Distributor和CPU interface在复位时均被禁用。复位后,必须初始化GIC,才能将中断传递给CPU核。
    • image.png

    参考资料:ARM® Generic Interrupt Controller Architecture Specification Architecture version 2.0(IHI0048B_b_gic_architecture_specification_v2).pdf