armv8 - GIC-V2 中断控制器

GIC起源

上一节中,粗略讲了hylicos上用的armv7上的一个通用中断控制器,其只支持60个中断源。但现代SoC上,中断系统正变得越来越复杂,旧的中断控制器已经无法胜任这些系统,主要体现在以下几点上:

  1. 中断源越来越多,有的系统中断源有几百个,甚至上千个。
  2. 中断类型越来越多,比如普通外设中断,软件触发中断,CPU Core之间的中断,还有类似于PCIe上的基于消息传递的中断等
  3. 虚拟化技术的引入,主要开始支持虚拟中断

针对这些需求,arm开发了GIC,专门管理中断。这里以GIC-V2作例子讲解,GIC-V2已经支持虚拟化,典型的IP核心为GIC-400。

中断状态,触发方式,硬件中断号

每一个中断支持的状态有:

  1. 不活跃状态(inactive): 中断处于无效状态
  2. 等待状态(pending):中断处于有效状态,但是等待CPU响应该中断
  3. 活跃状态(active):CPU已经响应该中断
  4. 活跃并等待(active and pending):CPU正在处理该中断,但是该中断源又发了中断过来。

针对外设中断,有两种触发方式:

  • 边沿触发
  • 电平触发

GIC会为每一个硬件外设,也就是每一个硬件中断源,都分配一个中断号,即硬件中断号。

中断类型 中断号范围
SGI 0 ~ 15
PPI 16 ~ 31
SPI 32 ~ 1019

GIC-V2

以ARM Vexpress V2P-CA15_CA7为例子,GIC和core cluster之间的关系
GIC-400同时支持两个cpu cluster
armv8 - GIC-V2 中断控制器_第1张图片
GIC-400支持多种中断类型:

  1. SGI - 软件触发中断,通常用于多核之间的通信。 GIC-V2最多支持16个SGI中断,中断号范围为0~15。SGI通常在linux内核中被用作处理器之间的中断(Inter-Processor Interrupt IPI),被送达指定CPU上。
  2. PPI - 私有外设中断,每个处理器内核私有的中断。 最多吃吃16个PPI,16 ~ 31。PPI会被送达到指定的CPU上处理,典型的,CPU的local timer interrupt就是PPI的。
  3. SPI - 共享外设中断,所有处理器共享的中断。可以支持988个外设中断。32 ~ 1019.

GIC 组成

主要由分发器(distributor)和CPU接口组成。
分发器:主要用于仲裁和分发中断请求给CPU

  • 全局中断使能
  • 每个中断的使能
  • 中断的优先级
  • 中断的分组
  • 中断的目的core
  • 中断触发方式
  • 对于SGI中断,传输中断到指定的core
  • 每个中断的状态管理 提供软件,可以修改中断的pending状态

CPU接口:负责与CPU内核连接,通过nIRQ和nFIQ与CPU Core交流

  • 将中断请求发送给cpu
  • 对中断进行认可(acknowledging an interrupt)
  • 中断完成识别(indicating completion of an interrupt)
  • 设置中断优先级屏蔽
  • 定义中断抢占策略
  • 决定当前处于pending状态最高优先级中断

armv8 - GIC-V2 中断控制器_第2张图片

中断流程

GIC 检测当前的中断流程如下:

  1. 当GIC检测到一个中断发生时,会将该中断标记为pending
  2. 对于每一个pending状态的中断,分发器负责为其确定目标CPU,将请求发送到该CPU
  3. 对于每个CPU,分发器会从众多pending状态的中断中,选择一个优先级最高的中断请求发送到与目标CPU对接的CPU接口。
  4. CPU接口会决定这个中断是否可以发送给目标CPU。如果优先级满足,GIC会发送一个中断请求给目标CPU。
  5. GIC进入中断异常,读取GICC_IAR来响应这个中断(由软件的异常处理程序负责,这个过程也被叫做中断认可)。GICC_IAR里保存的是硬件中断号。对于SGI来说,返回源CPI的ID。
  6. GIC在感知到软件读取该寄存器后会做如下处理:
    a. 如果中断处于pending,则pending --> active
    b. 如果中断重新产生,则pending --> active and pending
    c. 如果中断处于active,则active --> active and pending
  7. 处理器执行完中断的处理,发送一个EOI信号给GIC。

GIC-V2寄存器

寄存器也分为两部分:

  1. 分发器的寄存器:包括中断的设置和配置
  2. CPU接口的寄存器:CPU相关的特殊设置

GIC-V2的寄存器,名称以n结尾的,有n个。比如GICD_ISENABLERn寄存器有n个。这些寄存器每个寄存器上的每一个bit都用于描述一个中断源是否使能,因为中断源非常多,因此需要很多个这样的寄存器。GIC-V2支持1020个中断号,所以需要1020/32,即32个这样的寄存器,每个寄存器占用4字节偏移,所以其地址空间是0x100 ~ 0x100 + 0x4*31(0x7c)。

对于中断号m来说,要判断其是否使能:

n = m / 32;
off_bit = m mod 32;

这里n就是它所在寄存器GICD_ISENABLERn名称上的n。以此可以算出GICD_ISENABLERn所在的地址偏移为:

0x100 + n * 0x4

不是每个寄存器,都是用1bit来描述一个中断源,1bit描述的信息太少,有的寄存器需要以多bit来描述一个中断源的信息:
GICD_ITARGETSRn,用于描述一个中断源所能路由的目标CPU。
armv8 - GIC-V2 中断控制器_第3张图片
类似的,n的计算公式

n = m / 4;

寄存器偏移计算公式:

0x800 + n * 4

distributor部分

armv8 - GIC-V2 中断控制器_第4张图片

CPU Interface部分

armv8 - GIC-V2 中断控制器_第5张图片

中断路由

由于SGI和PPI都是cpu core私有的,中断路由只为SPI的中断号工作。分发器来负责中断路由,将SPI中断路由到不同的CPU Core上。如何配置路由?
GICD_ITARGETSRn就是做路由这件事的,通过配置GICD_ITARGETSRn可以指定中断号m配分发到指定的CPU Core上。GICD_ITARGETSRn是个32位的寄存器,每8位用于描述可为一个中断源处理的CP有哪些U,最多支持描述8个CPU ID,每一bit都描述一个CPU ID。某一bit被置位,说明对应的中断源可以被分发到以该索引值为CPU ID的cpu core上。

注:由于SGI和PPI的目标CPU Core是固定的,所以前32个中断源的路由配置是硬件配置死的。33 ~ 1019号中断是可以自由配置的。

GIC-400的配置

GIC-400的初始化流程

  1. 设置分发器和CPU接口寄存器组的基地址
  2. 读取GICD_TYPER,计算当前支持最多多少个中断源
  3. 初始化分发器
    a. 关闭分发器
    b. 设置SPI(比如串口中断)的路由
    c. 设置SPI的触发类型,例如,边沿触发
    d. 关闭所有中断源
    e. 重新打开分发器
  4. 初始化CPU接口
    a. 设置GIC_CPU_PRIMASK寄存器,配置优先级
    b. 配置GICC_CTLR寄存器,打开CPU接口

响应中断的流程

  1. 中断发生,CPU跳转到异常向量表
  2. 跳转到GIC中断函数,例如gic_handle_irq()函数
  3. 读取GICC_IAR,获取中断号
  4. 根绝中断号,进行相应中断处理,例如,读取的中断号是30 ,在树莓派4B上,就是PNS定时器触发了中断,跳转到定时器中断处理函数,处理中断。
  5. 中断返回。

你可能感兴趣的:(嵌入式Linux,操作系统,个人笔记,linux,arm开发)