ARM微处理器的编程模型之:异常中断处理
3.4.5 从异常处理程序中返回
当一个异常处理返回时,一共有3件事情需要处理:通用寄存器的恢复、状态寄存器的恢复以及PC指针的恢复。通用寄存器的恢复采用一般的堆栈操作指令即可,下面重点介绍状态寄存器的恢复以及PC指针的恢复。
1.恢复被中断程序的处理器状态
PC和CPSR的恢复可以通过一条指令来实现,下面是3个例子。
· MOVS PC,LR
· SUBS PC,LR,#4
· LDMFD SP!,{PC}^
这几条指令是普通的数据处理指令,特殊之处在于它们把程序计数器寄存器PC作为目标寄存器,并且带了特殊的后缀“S”或“^”。其中“S”或“^”的作用就是使指令在执行时,同时完成从SPSR到CPSR的拷贝,达到恢复状态寄存器的目的。
2.异常的返回地址
异常返回时,另一个非常重要的问题就是返回地址的确定。前面提到过,处理器进入异常时会有一个保存LR的动作,但是该保持值并不一定是正确中断的返回地址。以一个简单的指令执行流水状态图来对此加以说明,如图3.7所示。
图3.7 3级流水线示例
在ARM架构里,PC值指向当前执行指令地址加8。也就是说,当执行指令A(地址0x8000)时,PC等于0x8000+8=0x8008,即等于指令C的地址。假设指令A是BL指令,则当执行时,会把PC值(0x8008)保存到LR寄存器。但是,接下来处理器会对LR进行一次自动调整,使LR=LR-0x4。所以,最终保存在LR里面的是图3.5中所示的B指令地址。所以当从BL返回时,LR里面正好是正确的返回地址。
同样的跳转机制在所有的LR自动保存操作中都存在。当进入中断响应时,处理器对保存的LR也进行一次自动调整,并且跳转动作也是LR=LR-0x04。由此,就可以对不同异常类型的返回地址依次比较。
假设在指令B处(地址0x8004)发生了异常,进入异常相应后,LR经过跳转保存的地址值应该是C的地址0x8008。
(1)软中断异常
如果发生软中断异常,即指令B为SWI指令,从SWI中断返回后下一条执行指令就是C,正好是LR寄存器保存的地址,所以只有直接把LR恢复给PC即可。
(2)IRQ或FIQ异常
如果发生的是IRQ或FIQ异常,因为外部中断请求中断了正在执行的指令B,当中断返回后,需要重新回到B指令执行,也就是说,返回地址应该是B(0x8004),需要把LR减4送PC。
(3)Data Abort数据中止异常
在指令B处进入数据异常的相应,但导致数据异常的原因却应该是上一条指令A。当中断处理程序恢复数据异常后,要回到A重新执行导致数据异常的指令,因此返回地址应该是LR加8。
为方便起见,表3.7总结了各异常和返回地址的关系
表3.7 异常和返回地址
异 常 | 地 址 | 用 途 |
复位 | - | 复位没有定义LR |
数据中止 | LR-8 | 指向导致数据中止异常的指令 |
FIQ | LR-4 | 指向发生异常时正在执行的指令 |
IRQ | LR-4 | 指向发生异常时正在执行的指令 |
预取指令中止 | LR-4 | 指向导致预取指令异常的那条指令 |
SWI | LR | 执行SWI指令的下一条指令 |
未定义指令 | LR | 指向未定义指令的下一条指令 |
3.4.6 在应用程序中安装异常处理程序
1.使用汇编语言安装异常处理程序
如果系统启动不依赖于Debug或Debug monitor软件,可以使用汇编语言在系统启动时直接安装异常处理程序。
下面的例子显示了系统从0x0地址启动,直接安装异常处理程序的方法。
Vector_Init_Block
LDR PC, Reset_Addr
LDR PC, Undefined_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
NOP ;保留向量
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Start_Boot
Undefined_Addr DCD Undefined_Handler
SWI_Addr DCD SWI_Handler
Prefetch_Addr DCD Prefetch_Handler
Abort_Addr DCD Abort_Handler
DCD 0 ;保留向量
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
评论