++++++++++++++++++++++++++++++++++++++++++
本文系本站原创,欢迎转载! 转载请注明出处:
http://blog.csdn.net/mr_raptor/article/details/6556195
++++++++++++++++++++++++++++++++++++++++++
1.1
ARM
处理器异常处理
所谓异常就是正常的用户程序被暂时中止,处理器就进入异常模式,例如响应一个来自外设的中断,或者当前程序非法访问内存地址都会进入相应异常模式。
1.1.1
异常分类
(
1
)复位异常
当
CPU
刚上电时或按下
reset
重启键之后进入该异常,该异常在管理模式下处理。
(
2
)一般
/
快速中断请求
CPU
和外部设备是分别独立的硬件执行单元,
CPU
对全部设备进行管理和资源调度处理,
CPU
要想知道外部设备的运行状态,要么
CPU
定时的去查看外部设备特定寄存器,要么让外部设备在出现需要
CPU
干涉处理时“打断”
CPU
,让它来处理外部设备的请求,毫无疑问第二种方式更合理,可以让
CPU
“专心”去工作,这里的“打断”操作就叫做中断请求,根据请求的紧急情况,中断请求分一般中断和快速中断,快速中断具有最高中断优先级和最小的中断延迟,通常用于处理高速数据传输及通道的中数据恢复处理,如
DMA
等,绝大部分外设使用一般中断请求。
(
3
)预取指令中止异常
该异常发生在
CPU
流水线取指阶段,如果目标指令地址是非法地址进入该异常,该异常在中止异常模式下处理。
(
4
)未定义指令异常
该异常发生在流水线技术里的译码阶段,如果当前指令不能被识别为有效指令,产生未定义指令异常,该异常在未定义异常模式下处理。
(
5
)软件中断指令(
swi
)异常
该异常是应用程序自己调用时产生的,用于用户程序申请访问硬件资源时,例如:
printf()
打印函数,要将用户数据打印到显示器上,用户程序要想实现打印必须申请使用显示器,而用户程序又没有外设硬件的使用权,只能通过使用软件中断指令切换到内核态,通过操作系统内核代码来访问外设硬件,内核态是工作在特权模式下,操作系统在特权模式下完成将用户数据打印到显示器上。这样做的目的无非是为了保护操作系统的安全和硬件资源的合理使用,该异常在管理模式下处理。
(
6
)数据中止访问异常
该异常发生在要访问数据地址不存在或者为非法地址时,该异常在中止异常模式下处理。
1.1.2
异常发生的硬件操作
在异常发生后,
ARM
内核会自动做以下工作:
l
保存执行状态:将
CPSR
复制到发生的异常模式下
SPSR
中;
l
模式切换:将
CPSR
模式位强制设置为与异常类型相对应的值,同时处理器进入到
ARM
执行模式,禁止所有
IRQ
中断,当进入
FIQ
快速中断模式时禁止
FIQ
中断;
l
保存返回地址:将下一条指令的地址(被打断程序)保存在
LR(
异常模式下
LR_excep)
中。
l
跳入异常向量表:强制设置
PC
的值为相应异常向量地址,跳转到异常处理程序中。
(
1
)保存执行状态
当前程序的执行状态是保存在
CPSR
里面的,异常发生时,要保存当前的
CPSR
里的执行状态到异常模式里的
SPSR
里,将来异常返回时,恢复回
CPSR
,恢复执行状态。
(
2
)模式切换
硬件自动根据当前的异常类型,将异常码写入
CPSR
里的
M[4:0]
模式位,这样
CPU
就进入了对应异常模式下。不管是在
ARM
状态下还是在
THUMB
状态下发生异常,都会自动切换到
ARM
状态下进行异常的处理,这是由硬件自动完成的,将
CPSR[5]
设置为
0
。同时,
CPU
会关闭中断
IRQ
(设置
CPSR
寄存器
I
位),防止中断进入,如果当前是快速中断
FIQ
异常,关闭快速中断(设置
CPSR
寄存器
F
位)。
(
3
)保存返回地址
当前程序被异常打断,切换到异常处理程序里,异常处理完之后,返回当前被打断模式继续执行,因此必须要保存当前执行指令的下一条指令的地址到
LR_excep(
异常模式下
LR
,并不存在
LR_excep
寄存器,为方便读者理解加上
_excep
,以下道理相同
)
,由于异常模式不同以及
ARM
内核采用流水线技术,异常处理程序里要根据异常模式计算返回地址。
(
4
)跳入异常向量表
该操作是
CPU
硬件自动完成的,当异常发生时,
CPU
强制将
PC
的值修改为一个固定内存地址,这个固定地址叫做异常向量(详见
3.2.4
章节)。
1.1.3
异常返回地址
由
1.1.3
节可知,一条指令的执行分为:取指,译码,执行三个主要阶段,
CPU
由于使用流水线技术,造成当前执行指令的地址应该是
PC – 8
(
32
位机一条指令四个字节),那么执行指令的下条指令应该是
PC – 4
。在异常发生时,
CPU
自动会将将
PC – 4
的值保存到
LR
里,但是该值是否正确还要看异常类型才能决定。
各模式的返回地址说明如下:
(
a
)一般
/
快速中断请求:
快速中断请求和一般中断请求返回处理是一样的。通常处理器执行完当前指令后,查询
FIQ/IRQ
中断引脚,并查看是否允许
FIQ/IRQ
中断,如果某个中断引脚有效,并且系统允许该中断产生,处理器将产生
FIQ/IRQ
异常中断,当
FIQ/IRQ
异常中断产生时,程序计数器
pc
的值已经更新,它指向当前指令后面第
3
条指令(对于
ARM
指令,它指向当前指令地址加
12
字节的位置;对于
Thumb
指令,它指向当前指令地址加
6
字节的位置),当
FIQ/IRQ
异常中断产生时,处理器将值(
pc-4
)保存到
FIQ/IRQ
异常模式下的寄存器
lr_irq/lr_irq
中,它指向当前指令之后的第
2
条指令,因此正确返回地址可以通过下面指令算出:
SUBS
PC
,
LR_irq
,
#4
;
一般中断
SUBS
PC
,
LR_fiq
,
#4
;
快速中断
注:
LR_irq/LR_fiq
分别为一般中断和快速中断异常模式下
LR
,并不存在
LR_xxx
寄存器,为方便读者理解加上
_xxx
(
b
)预取指中止异常:
在指令预取时,如果目标地址是非法的,该指令被标记成有问题的指令,这时,流水线上该指令之前的指令继续执行,当执行到该被标记成有问题的指令时,处理器产生指令预取中止异常中断。发生指令预取异常中断时,程序要返回到该有问题的指令处,重新读取并执行该指令,因此指令预取中止异常中断应该返回到产生该指令预取中止异常中断的指令处,而不是当前指令的下一条指令。
指令预取中止异常中断由当前执行的指令在
ALU
里执行时产生,当指令预取中止异常中断发生时,程序计数器
pc
的值还未更新,它指向当前指令后面第
2
条指令(对于
ARM
指令,它指向当前指令地址加
8
字节的位置;对于
Thumb
指令,它指向当前指令地址加
4
字节的位置)。此时处理器将值(
pc-4
)保存到
lr_abt
中,它指向当前指令的下一条指令,所以返回操作可以通过下面指令实现:
SUBS
PC
,
LR_abt
,
#4
注:
LR_abt
为中止
模式下
LR
,并不存在
LR_abt
寄存器,为方便读者理解加上
_abt
(
c
)未定义指令异常
:
未定义指令异常中断由当前执行的指令在
ALU
里执行时产生,当未定义指令异常中断产生时,程序计数器
pc
的值还未更新,它指向当前指令后面第
2
条指令(对于
ARM
指令,它指向当前指令地址加
8
字节的位置;对于
Thumb
指令,它指向当前指令地址加
4
字节的位置),当未定义指令异常中断发生时,处理器将值(
pc-4
)保存到
lr_und
中,此时(
pc-4
)指向当前指令的下一条指令,所以从未定义指令异常中断返回可以通过如下指令来实现:
MOV
PC,
LR_und
注:
LR_und
为未定义
模式下
LR
,并不存在
LR_und
寄存器,为方便读者理解加上
_und
(
d
)
软中断指令
(
SWI
)
异常
:
SWI
异常中断和未定义异常中断指令一样,也是由当前执行的指令在
ALU
里执行时产生,当
SWI
指令执行时,
pc
的值还未更新,它指向当前指令后面第
2
条指令(对于
ARM
指令,它指向当前指令地址加
8
字节的位置;对于
Thumb
指令,它指向当前指令地址加
4
字节的位置),当未定义指令异常中断发生时,处理器将值(
pc-4
)保存到
lr_svc
中,此时(
pc-4
)指向当前指令的下一条指令,所以从
SWI
异常中断处理返回的实现方法与从未定义指令异常中断处理返回一样:
MOV
PC,
LR_svc
注:
LR_svc
为管理
模式下
LR
,并不存在
LR_svc
寄存器,为方便读者理解加上
_svc
(
e
)数据中止异常:
发生数据访问异常中断时,程序要返回到该有问题的指令处,重新访问该数据,因此数据访问异常中断应该返回到产生该数据访问中止异常中断的指令处,而不是当前指令的下一条指令。
数据访问异常中断由当前执行的指令在
ALU
里执行时产生,当数据访问异常中断发生时,程序计数器
pc
的值已经更新,它指向当前指令后面第
3
条指令(对于
ARM
指令,它指向当前指令地址加
12
字节的位置;对于
Thumb
指令,它指向当前指令地址加
6
字节的位置)。此时处理器将值(
pc-4
)保存到
lr_abt
中,它指向当前指令后面第
2
条指令,所以返回操作可以通过下面指令实现:
SUBS
PC,
LR_abt,
#8
注:
LR_abt
为中止
模式下
LR
,并不存在
LR_abt
寄存器,为方便读者理解加上
_abt
上述每一种异常发生时,其返回地址都要根据具体异常类型进行重新修复返回地址,再次强调下,被打断程序的返回地址保存在对应异常模式下的
LR_excep
里。
1.1.4
异常向量表
异常向量表是一段特定内存地址空间,每种
ARM
异常对应一个字长空间(
4Bytes
),正好是一条
32
位指令长度,当异常发生时,
CPU
强制将
PC
的值设置为当前异常对应的固定内存地址。如表
3-4
所示是
S3C2440
的异常向量表。
表
3-4
异常向量表
注:
异常向量也可以出现在高地址
0xFFFF0000
处,当今操作系统为了控制内存访问权限,通常会开启虚拟内存,开启了虚拟内存之后,内存的开始空间通常为内核进程空间,和页表空间,异常向量表不能再安装在
0
地址处了
ARM
的例外优先级从高到低依次为
Reset
→
Data abort
→
FIQ
→
IRQ
→
Prefetch abort
→
Undefined instruction/SWI
。
跳入异常向量表操作是异常发生时,硬件自动完成的,剩下的异常处理任务完全交给了程序员。由上表可知,异常向量是一个固定的内存地址,我们可以通过向该地址处写一条跳转指令,让它跳向我们自己定义的异常处理程序的入口,就可以完成异常处理了。
正是由于异常向量表的存在,才让硬件异常处理和程序员自定义处理程序有机联系起来。异常向量表里
0x00000000
地址处是
reset
复位异常,之所以它为
0
地址,是因为
CPU
在上电时自动从
0
地址处加载指令,由此可见将复位异常安装在此地址处也是前后接合起来设计的,不得不感叹
CPU
设计师的伟大,其后面分别是其余
7
种异常向量,每种异常向量都占有四个字节,正好是一条指令的大小,最后一个异常是快速中断异常,将其安装在此也有它的意义,在
0x0000001C
地址处可以直接存放快速中断的处理程序,不用设置跳转指令,这样可以节省一个时钟周期,加快快速中断处理时间。
我们可以通过简单的使用下面的指令来安装异常向量表:
b reset
;
跳入
reset
处理程序
b HandleUndef
;
跳入未定义处理程序
b HandSWI
;
跳入软中断处理程序
b HandPrefetchAbt
;
跳入预取指令处理程序
b HandDataAbt
;
跳入数据访问中止处理程序
b HandNoUsed
;
跳入未使用程序
b HandleIRQ
;
跳入中断处理程序
b HandleFIQ
;
跳入快速中断处理程序
通常安装完异常向量表,跳到我们自己定义的处理程序入口,这时我们还没有保存被打断程序的现场,因此在异常处理程序的入口里先要保存打断程序现场。
保存执行现场:
异常处理程序最开始,要保存被打断程序的执行现场,程序的执行现场无非就是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
STMFD
SP_excep!,
{R0 – R12,
LR_excep}
注
:
LR_abt
,
SP_excep
分别为对应异常模式下
LR
和
SP
,
为方便读者理解加上
_abt
需要注意的是,在跳转到异常处理程序入口时,已经切换到对应异常模式下了,因此这里的
SP
是异常模式下的
SP_excep
了,所以被打断程序现场(寄存器数据)是保存在异常模式下的栈里,上述指令将
R0~R12
全部都保存到了异常模式栈,最后将修改完的被打断程序返回地址入栈保存,之所以保存该返回地址就是将来可以通过类似:
MOV
PC,
LR
的指令,返回用户程序继续执行。
异常发生后,要针对异常类型进行处理,因此,每种异常都有自己的异常处理程序,异常处理过程通过下节的系统中断处理来进行分析。
1.1.5
异常处理的返回
异常处理完成之后,返回被打断程序继续执行,具体操作如下:
l
恢复被打断程序运行时寄存器数据
l
恢复程序运行时状态
CPSR
l
通过进入异常时保存的返回地址,返回到被打断程序继续执行
异常发生后,进入异常处理程序时,将用户程序寄存器
R0~R12
里的数据保存在了异常模式下栈里面,异常处理完返回时,要将栈里保存的的数据再恢复回原先
R0~R12
里,毫无疑问在异常处理过程中必须要保证异常处理入口和出口时栈指针
SP_excep
要一样,否则恢复到
R0~R12
里的数据不正确,返回被打断程序时执行现场不一致,出现问题,虽然将执行现场恢复了,但是此时还是在异常模式下,
CPSR
里的状态是异常模式下状态,因此要恢复
SPSR_excep
里的保存状态到
CPSR
里,
SPSR_excep
是被打断程序执行时的状态,在恢复
SPSR_excep
到
CPSR
的同时,
CPU
的模式和状态从异常模式切换回了被打断程序执行时的模式和状态。此刻程序现场恢复了,状态也恢复了,但
PC
里的值仍然指向异常模式下的地址空间,我们要让
CPU
继续执行被打断程序,因此要再手动改变
PC
的值为进入异常时的返回地址,该地址在异常处理入口时已经计算好,直接将
PC = LR_excep
即可。
上述操作可以一步一步实现,但是通常我们可以通过一条指令实现上述全部操作:
LDMFD
SP_excp!,
{r0-r12,
pc}^
注
:
SP_excep
为对应异常模式下
SP
,
^
符号表示恢复
SPSR_excep
到
CPSR
以上操作可以用下图来描述:
图
3-2
中断处理示意图
++++++++++++++++++++++++++++++++++++++++++
本文系本站原创,欢迎转载! 转载请注明出处:
http://blog.csdn.net/mr_raptor/article/details/6556195
++++++++++++++++++++++++++++++++++++++++++