裸机第八弹——异常(Exception)
参与Helper2416开发板助学计划心得
上一篇帖子介绍过ARM有5钟异常模式。我们可以通过修改CPSR的工作模式位来进入不同的模式,但这并没有什么实际意义,我们需要的是当有异常发生的时候,能进入相应的异常模式,并跳转到相应的异常服务函数中去对异常做出处理,同时在处理完后又能返回到处理异常之前的状态下去。这篇帖子主要介绍异常从发生到处理,到返回的具体过程。
Exception:
Exceptions are generated by internal and external sources to cause the processor to handle an event, such as an externally generated interrupt or an attempt to execute an Undefined instruction.The processor state just before handling the exception is normally preserved so that the originalprogram can be resumed when the exception routine has completed. More than one exception can arise at the same time.
异常的检测:
CPU在执行每条指令之前检测是否有异常发生。
mov r1,#0x10
mov r2, #0x20
sub r0, r2, r1
例如在CPU在执行mov r1,#0x10时,产生一个IRQ异常信号,则CPU将在这条指令执行完后,由于ARM的流水线模式PC指向sub r0, r2, r1这条指令但mov r2, #0x20并未执时检测到异常信号。
进入异常模式:
在检测到异常之后,硬件会自动切换到相应的异常模式同时切换到该异常模式下对应的那一组寄存器下,并把之前模式CPSR保存到当前异常模式下的SPSR下,同时把PC值压入该异常模式下的栈中。然后再将一个固定的地址装入PC,对于不同的异常,这个值也是不同的,对应的值如下:
通常我们所使用的是Normal address 若要使用High vector address还需要做另外的配置
我们通常在这些位置放上相应的跳转指令,构成了我们的异常向量表,例如如下这段代码构成的异常向量表
- _start:
- // 异常向量表
- b reset /* 复位时,cpu跳到0地址 */
- b halt /* cpu遇到不能识别的指令时 */
- b halt /* swi异常,进入svc模式 */
- b halt /* 预取中止异常 */
- b halt /* 数据访问异常 */
- b halt /* 没用到 */
- b IRQHandler /* 中断异常 */
- b _fiq /* 快中断异常 */
复制代码需要注意的是,这些条状指令一定要写在相应的特定地址上。
由于几个不同的异常可以同时产生,那就必须要引出
异常优先级的概念,来让各异常能够有序的执行。
各异常对应的优先级如下:
异常处理:
通过上面的异常向量表跳转到特定的异常服务函数来进行相应的处理。
通常在异常服务函数中还要对上下文的切换做一些工作,把一些之前模式用到的寄存器都要保存好,同时在退出异常服务的时候要恢复这些寄存器,
例如如下这段简单的IRQ异常服务函数:
需要注意的是由于ARM流水线操作,我们需要修正返回地址:如上我们必须要返回到mov r2, #0x20的位置。
- IRQHandler:
- SUB LR, LR, #4 @修正返回地址
- STMFD SP!, {R0-R12, LR}
- MRS R0, SPSR
- STMFD SP!, {R0}
- BL do_irq @处理异常
- LDMFD SP!, {R0} @恢复现场
- MSR SPSR, R0
- LDMFD SP!, {R0-R12, PC}^ @^表示把spsr恢复到cpsr
复制代码 异常返回:
如上代码,恢复上下文,把SPSR恢复到cpsr就完成了模式的切换,同时将返回地址装入PC即从异常返回。
由于我使用的是SD卡启动,代码会搬运到0x40000000的地址执行,所以如果要进行自定义异常处理的话,就必须让自己的异常向量表位于0x0开始的地址上,所以我们只能使用MMU把地址0x40000000重映射到地址0x00000000上去。
我将在下一篇帖子贴出我有关MMU及重映射的心得。
论坛ID:yuanlai2010
发表时间:2014-07-22