014 - ARM64上的GIC-400(GICv2)

本章节涉及到的参考文档有三个:
  • BCM2711 ARM Peripherals.pdf
  • ARM Generic Interrupt Controller Architecture Specification.pdf (简称gic_v2)
  • CoreLink GIC-400 Generic Interrupt Controller Technical Reference Manual.pdf

1. GIC 发展历史

在早期的 ARM 系统中,比如ARM7,ARM9 都是采用单核处理器设计,比如STM32,三星的2410。基本上几个简单的寄存器就可以描述中断源的使能,关闭以及状态。

树莓派4B上默认的 legacy 中断控制器就是采用了类似的实现。当然了树莓派4B上的中断源比较多,可以采用多级串联的方式来实现。三星的2410也是采用多级串联的方式实现。

但随着Soc 越来越复杂,中断源越来越多,中断类型也越来越多,以及现在比较流行的支持虚拟化等等因素。 ARM 公司开发了 GIC (Generic Interrupt Controller) 专门来管理中断。

2. GIC 通用常识

2.1. 中断类型

  • SGI : 软件产生的中断 (Software Generated Interrupt), 软中断即软件产生的中断,用于给其他CPU核心发送中断信号。
  • PPI: 私有外设中断(Private Peripheral Interrupt), 私有的外设中断,该中断时某个指定的CPU 独有的。
  • SPI : 共享外设中断 (Shared Peripheral Interrupt), 共享的外设中断,也就是我们常说的外设中断,所有CPU都可以访问这个中断。
  • LPI: 本地特殊外设中断(Locality-specific Peripheral Interrupt), GICv3 新增的中断类型,基于消息传递的中断类型。

2.2. 中断触发方式

  • 边沿触发(edge-triggered): 当中断源产生一个上升沿或者下降沿时,触发一个中断。
  • 电平触发(level-triggered): 当中断信号产生一个高电平或者低电平时,触发一个中断。

2.3. 中断状态

  • 不活跃状态(inactive): 中断处于无效状态。
  • 等待状态(pending): 中断处理有效状态,但是等待 CPU 响应该中断。
  • 活跃(active) : CPU 相应该中断。
  • 活跃并等待状态(active and pending): CPU 正在相应该中断的同时该中断源又发送新的中断过来。

2.4. GIC-V2中断控制器

GIC -V2 有两个硬件单元组成的。

  • Distributor :分发器,对应的 Distributor Registers(GICD_) 包含了中断设置和配置
  • CPU interface : CPU接口,每个CPU内核有一个CPU接口。对应的 CPU Interface registers(GICC_) 包含了CPU 相关的特殊设置。

2.5. 中断路由

GIC-V2 最多支持 8 个CPU.

GICD_ITARGETSRn 寄存器用来配置 Distributor 可以把中断路由到 哪个CPU 上,

  • 每 8bit 表示一个中断源,每个 bit 代表能路由到的 CPU
  • 每个 bit 设置了,说明该中断源可以路由到这个 CPU 上
  • 前 32 个中断源的路由配置时硬件设置好的,权限是只读RO
  • 第 33~1019 号中断,可以有软件来配置其路由,权限是读写RW

2.6. 中断流程

GIC检测中断

GIC检测中断的流程如下:

  1. 当GIC 检测到一个中断发生时,会将该中断状态从inactive状态标记为pending状态。
  2. 对于处在penging状态的中断,分发器会确定目标 CPU, 将中断请求发给这个CPU。
  3. 对于每个 CPU, 分发器会从众多处于等待状态的中断中选择一个优先级最高的中断,发送到目标CPU 的 CPU 接口。
  4. CPU 接口会决定这个中断是否可以发送给CPU, 如果这个中断的优先级满足要求,GIC 会发送一个中断请求信号给 CPU.
  5. CPU 进入中断异常, 读取 GICC_IAR 来响应该中断(一般是由 Linux 内核的中断处理程序来读寄存器)。寄存器会返回硬件中断号(hardware interrupt ID)。对于 SGI 来说,返回源CPU 的ID (source processor ID) 。当GIC感知到软件读取了该寄存器后,根据如下情况处理。
    1. 如果该中断源处于pending 状态,则将该中断状态切换到 active 状态
    2. 如果该中断又重新产生,那么该中断状态则变成 active and pending 状态
  6. 如果该中断正在忙,正在处理其他中断, 则该中断状态其切换为 active and pending 状态,等待CPU将当前当前的中断处理结束之后,再将该中断切换到 active 状态
  7. 处理器完成中断服务,发送一个完成信号结束中断(End of Interupt,EOI) 给 GIC。该中断状态再切换到 inactive 状态。

