GIC控制器(二)
目录
GIC结构框图
中断类型
外部中断触发方式
边缘触发(Edge-triggered)
电平敏感(Level-sensitive)
中断状态
中断分派模式
1-N 模式
N-N 模式
处理器安全状态
GIC逻辑分块
分发器(GIC Distributor)
GICD Memory-Map
CPU接口(CPU Interface)
GICC Memory-Map
编写IRQ异常处理框架
前言:结合GIC控制器(一)-CSDN博客 由于协处理器CP15的CBAR寄存器 保存 GIC(通用中断控制器)的基地址,当 IRQ 中断发生时,处理器会跳转到IRQ入口执行,汇编启动文件IRQ异常需要处理的任务如下:
- 保存上下文(寄存器状态),通过 CBAR寄存器获取GIC控制器的基地址;
- 通过GIC 的某个寄存器获取中断 ID,确定是哪个中断源;
- 跳转到 C 语言的中断处理函数;
- 中断处理函数执行完成后,通过 GIC 的某个寄存器清除中断状态;
- 最后恢复上下文,返回被中断的程序继续执行;
由于 GICv2 广泛应用于 Cortex-A 系列处理器,基于前文基础,本文将对GICv2控制器展开深入讨论;
GIC 接收众多的外部中断,然后对其进行处理,最终通过FIQ(快速中断)信号与IRQ(普通中断) 报给 ARM 内核,本文仅讨论 IRQ,所以相当于 GIC 最终向 ARM 内核上报一个 IRQ 信号;
参考资料
Cortex-A7 MPCore Technical Reference Manual
ARM Generic Interrupt Controller Architecture version 2.0
GIC结构框图
中断类型
- SGI (Software-generated Interrupt),软件中断,由软件触发引起的中断,采用中断号标识中断源的唯一性,SGI中断号为0~15,系统会使用 SGI 中断来完成多核之间的通信;
- GIC 架构中,外设中断(Peripheral Interrupt)是通过硬件信号触发的中断类型,主要分为私有外设中断(PPI)和共享外设中断(SPI),每种中断又支持边缘触发或电平敏感 两种触发方式;
- PPI (Private Peripheral Interrupt),私有外设中断,GIC 控制器支持多核,每个核具有自己独有的中断,PPI中断号为16~31,中断信号直接绑定到特定处理器,无需分发器仲裁;
- SPI(Shared Peripheral Interrupt) ,共享外设中断,所有处理器共享的中断,可被分发器路由到多个处理器组合的外设中断,SPI中断号为 32~1019 ,既支持中断信号仅发送给指定处理器(默认模式)又可以使得多个处理器同时接收中断,但各自应答;
由于常用外设(GPIO/UART/...) 通常为系统级资源,而非某个处理器核心的私有资源,所哟常用外设产生的中断属于SPI类型,某个 中断ID 对应 哪个中断 取决于芯片厂商定义;
外部中断触发方式
边缘触发(Edge-triggered)
核心特点:由信号的 “跳变瞬间” 触发,触发后状态持续保持直到主动清除;
- 触发条件:当检测到中断信号的 上升沿/ 下降沿 时,中断状态被激活,通知处理器需要处理;
- 状态保持:一旦触发,无论后续信号是高电平还是低电平,中断的 激活状态 会一直保持,直至满足清除条件;
- 注意事项:边沿触发中断则需要确保触发后及时清除状态(否则可能因状态持续存在导致重复处理);
电平敏感(Level-sensitive)
核心特点:由信号的 “持续电平状态” 决定,中断状态随信号电平实时变化;
- 触发条件:当中断信号处于有效电平(高电平/低电平)时,中断状态被激活;
- 状态保持:只要电平有效,中断就持续存在;电平无效,中断就立刻消失,没有保持的过程;
- 注意事项:电平敏感中断要求外设必须保持有效电平直到处理器处理完毕(否则信号提前消失会导致中断被 GIC 移除导致漏处理);
中断状态
- Inactive:中断未触发状态
- Pending:由于外设硬件或者软件产生中断事件,该中断事件通过硬件信号已通知GIC,等待GIC分配具体某个CPU进行处理;
- Active: CPU已经应答某个中断请求并且正在处理该请求但未处理结束;
- Active and Pending: 当一个中断源处于Active状态时,同一中断源又触发了中断,进入Pending状态;
中断分派模式
1-N 模式
1-N 模式的中断表示中断可以发给所有的 CPU,但只能由一个 CPU 来处理中断;
- 1-N 模式的中断有 N 个目标 CPU,但只能由其中一个来处理;
- 当某一个处理器应答了该中断,便会清除在所有目标处理器上该中断的挂起状态;
- SPI 使用 1-N 模式;
N-N 模式
N-N 模式的中断表示中断可以发给所有 CPU,每个 CPU 可以同时处理该中断;
- 当该中断被某一个处理器应答了,这不会影响该中断在其他 CPU 接口上的状态;
- PPI 和 SGI 使用 N-N 模式;
处理器安全状态
ARM处理器状态分类:
- 安全状态(Secure state):运行敏感代码,可访问安全资源;
- 非安全状态(Non-secure state):运行普通代码,仅能访问非安全资源;
- 当处理器为非安全状态,只能操作 GIC中非安全相关的资源;
- 当处理器为安全状态,既能操作 GIC中安全资源,也能操作GIC中非安全资源;
GICv2 为支持 ARM 安全扩展,将中断划分为两组:
- Group 0:安全中断,用于处理敏感事件(如安全启动、加密模块中断),优先配置为FIQ保证高优先级,仅允许安全状态的处理器访问和处理;
- Group 1:非安全中断,用于处理通用外设事件(如 SPI、UART、GPIO),只能配置为 IRQ,允许非安全状态的处理器访问,也支持安全状态的处理器兼容访问;
GIC逻辑分块
GIC 架构分为两个逻辑块:Distributor 和 CPU Interface,即分发器端和 CPU 接口端;
分发器(GIC Distributor)
分发器 集中管理所有中断源,确定每个中断的优先级,并为每个 CPU 接口转发当前最高优先级的中断,分发器提供以下编程接口功能:
- 全局使能 中断向 CPU 接口的转发 功能;
- 使能或禁用单个中断;
- 设置每个中断的优先级;
- 设置每个中断的目标处理器列表(即中断发给哪些 CPU);
- 将外设中断 配置为电平敏感型 或 边缘触发型;
- 将每个中断配置为 Group 0 或 Group 1(安全 / 非安全分组);
GICD Memory-Map
计算 GICD寄存器的物理地址,以GICD_CTLR寄存器为例,根据 GIC 内存映射表:
分发器(Distributor)的偏移:相对于
PERIPHBASE
,分发器的基地址偏移为0x1000
(表 8-1 中0x1000-0x1FFF
为 Distributor)。GICD_CTLR 的偏移:在分发器内部,GICD_CTLR 的偏移为
0x00
(表 4-1 中 Offset 为0x00
)GICD_CTLR 的物理地址 = PERIPHBASE(CBAR寄存器的值) + 0x1000(分发器偏移) + 0x00(GICD_CTLR偏移)
CPU接口(CPU Interface)
每个 CPU 接口模块为连接到 GIC 的处理器提供交互接口,每个 CPU 接口提供以下编程接口功能:
- 使能 向处理器发送中断请求信号 的功能;
- 应答中断;
- 指示中断处理完成;
- 为处理器设置中断优先级掩码(过滤低优先级中断);
- 为处理器定义抢占策略(高优先级中断能否打断低优先级);
- 确定处理器的最高优先级挂起中断(Pending 状态的中断中,优先级最高的那个)
当 CPU 接口使能时,它会为连接的处理器选取最高优先级的挂起中断,并根据 处理器的中断优先级掩码(低于掩码优先级的中断会被过滤)与 处理器的抢占设置(是否允许高优先级中断抢占当前中断)判断该中断的优先级是否足够高,以决定是否向处理器发送中断请求 ;
当处理器检测到IRQ信号时,处理器通过 读取 CPU 接口的 中断应答寄存器
GICC_IAR
应答中断请求,此动作实现两个核心功能:获取中断 ID:
GICC_IAR 返回 当前最高优先级的 Pending 中断的 ID,处理器通过此 ID,就能知道 具体要处理哪个中断 ,从而调用对应的中断服务程序(ISR);
触发 GIC 状态变化:
读取 GICC_IAR 的动作,会让 GIC 内部的 中断状态 发生转换:
对于 边沿触发中断:中断状态从
Pending
→Active
(表示处理器已开始处理该中断);对于 电平触发中断:中断状态从
Pending
→Active and Pending
(若外设仍保持电平有效,中断会持续挂起;若电平已撤销,则转为Active
);
当处理器的中断处理程序完成中断处理后,需向 CPU 接口写入数据以指示中断完成,中断完成分为两个阶段:
- 优先级下降(priority drop):表示已处理中断的优先级不再阻止向处理器发送新的中断(即允许后续中断抢占,即使优先级更低);
- 中断去激活(interrupt deactivation):表示分发器会移除该中断的活跃状态(标记为已处理);
GICC Memory-Map
计算 GICC寄存器的物理地址,以GICC_CTLR寄存器为例,根据 GIC 内存映射表:
CPU接口(CPU Interface)的偏移:相对于
PERIPHBASE
,CPU接口的基地址偏移为0x2000
(表 8-1 中0x2000-0x3FFF
为CPU接口端)。GICC_CTLR 的偏移:在CPU接口端内部,GICC_CTLR 的偏移为
0x0000
(表 4-2 中 Offset 为0x0000
)GICC_CTLR 的物理地址 = PERIPHBASE(CBAR寄存器的值) + 0x2000(分发器偏移) + 0x0000(GICC_CTLR偏移)
编写IRQ异常处理框架
@startup.s文件(ARM汇编启动文件)
.text
.global _start
_start:@ 1. 安装异常向量表b resetldr pc,=undefine_instructionldr pc,=software_interruptldr pc,=prefetch_abortldr pc,=data_abortldr pc,=not_usedldr pc,=irqldr pc,=fiq@ 2 reset异常
reset:cpsid i @ 关闭IRQ中断@ 2.1 设置异常向量表的基地址,统一采用加载-修改-写回的方式修改寄存器@ 2.1.1 设置异常向量表基地址选择位为0(SCTLR bit[13])mrc p15,0,r0,c1,c0,0 @系统控制寄存器 (SCTLR) 的值读取到 r0 寄存器中bic r0, r0,#(0x1<<13)mcr p15,0,r0,c1,c0,0 @ 将r0寄存器里修改后的值写回到系统控制寄存器 (SCTLR)@ 2.1.2 ldr r0,=0x87800000 @ 设置异常向量表基地址为0x87800000mcr p15,0,r0,c12,c0,0 @ 将0x8780000写入到VBAR寄存器@2.2 设置系统控制寄存器以关闭MMU,I/D cache,分支预测,对齐检查等mrc p15,0,r0,c1,c0,0bic r0,r0,#(0x1<<0) @ 关闭MMUbic r0,r0,#(0x1<<1) @ 关闭内存对齐检查bic r0,r0,#(0x1<<2) @ 关闭数据缓存bic r0,r0,#(0x1<<10) @ 禁用SWP指令bic r0,r0,#(0x1<<11) @ 禁用分支预测bic r0,r0,#(0x1<<12) @ 关闭I Cachemcr p15,0,r0,c1,c0,0 @2.3 初始化IRQ模式的栈空间,栈空间大小为2Mmrs r0,cpsrbic r0,r0,#0x1F orr r0,r0,#0x12 @ CPSR寄存器IRQ模式取值为10010msr cpsr,r0ldr sp,=0x80600000 @ 栈空间类型为满递减栈@2.3 初始化系统模式栈空间,栈空间大小为1M@ 注:用户模式与系统模式共用同一套寄存器,因此栈顶指针寄存器相同mrs r0,cpsrbic r0,r0,#0x1F orr r0,r0,#0x1F @ CPSR寄存器SYS模式取值为11111msr cpsr,r0ldr sp,=0x80400000@2.4 初始化FIQ模式的栈空间,栈空间大小为1Mmrs r0,cpsrbic r0,r0,#0x1F orr r0,r0,#0x11 @ CPSR寄存器FIQ模式取值为10001msr cpsr,r0ldr sp,=0x80300000@2.5 初始化SVC模式的栈空间,栈空间大小为2Mmrs r0,cpsrbic r0,r0,#0x1F orr r0,r0,#0x13 @ CPSR寄存器SVC模式取值为10011msr cpsr,r0ldr sp,=0x80200000cpsie i @ 开启IRQ中断@ 2.6 清空bss段ldr r0,=__bss_startldr r1,=__bss_end@ 将r2寄存器的值(0)写入r0所指向的空间,每写一个0,r0地址自动加1mov r2,#0 @ 将寄存器r2中的值存储到r0所指向的内存空间,然后R0的值递增@ IA:先存数据,后增地址@ IB:先增地址,后存数据
loop:stmia r0!,{r2}cmp r0,r1@ r0小于等于r1,继续循环ble loop@ 2.7. 跳入main函数b mainirq:@ 3.1 考虑指令三级流水线的影响,需要修正程序的返回地址sub lr,lr,#4@ 3.2 保存上下文(寄存器内容)到IRQ模式的栈空间stmfd sp!,{r0-r12,lr}@ 3.3 确保中断嵌套可以多级恢复,保存SPSR_irq寄存器mrs r0,spsrstmfd sp!,{r0}@ 3.4 读取GIC控制器的基地址,该地址保存于CBAR寄存器,CRn=c15,op1=4,CRm=c0,op2=0mrc p15,4,r1,c15,c0,0@ 3.5 获取中断ID,传递到上层方便根据中断ID调用具体的中断处理函数add r1,r1,#0x2000 @CPU端口基地址add r1,r1,#0x000c @IAR寄存器地址ldr r0,[r1]stmfd sp!,{r0,r1} @3.6 IRQ模式默认会禁用新的IRQ中断,而SVC模式允许重新启用IRQ,从而支持中断嵌套cps #0x13 @切换到SVC模式stmfd sp!,{lr} @3.7 执行中断处理函数ldr r2,=System_IRQHandlerblx r2@3.8 中断处理函数执行结束,恢复中断处理前的返回地址ldmfd sp!,{lr}@3.9 返回IRQ模式cps #0x12@3.10 写EOIR寄存器以结束中断ldmfd sp!,{r0,r1}str r0,[r1,#0x10]@3.11 恢复程序状态ldmfd sp!,{r0}msr spsr,r0@3.12 恢复上下文ldmfd sp!,{r0-r12,lr}@3.13 恢复中断未发生时的程序状态并自动恢复CPSR movs pc,lrundefine_instruction:b stop
software_interrupt:b stop
prefetch_abort:b stop
data_abort:b stop
not_used:b stop
fiq:b stop
stop:b stop
.end
欢迎大家批评指正,博主会持续输出优质内容,谢谢各位观众老爷观看,码字画图不易,希望大家给个一键三连支持~ 你的支持是我创作的不竭动力~