ARM汇编异常处理代码的流程图解析,按模块分层展示
┌───────────────────────┐
│ 向量表入口 │
│ (0x00000000开始) │
└──────────┬────────────┘
│
▼
┌───────────────────────┐
│ 异常类型判断 │
│ (通过向量表偏移跳转) │
└──────────┬────────────┘
│
├───────────────────┬─────────────┬────────────────┬────────────────┐
▼ ▼ ▼ ▼ ▼
┌───────────────────┐ ┌───────────┐ ┌───────────┐ ┌──────────────┐ ┌───────────┐
Reset Undefined SVC调用 PrefetchAbort DataAbort
(跳转_boot) 异常处理 处理 处理 处理
└───────────────────┘ └─────┬─────┘ └─────┬─────┘ └─────┬────────┘ └─────┬─────┘
│ │ │ │ │
├──────────────────┴──────────────┴───────────────┴────────────────┘
▼
┌───────────────────────┐
│ IRQ/FIQ中断处理 │
└──────────┬────────────┘
│
▼
┌───────────────────────┐
│ 公共处理流程 │
├───────────────────────┤
│ 1. 保存现场 │
│ (r0-r3,r12,lr入栈) │
├───────────────────────┤
│ 2. FPU状态保存 │
│ (如果启用FPU) │
├───────────────────────┤
│ 3. 调用C处理函数 │
│ (如IRQInterrupt) │
├───────────────────────┤
│ 4. FPU状态恢复 │
│ (如果启用FPU) │
├───────────────────────┤
│ 5. 恢复现场 │
│ (寄存器出栈) │
├───────────────────────┤
│ 6. 异常返回地址调整 │
│ (PC ← LR - 偏移量) │
└───────────────────────┘
1、向量表定义
.org 0 ; 设置当前地址为0x0
.text ; 进入代码段
.globl _vector_table ; 声明全局符号
.section .vectors ; 定义向量表段
_vector_table: ; 向量表起始标签
作用:定义位于地址0的异常向量表,使用.vectors段存放
2、异常向量入口
B _boot ; 0x00: Reset向量,跳转到启动代码
B Undefined ; 0x04: 未定义指令异常
B SVCHandler ; 0x08: SVC调用
B PrefetchAbortHandler ; 0x0C: 预取中止
B DataAbortHandler ; 0x10: 数据中止
NOP ; 0x14: 保留向量(历史地址异常)
B IRQHandler ; 0x18: IRQ中断
B FIQHandler ; 0x1C: FIQ中断
关键点:每个向量占用4字节,使用B指令跳转(范围限制±32MB)
3、IRQ处理程序
IRQHandler:
stmdb sp!,{r0-r3,r12,lr} ; 压栈保存关键寄存器
寄存器保存策略:保存被破坏的调用者保存寄存器(AAPCS标准)
4、FPU状态保存
#if FPU_HARD_FLOAT_ABI_ENABLED
vpush {d0-d7} ; 保存低128位FPU寄存器
vpush {d16-d31} ; 保存高128位FPU寄存器
vmrs r1, FPSCR ; 读取FPU状态控制寄存器
push {r1} ; 保存FPSCR
vmrs r1, FPEXC ; 读取FPU异常寄存器
push {r1} ; 保存FPEXC
#endif
技术要点:VFPv4架构需要保存D0-D31共32个双字寄存器
5、性能分析支持
#ifdef PROFILING
ldr r2, =prof_pc ; 加载prof_pc的地址
subs r3, lr, #0 ; 复制LR到R3(subs用于设置标志)
str r3, [r2] ; 存储异常发生时的PC值
#endif
作用:记录异常发生时的程序计数器,用于性能分析
6、中断处理调用
bl IRQInterrupt ; 调用C语言中断处理函数
调用约定:使用标准ARM调用约定,参数通过寄存器传递
7、FPU状态恢复
#if FPU_HARD_FLOAT_ABI_ENABLED
pop {r1}
vmsr FPEXC, r1 ; 恢复FPEXC
pop {r1}
vmsr FPSCR, r1 ; 恢复FPSCR
vpop {d16-d31} ; 恢复高128位
vpop {d0-d7} ; 恢复低128位
#endif
恢复顺序:严格按照保存的逆序进行
8、现场恢复与返回
ldmia sp!,{r0-r3,r12,lr} ; 弹出通用寄存器
subs pc, lr, #4 ; 修正返回地址(IRQ特定)
关键细节:subs pc, lr, #4 将导致:
恢复CPSR
PC ← LR - 4
自动切换回之前的处理器模式
9、SVC指令解析
tst r0, #0x20 ; 检查CPSR的T位(bit5)
ldrneb r0, [lr,#-2] ; Thumb模式:LR-2获取SWI指令
ldreq r0, [lr,#-4] ; ARM模式:LR-4获取SWI指令
biceq r0, r0, #0xff000000 ; 清除ARM模式SWI编号的高字节
SWI编号提取:
ARM模式:SWI指令的高24位是编号
Thumb模式:SWI指令的低8位是编号
10、数据中止特殊处理
#ifdef CONFIG_ARM_ERRATA_775420
dsb ; 数据同步屏障
#endif
subs pc, lr, #8 ; 返回触发异常的指令
勘误说明:Cortex-A9 Errata 775420要求在异常入口添加屏障
11、未定义指令处理
ldr r0, =UndefinedExceptionAddr
sub r1, lr, #4 ; LR-4获取异常指令地址
str r1, [r0] ; 存储到全局变量
bl UndefinedException ; 调用C处理函数
movs pc, lr ; 直接返回(不重新执行异常指令)
处理策略:记录异常地址后交给C代码处理,不尝试恢复
12、FIQ快速处理循环
FIQLoop:
bl FIQInterrupt ; 可能连续处理多个FIQ
设计特点:允许在FIQ处理中继续响应新的FIQ中断
各异常返回地址修正总结表:
异常类型 LR值 PC修正 指令示例
Reset - 跳转到_boot B _boot
IRQ PC+4 PC = LR - 4 subs pc, lr, #4
FIQ PC+4 PC = LR - 4 subs pc, lr, #4
Data Abort PC+8 PC = LR - 8 subs pc, lr, #8
Prefetch Abort PC+4 PC = LR - 4 subs pc, lr, #4
Undefined PC+4 PC = LR movs pc, lr
SWI PC+4 PC = LR movs pc, lr
关键设计决策:
最小化中断延迟:通过分层保存(先通用寄存器后FPU)
勘误兼容性:通过条件编译处理芯片特定问题
调试支持:提供PC记录机制
模式透明:自动处理ARM/Thumb模式差异
可扩展性:通过C函数实现具体处理逻辑