当然,实际情况可能更复杂。

3.2.4 Interrupt handling state machine, ARM Generic Interrupt Controller Architecture Specification.pdf

GIC中断时序图

GIC 支持中断优先级抢占功能。一个高优先级的中断可以抢占一个处于 active 状态的低优先级中断,即 GIC 的分发器会先找出并记录当前优先级最高并且处于 peding 的中断,然后抢占当前的中断服务,转而先处理高优先级中断,上述内容是从 GIC 角度分析的。总之,GIC 的分发器总会把 pending 状态中优先级最高的中断请求发送给 CPU。

从 Linxu 内核角度分析,如果在低优先级的中断处理程序中发生了 GIC 抢占,虽然 GIC 会发送高优先级中断请求给 CPU, 但是CPU处于关中断的状态,需要等到CPU开中断时才会响应高优先级中断。这一点需要注意。

B1. Interrupt signaling in the GIC-400 with physical interrupts only, CoreLink GIC-400 Generic Interrupt Controller Technical Reference Manual.pdf

假设中断N和中断M 都是 SPI 类型的外设中断且通过快速中断请求(Fast Interrupt Request, FIR) 来处理,高电平触发,N的优先级比M的高,它们的目标CPU 相同。

  • T1时刻,GIC 的分发器检测到中断M的电平变化。
  • T2时刻,分发器设置中断 M 的状态为 pending.
  • T17 时刻,CPU接口会拉低 nFIQCPU[n] 信号来向 CPU 报告中断请求。分发器需要这些时间来计算哪个是 pending状态下的优先级最高的中断。
  • T42时刻,分发器检测到另外一个更高优先级更高的中断 N.
  • T43时刻,分发器用中断 N 来替换中断 M, 作为当前pengding 状态下最高的中断,并设置中断 N 处于 pending 状态。
  • T58时刻,经过 tph个时钟周期之后,CPU 接口拉低 nFIQCPU[n] 信号来通知 CPU。nFIQCPU[n] 信号在T17时刻已经被拉低。 CPU接口会更新 GICC_IAR的 ID 字段,该字段的值变成中断 N 的硬件中断号。
  • T61 时刻,CPU (Linux 内核的中断服务程序) 读取GICC_IAR, 即软件响应了中断 N, 这时分发器把中断 N 的状态从 pending 状态切换为 active and pending 状态。
  • T61-T131 时刻,Linux 内核处理中断 N 的中断服务程序。
    • T64时刻,在中断 N 被 Linux 内核响应后的 3个时钟周期内,CPU接口完成对 nFIQCPU[n] 信号的复位,即拉高 nFIQCPU[n]信号。
    • T126时刻,外设也复位了中断 N。
    • T128时刻,退出了中断 N 的 Pending 状态。
  • T131时刻,处理器(Linux内核中断服务程序)把中断 N 的硬件ID 写入 GICC_EOIR来完成中断 N 的全部处理过程。
  • T146 时刻,在向GICC_EOIR 写入中断N 硬件 ID 后的 tph 个时钟周期后,分发器会选择下一个优先级最高的中断,即中断M, 发送中断请求给 CPU 接口。CPU接口拉低 nFIQCPU[n] 信号来向 CPU 报告中断 M 的请求。 CPU接口会更新 GICC_IAR的 ID 字段,该字段的值变成中断 M 的硬件中断号。
  • T211 时刻,CPU(Linux 内核中断服务程序)读取GICC_IAR 来响应该中断,分发器设置中断M的状态为 active and pending 状态。
  • T214时刻,在 CPU 响应中断后的3个时钟周期内,CPU 接口拉高 nFIQCPU[n] 信号来完成复位动作。

2.7. GIC-v2 寄存器

不考虑虚拟化的话,GIC 的寄存器可以分为两组:

  • D: 表示 Distributor 相关的寄存器, 以 ”GICD_“ 打头的寄存器
  • C: 表示 CPU interface 相关的寄存器,以 ”GICC_“ 打头的寄存器

Chapter 4.1 Distributor register map, ARM Generic Interrupt Controller Architecture Specification.pdf

Chapter 4.1 CPU interface register map, ARM Generic Interrupt Controller Architecture Specification.pdf

GIC-V2寄存器有个特点:名称以n 结束的寄存器会有 n个。比如 GICD_ISENABLERn寄存器就有 n 个GICD_ISENABLER 寄存器。

第一种:一个bit表示一个中断号类型的寄存器

