目录
处理器工作模式
异常源
编辑寄存器组织结构
异常处理流程
CPSR寄存器
异常向量表
编写异常向量表
CP15协处理器
CP15 协处理器寄存器分组
协处理器指令
C0寄存器
C1寄存器
C12寄存器
C15寄存器
CBAR寄存器
Reset异常
前言:
GIC(Generic Interrupt Controller,通用中断控制器)是 ARM 架构中用于管理中断的核心模块,主要应用于现代多核处理器系统中,GIC 控制器的功能如下:
- 中断管理与分发:系统中存在众多中断源(外设中断( GPIO、UART、SPI 等外设产生的中断 )、软件触发中断)GIC 能够收集这些中断请求,根据预先设定的优先级和配置信息,决定将中断分配给哪个 CPU 核心进行处理;
- 优先级处理:为不同的中断分配优先级,高优先级的中断可以打断低优先级中断的处理;
- 中断屏蔽与使能:可以对中断进行屏蔽和使能操作,开发人员可以通过配置 GIC 的相关寄存器,来决定是否允许某个中断被 CPU 接收和处理;
- 支持虚拟化:在虚拟化环境中,GIC 能够将中断正确地路由到虚拟机,使得虚拟机也能像物理机一样有效地处理中断;
GIC具有 GICv1、GICv2、GICv3、GICv4 等版本,由于GICv2广泛应用于 Cortex-A 系列处理器,选择GICv2版本进行讨论,本文并不局限于GIC控制器的讨论,着眼于cortex-A系列芯片的整个异常处理流程,而GIC控制器提供中断处理的硬件支持;
处理器工作模式
处理器模式详解:ARM架构_arm架构 csdn-CSDN博客
异常源

