异常就是处理器从用户模式切换到对应的异常模式的事件,进入异常模式之后执行对应的异常代码。
异常源:引发处理器进入到异常模式的事件,统称异常源
(一)异常模式
Cortex-A7核有9中工作模式,在不同的工作模式下,执行不同的代码,最终完成不同的功能。
异常模式 | 异常源 | 备注 |
---|---|---|
IRQ异常模式 | IRQ类型的异常源 | 如按键中断,定时器中断,串口中断 |
FIQ异常模式 | FIQ类型的异常源 | 如需要快速响应的中断 |
SVC异常模式 | 复位信号 | 当发生复位信号时,处理器进入SVC模式。如:复位按键,看门狗复位,上电 |
软中断指令 | 当执行软中断指令时,处理器进入SVC模式。 | |
Undef异常模式 | 未定义指令 | 当执行未定义的指令时,处理器进入到Undef未定义异常。如:CPU无法解析的指令的机器码,属于未定义的指令 |
Abort异常模式 | data Abort | 数据中止异常:取数据失败 |
Prefetch Abort | 预取终止异常:取指令失败 |
当发生对应的异常源之后,处理器就会进入到对应的异常模式
(二)Cortex-A7核的异常处理流程分析
1. 保存现场(系统自动完成)
- 保存CPSR寄存器(当前程序状态寄存器)的值到SPSR寄存器(保存程序状态寄存器)中
- 对CPSR寄存器进行修改
① 修改CPSR寄存器的T位,切换到ARM状态
② 修改CPSR寄存器的I位和F位,根据需要屏蔽IRQ和FIQ中断
③ 修改CPSR寄存器的M位,切换到对应的异常模式
- 保存返回地址到LR寄存器中
- 修改PC寄存器的值,执行异常源对应的异常向量表
- 注:由于异常处理函数的入口地址不固定,而保存现场的过程是由CPU完成的,因此需要保证异常处理程序的入口地址固定,因此设计了异常向量表。
- 异常向量表就是代码段中一块连续的内存空间,这块空间的大小为32字节,被平均分为8份,每份占四个字节的内存空间。在异常向量表中存储7种异常源,还有一份说保留的。7种异常源在异常向量表中的位置是固定的,只要让异常向量表中的基地址固定,则异常向量表的入口地址就固定(入口地址=基地址+偏移地址)。
2. 恢复现场(程序员手动完成)
- 恢复SPSR寄存器的值到CPSR寄存器中
- 恢复LR寄存器的值到PC寄存器中
3. 异常处理流程
(三)软中断验证异常处理函数
.globl _start
_start:
/* 1. 构建异常向量表
异常向量表的基地址0x00000000 */
b reset
b undef_handler
b swi_handler
b pref_handler
b data_handler
b .
b irq_handler
b fiq_handler
reset:
/* 2. 程序运行就是复位信号, 进入到SVC模式 */
/* 2.1 初始化SVC模式下的栈空间 */
ldr sp, =0x40000800
/* 2.2 切换处理器的工作模式为user模式 */
mrs r0, cpsr
bic r0, r0, #0x1F
orr r0, r0, #0x10 @10000 User mode
msr cpsr, r0
/* 3. 用户模式,执行的用户代码 */
mov r0, #0x10
mov r1, #0x20
swi 2 @ 软中断指令
/*
当执行软中断指令时,会执行异常的处理过程
保存现场:4大步3小步,CPU自动完成
1> 观察SPSR中是否保存CPSR的值
2> 观察CPSR是否被修改
3> 观察LR中是否保存返回地址
4> 查看PC是否执行对应的异常向量表
*/
add r2, r0, r1 @ r2 = r0 + r1 = 0x30
b stop
undef_handler:
swi_handler:
@ 压栈保存现场
stmfd sp!, {r0-r1, lr}
mov r0, #0x100
mov r1, #0x200
@ 出栈恢复现场
ldmfd sp!, {r0-r1, pc}^
@ ^ : 恢复SPSR寄存器的值到CPSR中
pref_handler:
data_handler:
irq_handler:
fiq_handler:
stop:
b stop
.end