GICD_ISENABLERn寄存器是用来使能某个中断号的,并且是按照中断号来描述的。

Chapter 4.1 Distributor register map, ARM Generic Interrupt Controller Architecture Specification.pdf

此寄存器的偏移量是 0x100~0x17c。 总共有n = (0x17c-0x100)/4 = 31 个寄存器。分别是 GICD_ISENABLER0 到 GICD_ISENABLER31。

对于中断号 m 来说,我们需要计算中断m对应的是哪个寄存器,也就是说我们要计算偏移多少。

Chapter 4.3.5 Interrupt Set-Enable Registers, GICD_ISENABLERn, ARM Generic Interrupt Controller Architecture Specification.pdf

GICD_ISENABLERn寄存器中每bit表示一个中断源。所以 n=m/32。 那么对于中断号 m 的GICD_ISENABLERn寄存器的地址就是 (0x100+4n)。

假设 m = 50, 即50号中断。 n = 50/32=1。 即GICD_ISENABLER1,偏移地址是(0x100+4*1)

第二种:8bits表示一个中断源类型的寄存器

GICD_ITARGETsRn 寄存器 使用 8位表示一个中断源所能路由的目标CPU有哪些。

Chapter 4.3.5 Interrupt Set-Enable Registers, GICD_ISENABLERn, ARM Generic Interrupt Controller Architecture Specification.pdf

  • 前32个中断的Target reg 是固定的,只读。
  • 后面的32~1019个中断的 Target 是软件可以设置的。

Chapter 4.3.12 Interrupt Processor Targets Register, GICD_ITARGETSRn, ARM Generic Interrupt Controller Architecture Specification.pdf

Chapter 4.3.12 Interrupt Processor Targets Register, GICD_ITARGETSRn, ARM Generic Interrupt Controller Architecture Specification.pdf

对于中断 m来说,这个寄存器 n的计算公式变成了 n =m/4。这里是取整计算。 寄存器的偏移量等于(0x800+4n)。

假设 m =50, 即50号中断。 n=50/4=12。 即GICD_ITARGETSR12。寄存器的偏移量是 0x800 +4*12 = 0x830。

3. 树莓派4B 上的 GIC-400

树莓派4B上集成了 GIC-V2架构的GIC-400。 GIC-400 上的具体中断号的分配和Soc 芯片的实现相关。

6.3 GIC-400 interrupt controller, BCM2711 ARM Peripherals.pdf

树莓派上的GIC-400分配有5种情况:

  • ARM Core 中断组:所有的PPI的中断组以及部分 SPI 寄存器组,包括ARM 核心上的通用寄存器,例如,PNS(non-secure physical)定时器的中断号就是30
    • Core n PS(Secure Physical) 定时器中断,PPI
    • Core n PNS(Non-Secure Physical) 定时器中断,PPI
    • Core n HP(Hypervisor) 定时器中断,PPI
    • Core n V(Virtual) 定时器中断,PPI
    • Core n PMU(Performance Monitoring Unit) 中断,SPI

  • ARM local 的中断,这部分都是SPI中断,例如, 16个 ARM 邮箱中断号为 32~47, CPU 内核0 上的 PMU 中断号是48
  • ARMC 外设中断组,CPU和GPU都可以访问的中断。这部分都是SPI中断。他们对应的中断号为64~79,比如常见的软件中断
  • VC(VideoCore)外设中断组,对应的中断号为96~159。这部分都是SPI中断。比如树莓派4B的 system timer 0 的中断号是96。相应的中断寄存器也是在 ARMC 寄存器组里。
  • 与PCIe相关的中断,对应中断号为 160~216。这部分都是SPI中断,

这些中断是如何映射到GIC-400 中的呢?

ARMC 外设中断组中每个中断定义如下

Chapter 6.2.3 ARMC interrupt, BCM2711 ARM Peripherals.pdf

GIC-400 分配的 ARMC peripheral IRQs 的 中断号是从 64-79,直接映射过去即可,比如 Timer 在ARMC中断组的id是0 ,那么映射到GIC-400就是64。 Software Interrupt 7 在ARMC组的id是15, 那么映射到GIC-400就是79。

另外,ARMC 中断组的寄存器 IRQn_PENDING2 每个bit 表示一个中断。

Chapter 6.5.3 ARMC, BCM2711 ARM Peripherals.pdf

VC(VideoCore) 外设中断组详细中断号如下

Chapter 6.2.4. VideoCore interrupts, BCM2711 ARM Peripherals.pdf

