|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
ARM异常处理:
9 B% V0 S! ^2 @# T只要正常的程序流被暂时中止,处理器就进入异常模式。例如响应一个来自外设的中断。在处理异常之前,ARM内核保存当前的处理器状态,这样当处理程序结束是可以恢复执行原来的程序。
6 c7 I6 j, C M' j0 u h注意:如果同时发生两个或更多异常,那么将按照固定的顺序来处理异常 。
! T9 |/ o$ q7 zARM支持的异常种类:
" H' |# y7 j! R1 x& ? P) }一、异常的进入与退出* P6 y( L! ^% @
当一种异常发生时,硬件就会自动执行如下动作:+ R( b/ I% [5 W4 u/ P7 O1 E
(1)将CPSR保存到相应异常模式下的SPSR中/ f& y1 R& Z V! N
(2)把PC寄存器保存到相应异常模式下的LR中
+ o F/ O/ |- w* s! R7 ~(3)将CPSR设置成相应的异常模式
8 v( s2 q- c/ H$ O z- t* z8 A; a3 v(4)设置PC寄存器的值为相应处理程序的入口地址# N6 {# x) m# P" `) h( Z2 T; r
可以总结如下:
" Z4 ?# F7 X* S- p! m% c( V 在这里我们需要重点研究的是,异常产生后,最后PC会指到哪里去呢?这个事情实际不需要我们操心,ARM核在设计的时候就已经确定好了,也就是经常我们所说的异常向量表。异常向量表:
8 J& K* U# M2 A8 u: X. T) ` `在ARM7,ARM9/10等处理器,异常向量表可以存放在以 0x00000000或0xffff00000其始的地址。默认是以零地址开始存放的。可能有些同学还是有些晕,我们来举个例子说明一下。$ s4 ^/ E6 v. S( }- j( I0 L, n
例如:ARM处理器正在执行指令,此时外部硬件产生了一个中断。此时将产生IRQ异常,然后ARM核就会自动完成我们上面说的4步。完成前3步后,ARM核会强制将pc的值修改为0x18。修改完成后,处理器就开始从0x18这个地址取指令执行。
( N) p% ^7 q) }' N2 d- V从上图可以知道,不同的异常产生时,ARM核修改的PC值不一样。例如:如果是swi指令引起的异常,ARM核最后就会修改pc的值为0x08。
; Z$ |6 i+ X t5 a3 v5 }. v是不是异常向量表,一定要放在0x00000000或0xffff00000其始的地址呢?答案:不是,现在cortex-A系列的处理器可以将异常向量表放在任何位置,拿ARM核收到异常后,它怎么知道应该将pc的值修改为多少呢?这就需要我们通过协处理指令告诉它了。
$ @# l* j! z: E$ @4 F9 w5 I例如:在cortex-A8上,我们可以操作如下协处理指令,来告诉ARM核异常向量表的位置。
1 [0 k" A: u+ p4 X3 z0 y: wcortex-A8官方手册:3.2.68节有详细说明' ]3 q. b* V! L, K) @) R5 W
如将告诉ARM核,异常向量表存放在0x20008000
- w' {. @4 n/ H# w9 B( ^6 T' a# Lldr r0,=0x20008000
8 N1 ]- U7 t- g# \' xmcr p15,0,r0,c12,c0,0
- w7 r, D- Y' U# C好了,到这里大家已经知道了异常是什么,当异常产生的时候,ARM核都会自动做那些事情。当然,当异常产生的时候,我们应该对异常做出处理,处理完之后,要返回异常产生之前的场景继续运行。就像,有些时候,我们在做事情的时候,生病了,我们就需要到医生那里去治疗一下,等治疗完成之后,就必须把生病前的事情接着后面干。, _* o8 B/ C, M# H; \
有些人,肯定忍不住了,我知道了异常,也知道异常产生后,pc会指向异常向量表,那异常向量表中,到底放什么东西呀,我该怎么处理我的异常呢?
8 d* @+ v8 \$ x! R首先,回答第一个问题,异常向量表中存放的就是去医生的火箭。坐上火箭就可以到医生哪了,这叫一个字"快"。医生,火箭.....
& I1 Q- v( R. U! D* R& T呵呵,别折磨大家了,我们公布答案吧!看下面) v: Q: p5 ?$ P2 m0 S3 h2 {
从上面我们可以知道,异常向量表里面存放的都是跳转指令。有些时直接通过b指令实现的,有些时通过修改pc值实现的。这也就是刚刚我说到的"火箭"。跳转的目的是跳到一个地方对异常进行处理。也就是我说到的到医生那里去"治疗"。5 T$ g. P" q: I$ K r
我们以irq异常为例子,来说明我们需要干的事情
( T* I+ y- l- e- ~) _irq :
! m# d) Y( W5 Q: V4 kSUB LR,LR,#4 ;计算返回地址
9 |$ ~( @6 i: [/ ~STMFD SP!,{R0-R3,LR} ;保存使用到的寄存器
. {) [9 ~, @5 b' c2 x. L$ z干里你想干的事情* b2 k' [: @* p2 r3 E
.....+ _, K8 H7 h3 Y: W2 h! Z
LDMFD SP!,{R0-R3,PC}^ ;中断返回' s% C' y, P2 N: w
注意:当异常结束时,异常处理程序必须:
; K% c0 D/ n4 d" j) F% s0 y1.将LR中的值减去偏移量后存入PC,偏移量根据异常的类型而有所不同;' X- N" h2 d4 S i1 j. l
2.将SPSR的值复制回CPSR;4 L$ ]& X7 i/ ?+ L! n
3.清零中断禁止标志。
0 j+ k- ^! M) L& k, F注:恢复CPSR的动作会将T、F和I位自动恢复为异常发生前的值。; o# B4 S3 _5 ^" `7 s
0 s& F6 y4 T3 C z( a, T5 e. j5 M9 D! Q A. u2 w
哎,终于说完了异常。下面我们用一句话总结一下:异常产生需要保存现场(ARM核已经自动为我们做了),异常返回的时候需要恢复现场(需要程序员自动完成)。, H! }4 G: \. b/ } a2 y
__________________________________________________________________________________________________________
) C6 P" h, N6 g! P6 a下面我们来详细说明异常产生的原因,以及异常返回时,异常模式的lr应该保存的值是多少。最后我们会以软中断实验,来给大家强化对异常的理解。, S9 S$ W+ B5 ]; u, z
重要基础知识:R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,人们习惯性约定将“正在执行的指令作为参考点”,称之为当前第一条指令,因此PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址,即:PC值=当前程序执行位置+8;
3 S( {+ ? }) C$ |# V注意:不管是几级流水线都统一按照三级流水线来分析,这一点已经向ARM官方求证过。* q* z6 C( x- N8 o% H) }1 A
(1)快速中断异常
# E2 F& W0 J/ [快速中断请求(FIQ)适用于对一个突发事件的响应,这得益于在ARM状态中,快速中断模式有8个专用的寄存器可用来满足寄存器保护的需要(这可以加速上下文切换的速度)。3 ~* _0 y( k/ L9 ?5 s: A8 d
不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过下面的指令从中断返回:
: F' @1 J. P) p8 L7 zSUBS pc,R14_fiq,#4
3 p$ M6 o0 k1 m8 A0 l5 [, G在一个特权模式中,可以通过置位CPSR中的F位来禁止FIQ异常。
9 _+ Y0 I+ U# o5 T$ @' O; O(2)中断请求异常
# i; @- k% ?# g' J中断请求(IRQ)异常是一个由nIRQ输入端的低电平所产生的正常中断(在具体的芯片中,nIRQ由片内外设拉低,nIRQ是内核的一个信号,对用户不可见)。IRQ的优先级低于FIQ。对于FIQ序列它是被屏蔽的。任何时候在一个特权模式下,都可通过置位CPSR中的I 位来禁止IRQ。, m" k4 \7 ]9 @3 w$ G. t/ n( A
不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过执行下面的指令从中断返回:" v" v1 ?3 Q) }. n
SUBS PC,R14_fiq,#4
# C. ^. j5 W4 [$ x$ d7 h" d; S分析IRQ 和 FIQ异常中断处理的返回:9 w. T3 Z" W! q. o- N$ q1 d& Y% p
指令地址 对应于PC3 s' q: G1 \: h5 K" ^' _% A
A PC-8 执行此指令完成后(!)查询IRQ及FIQ,如果有中断请求则产生中断.
( R9 B! p- t# f" h2 d$ ZA+4 PC-4
7 \, M" L5 i, ~1 b7 D9 bA+8 PC ;lr!: P2 ?8 N. [0 N4 P& [" Y, [
(此时PC的值已经更新,指向A+12.将当前PC-4(即A+8)
- i' s5 d) l. z, P' k保存到LR.返回时,要接着执行A+4(LR-4)处的指令,所以返回指令为
- d. @2 b/ Q. R" ^& A3 ZSUBS PC, LR,#4(PC=A+4=LR-4)& t2 m$ V# k' F& v' f8 `' e! k( y
白话解释:对于普中断和快中断异常:0 @' M. G" t. L6 f% @
中断必须在一条指令执行完以后被检测到,如正在执行指令甲时发生了中断,不等指令甲执行完是不/ c9 c. ~6 w1 ~# X
会处理该中断的,发生异常时pc已经更新(A+12);5 Y- [6 J& l+ S! c; C$ n
lr = pc – 4(这时处理器决定的,无法更改!)即A+8; }% T; Q9 C) j8 n f" P$ W
返回后,应执行被中断而没有执行的指令(上面的A+4),所以返回时,pc = lr-4
6 X! X0 V. j& @# G' q(3)中止
: \$ Q A! ~$ p, ]" g- t3 [. ~0 ~中止发生在对存储器的访问不能完成时,中止包含两种类型:
, i1 Y4 a5 ?1 O: l预取中止 : 发生在指令预取过程中
" [8 n( l+ l/ q& p. s数据中止 : 发生在对数据访问时
; n: f. P; F. Y, Q3 g7 U% TA.预取中止
/ s7 ?, E. B' t* n1 T, \8 T当发生预取中止时,ARM核将预取的指令标记为无效,但在指令达到流水线的执行阶段时才进入异常。如果指令在流水线中因为发生分支而没有被执行,中止将不会发生。
+ m; n0 ~1 c& a J7 O9 @8 R在处理中止的原因之后,不管处于哪种处理器操作状态,处理程序都会执行下面的指令恢复PC和CPSR并重试被中止的指令:
2 P( S a1 o! ^0 K" a9 p2 C4 dSUBS PC,R14_abt,#41 B: `4 g2 I) G3 i" T! m/ E' y
分析指令预取中止异常中断处理的返回:, n) \& q5 E6 ]* o: y8 e
指令地址
3 ?5 ?# A) u3 bA PC-8 执行本指令时发生异常, 7 u3 g6 K0 f1 o5 f
A+4 PC-4 处理器将A+4(PC-4)保存到LR. ;lr!
6 B* ^+ j# J+ OA+8 PC
) `' M4 Z( E- W T) t4 b9 \返回时,发生指令预取中止的指令A(PC-8)处重新执行,所以返回指令为
" g9 E& k, K1 e2 K- VSUBS PC, LR,#4(PC=A=LR-4)8 D# \) W* F; o) @
白话解释:对于预取指令中止异常:
) j( N# G8 N, r6 y 发生预取指令异常时,是在执行时发生的异常,pc未更新,即pc = A+85 [) I) i, o9 T& q1 g& Q- f
lr = pc – 4(这时处理器决定的,无法更改!)即A+4
; f+ I( R( k2 Y' f 由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-4
E! n) m" |! C( h/ B6 O. F$ L7 z% \* ?B.数据中止2 G# h' `1 V& J- N
指令地址9 j1 G' d4 t6 [/ D
A PC-8 本指令访问有问题的数据,产生中断时,PC的值已经更新 9 L, F5 O2 Q# n5 E6 I9 ^2 n. Y
A+4 PC-4 中断发生时PC=A+12,处理器将A+8(PC-4)保存到LR.6 m% q2 a% q/ y K" J+ o$ @5 B" W
A+8 PC ;lr!
. a, f `! p* Q% t |, T返回时,要返回到A处继续执行,所以指令为SUBS PC, LR,#8.(PC=A=LR-8) `' J: Y' Q: M* A' I1 b
白话解释:对于数据访问中止异常:0 M' T7 U" E, Z% T `8 b- B
发生数据访问中止异常时,是在执行时访问数据错误导致的异常,pc已经更新,即pc = A+12
& r( G# X" O% @% U lr = pc – 4(这时处理器决定的,无法更改!)即A+8
- D, g3 ^) V: S+ x" U! E 由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-8
8 \7 a* F" ^1 I% L(4)软件中断指令- W$ g3 A. V& i
所有的任务都是运行在用户模式下的,因此任务只能读CPSR而不能写SPSR。任务切换到特权模式下唯一的途径就是使用一个SWI指令调用,SWI指令强迫处理器从用户模式切换到SVC管理模式,并且IRQ自动关闭,所以软件中断方式常被用于系统调用。
% q" _ N! f& c. c, {/ v, {(5)未定义指令异常
0 u% u" B( L. H(1)当ARM在对一条未定义指令进行译码时,发现这是一条自己和系统内任何协处理器都无法执行的指令时,就会发生未定义指令异常;3 ]3 H) E0 h! P" K% x* T' C
(2)由于是在对未定义指令译码时发生异常,所以PC的值等于未定义指令的地址+4(即刚好为中断返回地址),因此R14保存的值是 中断返回地址 ,所以当异常要返回时可执行以下指令:& `1 V) F. e! s8 d! ~2 W! v
MOVS PC,R14_und + T3 ~; Y" \' d4 s U" a
分析:SWI和和未定义指令异常中断的返回:5 D; ]; d5 Z/ f' f
指令地址: J j6 Z& Q6 g+ L4 n) Q% e
A PC-8 当前指令为SWI或未定义指令 此时发生异常PC的值还没有更新.
' N& _2 B r: ], C" x! ]) e6 N2 M+ lA+4 PC-4 中断时处理器将PC-4保存到LR ;lr!
7 X: O9 Y; @& ] L2 t. TA+8 PC
7 k2 ?1 ]. N# v返回时,从发生中断的指令A(PC-8)的下一条指令A+4(PC-4)处开始执行,所以直接
: v. b+ c5 ]; t0 E/ \" B3 h. Q把LR的值赋给PC就行了,具体指令为MOVPC,LR (PC=A+4=LR)3 ^' E; I6 q5 G7 r4 z2 d2 ~8 A
白话解释:对于SWI和未定义指令异常:
5 D' w: X, E9 R# D0 w4 B 发生异常时pc没有更新,根据ARM的三级流水线原理,pc没有更新,仍然等于(A+8);# I7 q$ G$ _$ }( [ S- K. }
lr = pc – 4(这时处理器决定的,无法更改!)即A+40 |8 F' F: d1 o, P, u% @
由于这类异常返回后应执行下一条指令(A+4),所以返回时,pc = lr即可0 J$ M/ x: X4 A, t+ H% P2 G
最后我们来总结一下: S% P' d# T* x) c* d. K; l7 ^
引起PC更新的原因一种是数据中止,还有就是中断了.
/ N- Q j. S& W, ?8 T 中断必须是在一条指令执行完毕后才能被检测到,所以它中断的只是还未执行的那条指令(pc - 8),2 Q1 l) a" u" C+ G c# U
所以pc = lr – 4;. _; }9 M! u$ S& e
与中断相同,SWI和未定义指令异常也是返回到下一条指令(pc - 4),只是他们在执行时,PC的值并没' N* h! B( i; S. @
有更新,所以pc = lr;; n, i$ S9 n5 u# H8 Z
预取指令中止异常,也没有发生pc更新,但它还得重新执行发生异常的那条指令,所以pc = lr – 4;; G" L6 s" H, F5 t' J3 {
数据访问中止异常,发生了pc更新,并且它也需要重新执行发生异常的那条指令,所以pc = lr – 8;
" @$ \& ^. ?" |! |8 h! X: V当多个异常同时发生时,一个固定的优先级系统决定它们被处理的顺序:/ c i" A1 |0 Y, ^
______________________________________________________________________________________________________________________
4 w# \. F8 @& \4 U( R% T0 ]7 b二、SWI 实验
- `; [. W( \* |: R3 v1 S(1)swi指令
7 s# [' i. `) l6 c9 N* Z# k# |1 w2 W, @7 W8 v% i
8 W+ }( a6 ]) s* Z/ A2 ~3 y' z8 Y, QSWI指令用于产生软中断,从而实现在从户模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。在其它模式下也可使用SWI指令,处理器同样地切换到管理模式。
8 d9 j( s9 U- u% b$ G9 e该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。2 C2 v$ l1 b* r$ W$ R' K8 ]; \
注意:
( W7 n9 P9 ]& c( f* hARM 内核不提供直接传递软中断(SWI)号到处理程序的机制:
: X: [3 ~. P) Q6 @5 P3 _6 GSWI 处理程序必须定位SWI 指令,并提取SWI指令中的常数域2 [! _( g! k3 a2 M
为此, SWI 处理程序必须确定SWI 调用是在哪一种状态(ARM/Thumb).* P; p8 ^2 |* }
检查 SPSR 的 T-bit: n7 A3 V) c2 x7 Z* f- l
SWI 指令在ARM 状态下在 LR-4 位置, Thumb 状态下在LR-2位置
, T9 O0 a' ~4 W& f# WSWI 指令按相应的格式译码:2 [. c* G4 _9 r2 P% g
例如:5 A/ b: f, g2 I) _
/ V# F2 A$ m& X7 k9 c+ bSWI 13
' s( L( I. k) R, B6 y思考,当软中断产生后,怎么获取它的软中断号呢?/ r5 X* c; D0 E4 K
实验的核心代码如下:
8 N# G* G5 Z9 ? p! H/ t8 i( V9 Q' d
software_interrupt:
$ y) @( s% E& T& ~@设置软中断所在模式的栈) q: \& ~0 ^. b; [, d
ldr sp,=0x34000; f) u" d0 l# N. F8 U6 e
@保存用到的寄存器6 R. c5 [. e7 s% y' [7 T8 Q
stmfd sp!,{r0-r2,lr}
- f1 e. J* O; S4 }% x9 _@获取swi指令对应的机器码! R$ D, Z; Y/ ?# U. U
ldr r0,[lr,#-4]
2 k9 J$ {' z2 N7 ~5 S8 ~@获取软中断号
; [; G" w- @) |6 [bic r0,r0,#0xff000000
0 c5 ~ ~; |8 l _@调用C处理函数,求累加和
2 ~5 ~0 Y0 r7 J; w) s5 i, tbl calc_sum3 t" M* O3 I- Q6 @
@获得函数返回值,存放在r1* y, B. D" B8 q5 L" j
mov r1,r0
+ q* n0 J6 j- J7 D- P- p0 H/ W@恢复现场,^表示目标寄存器是pc时,传递数据给pc,同时更新CPSR
; @: W c: T; K9 _6 a+ I. oldmfd sp!,{r0-r2,pc}^
% g: Z; Y4 E o/ v |
|