唉,昨晚,不对,今天凌晨突然发癫睡不着觉,只好抱起本书来消耗精神。
首先看看异常,所谓异常,就是需要中止指令正常执行的任何情形。大多数异常都对应一个软件的异常处理程序。对于ARM的异常处理,有四个关键部分:
◎ ARM处理器模式及异常
◎ 向量表
◎ 异常的优先级
◎ 连接寄存器的偏移
ARM处理器模式及异常
每种异常都导致内核进入一种特定的模式,但用户模式和系统模式只能通过修改cpsr来进入。当异常导致模式的改变时,内核自动地:
× 把cpsr保存到相应异常模式下的spsr 到底是切换前的spsr还是切换后的?
× 把pc波阿存到相应地异常模式下的lr
× 设置cpsr到相应的模式,并切换到ARM模式
× 跳转到相应的处理程序入口
向量表
向量表是异常发生时,ARM内核跳转地址组成的表,通常用跳转指令跳转到对应的处理程序。
异常 模式 向量表偏移
复 位 SVC +0x00
未定义指针 UND +0x04
软件中断(SWI) SVC +0x08
预取指中止 ABT +0x0c
数据中止 ABT +0x10
未分配 - +0x14
IRQ IRQ +0x18
FIQ FIQ +0x1c
当异常发生时,内核会跳转到向量表的相应位置,并执行当中的指令,通常,在向量表中会存放一些B,LDR PC等跳转指令,用来跳转到对应的处理程序,下面给出一个一地址0x00000000开始的向量表例子:
向量表地址 模式 指令
0x00000000: RESET: LDR pc,[pc,#rest]
0x00000004: UNDEF: b undInstr
0x00000008: SWI: ldr pc,[pc,#swi];
0x0000000c: PABT: ldr pc,[pc,#prefetch]
0x00000010: DABT: ldr pc,[pc,#data]
0x00000014: -------: ldr pc,[pc,#notassigned]
0x00000018: IRQ: ldr pc,[pc,#irq]
0x0000001c: FIQ: ldr pc,[pc,#fiq]
异常优先级
ARM的异常优先级如下表所示:
异常 优先级 I位 F位
复位 1 1 1
数据中止 2 1 -
快速中断请求 3 1 1
中断请求 4 1 -
预取指中止 5 1 -
软件中断 6 1 -
未定义指令 6 1 -
从上表还可以看出,cpsr的I位和F位可以屏蔽或使能某些异常。复位程序具有最高的优先级,是系统启动(或复位)时调用的程序。1)它应该对异常处理程序的系统进行初始化(包括配置储存器和cache)。2)保证在IRQ和FIQ中断允许之前初始化外部的中断源,避免在还没有设置好相应的处理程序前产生中断。3)设置好各种处理器模式的堆栈指针。
数据中止异常指示访问了无效的存储器地址,或者当前代码没有正确的数据访问权限。它不屏蔽FIQ,所以在处理过程中可以处理IRQ。
FIQ中断的优先级比IRQ中断的优先级要高,且内核进入FIQ处理程序时,把FIQ和IRQ都禁止了,因此,任何外部中断源都不能被处理,如果要实现嵌套或者优先级时就要对上下文进行适当处理然后用软件使能中断。同样,要实现IRQ的嵌套,也要相应的上下文操作和软件开中断,但IRQ不能屏蔽FIQ请求。当IRQ中断请求出现时,只有当FIQ异常和数据中止异常都没有发生时,IRQ处理程序才被调用,并屏蔽IRQ中断,直到中断源被清除。
预取指中断会屏蔽IRQ异常,但不影响FIQ。
当上述的所有异常都没有发生,此时系统可以处理SWI异常,cpsr会被置成管理模式。如果要实现SWI的嵌套,也要保存r14(lr)和spsr。SWI和未定义指令异常共享同一优先级,所以两者不能同时出现。
链接寄存器偏移
由于ARM的流水线特性,pc在执行一条指令时,已经指向当前指令后的第二条指令,所以当IRQ异常发生时,链接寄存器lr的值其实是最后执行的指令的地址+8。返回时应跳到异常处理前最后一条指令的下一条指令,所以应返回lr-4。 (看不懂就看俺的下一篇文章《ARM7中的中断返回地址和3级流水线问题》)
下表给出了各种异常应使用的lr偏移。
异常 地址 用法
复位 - 没有定义
数据中止 lr - 4 指向导致数据中止异常的那条指令
FIQ lr - 4 FIQ处理程序的返回地址
IRQ lr - 4 IRQ处理程序的返回地址
预取指中止 lr - 4 指向导致预取指中止异常的那条指令
SWI lr 指向SWI指令的下一条指令
未定义指令 lr 指向未定义指令的下一条指令
另外,如果指令以pc为目标,且有后缀s,那么cpsr将自动从spsr中恢复,可以节省指令。用LDM的后缀^也有同样的效果。
看完了异常处理的基本问题,下面就是一些优化策略了问题了,先休息一会。