GIC-400 分配的 VC(VideoCore) peripheral IRQs 的 中断号是从 96-159, 那么System Timer 1 映射到 GIC-400,中断号就是97。

另外,VC 中断组的寄存器IRQn_PENDING0, IRQn_PENDING1 每个bit 表示一个中断。总共64个。

Chapter 6.5.3 ARMC, BCM2711 ARM Peripherals.pdf

PCIe 的中断组详细的中断号如下

Chapter 6.2.5. ETH_PCIe interrupts, BCM2711 ARM Peripherals.pdf

4. 访问树莓派上 GIC-400 寄存器

树莓派4B有两种地址模式

  • 高地址模式,此模式下GIC-400的基地址是0x4C0040000
  • 低地址模式, 此模式下GIC-400的基地址是0xFF840000

一般我们使用低地址模式(enable low peripheral),对应的基地址是0x0 FF84 0000。

Chapter 6.5.1 GIC-400, BCM2711 ARM Peripherals.pdf

然后,我们需要继续查看 GIC-V2的手册来确定 Distributor 和 CPU interface 的偏移量。

3.2 GIC-400 register map, CoreLink GIC-400 Generic Interrupt Controller Technical Reference Manual.pdf

从上图可以知道, Distributor 和CPU interface 的基地址分别是 0x1000和0x2000。

最后,在查看对应的寄存器的偏移量。寄存器的偏移量可以参考[[014 - GIC-400#2.5. GIC-v2 寄存器]]

我们继续使用中断 m 来举例, 假设通过[[014 - GIC-400#3. 树莓派4B 上的 GIC-400]]c 参考到中断M的中断号是 50。

我们来计算下,中断 m 对应的 GICD_ISENABLER1 和 GICD_ITARGETSR12 在树莓派4B设置为低地址模式下的访问地址。

GICD_ISENABLER1 的访问地址 = GIC-400 的基地址+ GIC-400 的 Distributor的偏移量+ 指定寄存器的偏移量 = 0x FF84 0000 + 0x100 + (0x100+4*1) = 0x FF84 0204

GICD_ITARGETSR12 的访问地址 = GIC-400 的基地址+ GIC-400 的 Distributor 的偏移量+ 指定寄存器的偏移量 = 0xFF84 0000 + 0x100 +(0x800+4*\12) = 0xFF84 093C

这些地址的来源总结下:

  • GIC-400 的基地址: 来自树莓派的手册,Chapter 6.5.1 GIC-400, BCM2711 ARM Peripherals.pdf
  • GIC-400 的 Distributor/CPU 的偏移量 : 来自 gic -v2, 3.2 GIC-400 register map, CoreLink GIC-400 Generic Interrupt Controller Technical Reference Manual.pdf
  • GIC-400 的 指定寄存器的偏移量 : 来自 gic -v2, 4.3 Distributor register descriptions, ARM Generic Interrupt Controller Architecture Specification 以及 4.4 CPU interface register descriptions, ARM Generic Interrupt Controller Architecture Specification.pdf

“GICC_” 打头的寄存器也可以这样计算访问地址。

5. GIC-400 中断处理流程

5.1. GIC-400 初始化

  1. 设置Distributor 和 CPU interface 寄存器组的基地址
  2. 读取 GICD_TYPER寄存器,计算当前GIC 最大支持多少个中断源
  3. 初始化 Distributor
    1. Disable Distributor
    2. 设置SPI 中断的路由
    3. 设置SPI 中断的触发类型,例如 level触发
    4. Disactive and disable 所有的中断源
    5. Enable distributor

  1. 初始化 CPU interface
    1. 设置 GIC_CPU_PRIMASK, 设置中断优先级mask level
    2. Enable CPU interface

5.2. 注册中断

  1. 初始化外设
  2. 查找该外设的中断在 GIC-400 的中断号,例如 PNS timer 的中断号为30
  3. 设置 GIC_DIST_ENABLE_SET 寄存器enable 这个中断号
  4. 打开设备相关的中断,例如树莓派上的 generic timer, 需要打开ARM_LOCAL 寄存器组的 TIMER_CNTRL0 寄存器种相关的 enable 位
  5. 打开 CPU 的 PSTAE 中 I 位(PSTATE.I)

5.3. 中断响应

  1. 中断发生
  2. 异常向量表
  3. 跳转到GIC中断函数里,gic_handle_irq()
  4. 读取GIC_IAR 寄存器,获取中断号
  5. 根据中断号来进行相应中断处理,例如读取的中断号为30,说明是 PNS 的 generic timer,然后跳转到generic timer 的处理函数里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值