寄存器组织结构
- 系统模式与用户模式共用同一套寄存器
- 异常模式都具有独立的栈顶指针寄存器r13_<mode> 与 链接寄存器(lr) r14_<mode>
- 只有异常模式具有SPSR寄存器且相互独立
- 所有工作模式只具有一个pc指针寄存器
寄存器详细介绍(重点掌握CPSR寄存器):ARM架构_arm架构 csdn-CSDN博客
异常处理流程
若想达到上图所呈现的效果,硬件上需要做那些事,软件上又需要做那些事,以中断异常为例,展开如下讨论
CPSR寄存器
硬件阶段:
- 由于CPSR保存用户程序的执行状态(比如处理器当前所处的工作模式),当异常发生时,硬件会自动将CPSR寄存器中的值保存到对应异常模式中的SPSR_<mode>寄存器,当异常返回时,以便恢复程序之前的执行状态;
- 硬件自动关闭 FIQ(快速中断)与 IRQ(普通中断),由于异常是由处理器内部事件 或 程序执行指令 引发的,而 中断 是由 外部设备 或 处理器外部事件 引起,异常优先级高于中断,所以关闭 FIQ 和 IRQ 中断可以保证异常处理的高优先级得以实现,让系统能够优先处理异常情况,尽快恢复到正常运行状态 ,所以CPSR bit[6]:1 bit[7]:1
- 无论处理器是在ARM状态或者THUMB状态下发生异常, 硬件都会自动切换到ARM状态下进行异常处理,所以CPSR bit[5]:0
- 硬件根据当前发生的异常类型,将异常码写入CPSR里 的M[4:0]模式位以达到模式切换;
- 由于用户程序被异常打断,当前正在执行的指令必定由处理器执行结束,然后切换到异常处理程序里,当异常处理完成,需要返回用户程序继续向下执行,因此硬件自动保存当前执行指令的下一条指令的地址到LR_<mode> 方便程序返回;
- 硬件自动将pc指针寄存器的值设置为异常向量表的起始地址;
当硬件完成上述六步之后,嵌入式软件工程师为cortex-A系列的芯片编写异常向量表,但是什么是异常向量表,异常向量表的起始地址是多少,开发人员如何编写异常向量表
异常向量表
异常向量表(Exception Vector Table)是处理器为不同类型的异常源预设的固定内存地址集合,每个地址对应一种异常的处理程序入口,当异常发生时,处理器会自动跳转到向量表中对应异常的地址,执行该地址指向的异常处理程序,异常向量表的异常类型和异常数量是由处理器架构的设计者规定的(如 ARM 架构由 ARM 公司定义,x86 架构由 Intel/AMD 定义);
经典 ARM架构 规定:处理器复位后,默认从 0x00000000 地址读取异常向量表;
异常向量表的起始地址难道是0x00000000,当烧录MCU程序时,比如 STM32,FLASH起始地址0x08000000,开发人员编写的异常向量表只会存放在flash所对应的地址空间范围内,根本不可能存放到0x0000000,当烧录MPU程序时,比如I.MX 6ULL/STM32MP157/ITOP 4412都会指定程序的运行地址,但是处理器复位后默认从 0x00000000 地址读取异常向量表,必定存在一种机制,用于通知处理器由开发人员编写的异常向量表的实际存放地址,通过配置协处理器CP15中的向量基址寄存器(VBAR,Vector Base Address Register),此寄存器用于存储异常向量表的基地址,当处理器在处理异常时,便会从 VBAR 指向的地址开始查找对应异常的处理程序入口;
编写异常向量表
如下方案仅展示异常向量表的基本框架,实际项目中需添加功能扩展
方案一
@ ARM汇编编写异常向量表 startup.s文件(裸机启动文件)
.text
.global _start
_start:b reset @ 复位异常, 该条指令的偏移地址为0x00b undef_handler @ 未定义异常, 该条指令的偏移地址为0x04b swi_handler @ 软中断异常, 该条指令的偏移地址为0x08b pref_handler @ 预取中止异常, 该条指令的偏移地址为0x0Cb data_handler @ 数据中止异常, 该条指令的偏移地址为0x10b . @ b . 含义: 程序会跳转到当前地址,本质为形成一个无限循环b irq_handler @ 普通中断异常, 该条指令的偏移地址为0x18b fiq_handler @ 快速中断异常, 该条指令的偏移地址为0x1Creset:b stop @所有处理函数暂时都指向stop标签, 仅用于展示基础框架
undef_handler:b stop
swi_handler:b stop
pref_handler:b stop
data_handler:b stop
irq_handler:b stop
fiq_handler:b stopstop:b stop
方案二
@ startup.s文件(裸机启动文件)
.text
.global _start
_start:b resetldr pc, =undef_handlerldr pc, =swi_handlerldr pc, =pref_handlerldr pc, =data_handlerb .ldr pc, =irq_handlerldr pc, =fiq_handlerreset:b stop
undef_handler:b stop
swi_handler:b stop
pref_handler:b stop
data_handler:b stop
irq_handler:b stop
fiq_handler:b stopstop:b stop
方案三
@ startup.s文件(裸机启动文件)
@ 异常向量表 - 定义异常处理程序的入口点
.text @ 指定代码段
.global _start @ 声明全局符号_start作为程序入口
_start:b reset @ 复位异常:跳转到reset标签(系统启动入口)ldr pc, _undef_handler @ 未定义指令异常:从_undef_handler地址加载处理函数地址到PCldr pc, _swi_handler @ 软件中断异常(如系统调用)ldr pc, _pref_handler @ 预取指中止异常(指令读取失败)ldr pc, _data_handler @ 数据访问中止异常(内存访问失败)b . @ 保留异常:跳转到自身(无限循环,防止意外执行)ldr pc, _irq_handler @ IRQ普通中断(外部设备请求)ldr pc, _fiq_handler @ FIQ快速中断(高优先级外部事件)@ 异常处理函数地址表 - 存储实际处理函数的地址
_undef_handler:.word undef_handler @ 存储undef_handler函数的地址
_swi_handler:.word swi_handler @ 存储swi_handler函数的地址
_pref_handler:.word pref_handler @ 存储pref_handler函数的地址
_data_handler:.word data_handler @ 存储data_handler函数的地址
_irq_handler:.word irq_handler @ 存储irq_handler函数的地址
_fiq_handler:.word fiq_handler @ 存储fiq_handler函数的地址@ 异常处理函数实现(当前全部指向stop无限循环)
reset:b stop @ 复位处理:跳转到停机状态(未实现初始化)
undef_handler:b stop @ 未定义指令处理:跳转到停机状态
swi_handler:b stop @ 软件中断处理:跳转到停机状态
pref_handler:b stop @ 预取指中止处理:跳转到停机状态
data_handler:b stop @ 数据访问中止处理:跳转到停机状态
irq_handler:b stop @ IRQ中断处理:跳转到停机状态
fiq_handler:b stop @ FIQ中断处理:跳转到停机状态@ 无限循环(停机状态)
stop:b stop @ 死循环,系统暂停运行
CP15协处理器
协处理器是一种协助主处理器(CPU)完成特定任务的专用处理器,主要用于针对特定任务优化(浮点运算、内存管理),从而提高系统整体效率,协处理器不能独立启动或运行程序,需主处理器通过专用指令(MCR/MRC)调度 协作工作,相当于主处理器的 "专用助手"
CP15(系统控制协处理器):负责内存管理(MMU)、缓存(Cache)控制、特权级配置、异常向量表基址设置等底层系统功能;
CP15 协处理器寄存器分组
ARM 的 CP15 协处理器只有 16 个主寄存器(CRn=C0~C15),但系统控制功能复杂(内存、缓存、安全、调试等),通过 CRn(主分类)+ CRm(子分类)+ opc1/opc2(微调) 的组合,可扩展出数百种寄存器 / 功能;
协处理器指令
MRC指令将 CP15 协处理器中的寄存器数据读取到 ARM 寄存器中;
MCR指令将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中;
MRC{cond} p15, <Opc1>, <Rd>, <CRn>, <CRm>, <Opc2>
MCR{cond} p15, <Opc1>, <Rd>, <CRn>, <CRm>, <Opc2>
cond: 指令执行的条件码,忽略表示无条件执行;
opc1: 协处理器要执行的操作码;
Rd: ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中;
CRn: CP15 协处理器的目标寄存器的主编号;
CRm: CRm 是 "附加寄存器",用于给
CRn
主寄存器提供额外的操作对象信息,当某个操作只需要CRn便可以确定(不需要额外的附加寄存器信息)时,必须把 CRm 设置为C0;opc2: opc2 是 "细分操作码",当某个操作不需要这种细分(
CRn
+CRm
已经能唯一确定操作)时,必须把 opc2 设为0
(这是 “无细分操作” 的约定标记);
ARM 官方明确规定 CP15 的
opc1
必须为0000
,否则视为未定义指令
MRC p15, 0, r0, c0, c0, 0 @ 读CP15的ID寄存器到r0 LDR r1, =0x80000000 @ 指定页表起始地址(虚拟地址转物理地址的映射表)
MCR p15, 0, r1, c2, c0, 0 @ 写TTBR0,配置页表基地址
C0寄存器
当CRn=c0,opc1=0,CRm=c0,opc2=0 时表示 此时 c0寄存器为 MIDR 寄存器,MDIR 寄存器含义如下图所示
bit[31:24]:厂商编号,0X41表示ARM公司;
bit[23:20]:内核架构的主版本号,ARM 内核版本一般使用 rnpn 来表示,比如 r0p1,其中 r0 后面的 0 就是内核架构主版本号;
bit[19:16]:架构代码,0XF代表ARMv7 架构;bit[15:4]:内核版本号,0XC07代表Cortex-A7内核;
bit[3:0]:内核架构的次版本号,rnpn 中的 pn,比如 r0p1 中 p1 后面的 1 就是次版本号;
C1寄存器
当CRn=c1,opc1=0,CRm=c0,opc2=0 时表示 此时 c1寄存器为 系统控制寄存器(SCTLR寄存器),SCTLR寄存器含义如下图所示
[bit13]:异常向量表基地址选择位,为 0 的话异常向量表基地址为 0X00000000,软件可 以使用 VBAR寄存器来设置异常向量表的基地址,为 1 的话中断向量表基地址为 0XFFFF0000,此基地址不可被设置。
bit[12]:I Cache(指令缓存) 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache;
bit[11]:分支预测使能位,如果开启 MMU 的话,此位也会使能;
bit[10]:SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候 就使能 SWP 和 SWPB 指令;
bit[2]:D Cache(数据缓存) 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时 使能;
bit[1]:内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐 检查;
bit[0]:MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU;
I Cache(指令缓存)
- 定义:CPU 内专门存储即将执行的指令的高速缓存;
- 作用:减少 CPU 从内存读取指令的延迟(内存速度远低于 CPU),提升程序运行效率;
- 原理:CPU 优先查询 I Cache,命中则直接取指令;未命中时从内存加载,并将指令块存入 I Cache 供后续复用;
D Cache(数据缓存)
- 定义:CPU 内专门存储程序操作的数据(如变量、数组)的高速缓存;
- 作用:加速数据读写,减少内存访问次数;
- 原理:读写数据时优先查询 D Cache,命中则直接操作;未命中时从内存加载,并缓存数据块;
分支预测
- 定义:CPU 提前预测分支指令(如
if-else
)的执行走向(true/false),提前加载预测路径的指令到流水线;- 作用:避免分支执行时的流水线停滞(因分支结果未确定时,后续指令无法执行),提升 CPU 吞吐量;
内存对齐检查
- 定义:CPU 检查数据访问地址是否符合 "对齐规则"(如
int
需存放在 4 的倍数地址,short
需存放在 2 的倍数地址);- 影响:若数据未对齐,ARM 芯片可能触发 总线错误(Bus Error),或导致性能严重下降;
当为cortex-A系列的芯片配置裸机开发的开发环境,这些比特位该如何设置
bit[13]: 0 ,设置为
0
时,可通过 VBAR 寄存器 自由指定向量表基地址;bit[12]: 0 ,裸机初期,内存系统未完全初始化(如内存控制器未配置),开启 I Cache 会导致指令缓存与实际内存不一致,关闭I cache;
bit[11]: 0,裸机初期代码逻辑简单,分支少,分支预测的收益极低;
bit[10]: 0,SWP/SWPB 是原子指令(用于共享内存的互斥访问),裸机开发中通常无多任务调度;
bit[2]: 0,若开启 D Cache,写入的数据可能暂存于 Cache 而非立即更新硬件,导致配置失效;
bit[1]: 0,裸机开发中,若自定义数据结构未严格对齐,开启对齐检查会触发总线错误;
bit[0]: 0,裸机开发直接操作物理地址,无需虚拟地址映射;
C12寄存器
当CRn=c12,opc1=0,CRm=c0,opc2=0 时表示 此时 c12寄存器为异常向量表基址寄存器(VBAR寄存器),VBAR寄存器含义如下图所示
假定cortex-A系列的芯片的程序 链接起始地址=程序运行地址 为 0X87800000,一般推荐将异常向量表存放于链接起始地址,所以需要设置 VBAR 为 0X87800000,设置命令如下:
ldr r0, =0X87800000 @ r0=0X87800000
MCR p15, 0, r0, c12, c0, 0 @ 将r0里面的数据写入到 c12 中,即 c12=0X87800000
C15寄存器
ARM 架构本身不限制 c15 寄存器的用途,芯片厂商(如高通、三星)可以根据需求自定义c15 中寄存器的功能,但 ARMv7 要求芯片厂商必须在其 "实现文档" 中完整描述 c15 中每个寄存器的用途、位定义、操作方式;
以ARM公司为例,假设芯片内核为Cortex-A7,架构为ARMv7,查看Cortex-A7 Technical Reference Manual 手册关于c15寄存器的定义;
CBAR寄存器
CBAR 是 系统级寄存器,用于告知处理器 GIC(通用中断控制器)的物理基地址
CBAR寄存器特性:
- CBAR 为 只读寄存器,GIC基地址由 硬件设计 或 启动固件(如 BootROM) 初始化,保证地址的稳定性,软件无法修改;
- 无论处理器处于 安全状态(TrustZone Secure 模式,处理敏感数据) 还是 普通状态(Non-secure 模式,运行普通应用),都能访问 CBAR;
- 特权模式(svc模式)可访问CBAR寄存器,用户模式无法访问;
- CBAR 在 所有 ARMv7 配置中均存在,无需担心硬件选型导致的功能缺失,保证了架构兼容性;
位域结构:
PERIPHBASE[31:15]
:存储 GIC 基地址的 高 17 位Reserved
:保留位,读为 0PERIPHBASE[39:32]:
存储 GIC 基地址的 低 8 位(对应 64 位地址的 bit39~bit32)
由于ARMv7架构诞生于 32 位向 64 位过渡时期,其架构设计允许 物理地址扩展到 40 位甚至更多,以支持大容量内存,若 CBAR 仅用 32 位,最多只能寻址 4GB 内存,无法满足现代系统需求,所以CBAR 采用 64 位地址,但 实际寄存器仅用 32 位存储,所以GIC基地址计算方案如下:
Reset异常
当系统上电时,便会产生reset异常,此时系统进入SVC模式,因此最先处理的任务配置异常向量表的基地址以确保系统稳定性;当处理器从一种工作模式切换到另一种工作模式,必然涉及到上下文(处理器寄存器中的内容)的保护,所以必须初始化处理器各个工作模式的栈空间以保存上下文;需要配置
SCTLR
(系统控制寄存器)以设置内存访问特性;若系统运行C程序,必须搭建C语言运行环境,由于C语言标准规定,未显式初始化的全局变量和静态变量必须为 0 值,而未初始化的全局变量存放于 bss 段,所以必须将bss段清0,最后跳入main函数;
reset异常处理任务:
- 设置异常向量表的基地址;
- 配置SCTLR系统控制寄存器;
- 初始化处理器各个工作模式的栈顶指针寄存器;
- 清空bss段,跳入主函数;
.text
.global _start
_start:@ 异常向量表b resetldr pc, =undefined_instructionldr pc, =software_interruptldr pc, =prefetch_abortldr pc, =data_abortldr pc, =not_usedldr pc, =irqldr pc, =fiqreset:@ 重新映射异常向量表的入口地址/* Set Vector Base Address Register */mrc p15, 0, r0, c1, c0, 0bic r0, #(1<<13)mcr p15, 0, r0, c1, c0, 0ldr r0,=0x87800000mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register@ 配置系统控制寄存器mrc p15, 0, r0, c1, c0, 0 @ 读取 CP15 的 C1 寄存器到 R0 中 bic r0, r0, #(0x1 << 12) @ 清除 C1 的 I 位,关闭 I Cache bic r0, r0, #(0x1 << 2) @ 清除 C1 的 C 位,关闭 D Cache bic r0, r0, #0x2 @ 清除 C1 的 A 位,关闭对齐检查 bic r0, r0, #(0x1 << 11) @ 清除 C1 的 Z 位,关闭分支预测 bic r0, r0, #0x1 @ 清除 C1 的 M 位,关闭 MMU mcr p15, 0, r0, c1, c0, 0 @ 将 r0 的值写入到 CP15 的 C1 中 @配置IRQ模式栈顶指针寄存器mrs r0, cpsrbic r0, r0, #0x1f @ 将 r0 的低 5 位清零,也就是 cpsr 的 M0~M4 orr r0, r0, #0x12 @ r0 或上 0x12,表示使用 IRQ 模式 msr cpsr, r0 @ 将 r0 的数据写入到 cpsr 中 ldr sp, =0x80600000 @ IRQ 模式栈首地址为 0X80600000,大小为2MB@配置SYS模式栈顶指针寄存器mrs r0, cpsrbic r0, r0, #0x1f @ 将 r0 的低 5 位清零,也就是 cpsr 的 M0~M4orr r0, r0, #0x1f @ r0 或上 0x1f,表示使用 SYS 模式 msr cpsr, r0 @ 将 r0 的数据写入到 cpsr 中 ldr sp, =0x80400000 @ SYS 模式栈首地址为 0X80400000,大小为2MB @配置SVC模式栈顶指针寄存器mrs r0, cpsrbic r0, r0, #0x1f @ 将r0的低 5 位清零,也就是 cpsr 的 M0~M4 orr r0, r0, #0x13 @ r0 或上 0x13,表示使用 SVC 模式msr cpsr, r0 @ 将 r0 的数据写入到 cpsr 中ldr sp, =0X80200000 @ SVC 模式栈首地址为 0X80200000,大小为2MB @ 3. 初始化数据段@ 3.1 清空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@ 4. 跳入main函数b mainundefined_instruction:b stop
software_interrupt:b stop
prefetch_abort:b stop
data_abort:b stop
not_used:b stop
irq:b stop
fiq:b stopstop:b stop
.end