|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
ARM异常处理:
# q, p, v6 w$ _, H K& m. y只要正常的程序流被暂时中止,处理器就进入异常模式。例如响应一个来自外设的中断。在处理异常之前,ARM内核保存当前的处理器状态,这样当处理程序结束是可以恢复执行原来的程序。9 C v4 r( m3 E6 \
注意:如果同时发生两个或更多异常,那么将按照固定的顺序来处理异常 。" c, Q' }+ S$ q3 F2 F. _
ARM支持的异常种类:( B* L' \( s" G1 m: {+ x& h
一、异常的进入与退出
& b! Z" K, ~+ s" o# b0 f# s当一种异常发生时,硬件就会自动执行如下动作:
8 |) c2 x4 f$ |(1)将CPSR保存到相应异常模式下的SPSR中
: y! A' A/ N6 B4 P(2)把PC寄存器保存到相应异常模式下的LR中
+ q7 M6 v0 ~# O(3)将CPSR设置成相应的异常模式- C* a4 s! g- m
(4)设置PC寄存器的值为相应处理程序的入口地址) \/ t: y+ T; i5 e a8 ^9 a
可以总结如下:
9 a4 a# E: t+ u1 q( r 在这里我们需要重点研究的是,异常产生后,最后PC会指到哪里去呢?这个事情实际不需要我们操心,ARM核在设计的时候就已经确定好了,也就是经常我们所说的异常向量表。异常向量表:
2 N5 Y. f5 m8 a5 l% i: e! y6 i8 b) U在ARM7,ARM9/10等处理器,异常向量表可以存放在以 0x00000000或0xffff00000其始的地址。默认是以零地址开始存放的。可能有些同学还是有些晕,我们来举个例子说明一下。4 G% u" I2 B9 R- B) ]( Z9 |
例如:ARM处理器正在执行指令,此时外部硬件产生了一个中断。此时将产生IRQ异常,然后ARM核就会自动完成我们上面说的4步。完成前3步后,ARM核会强制将pc的值修改为0x18。修改完成后,处理器就开始从0x18这个地址取指令执行。. T9 M6 m' Z# P0 O! h' E
从上图可以知道,不同的异常产生时,ARM核修改的PC值不一样。例如:如果是swi指令引起的异常,ARM核最后就会修改pc的值为0x08。
% O( \) |+ T, t5 j' @是不是异常向量表,一定要放在0x00000000或0xffff00000其始的地址呢?答案:不是,现在cortex-A系列的处理器可以将异常向量表放在任何位置,拿ARM核收到异常后,它怎么知道应该将pc的值修改为多少呢?这就需要我们通过协处理指令告诉它了。
3 D' ?8 q" d* m! E1 M6 O例如:在cortex-A8上,我们可以操作如下协处理指令,来告诉ARM核异常向量表的位置。( j g q6 D: s: B; ^2 }" I% z
cortex-A8官方手册:3.2.68节有详细说明. I# H( F7 c4 | x/ z
如将告诉ARM核,异常向量表存放在0x200080007 V0 R9 }; i( v2 V2 w- A
ldr r0,=0x20008000
8 ?, X1 P- v0 g% Q! |mcr p15,0,r0,c12,c0,0
# F- A+ O+ t* e3 k) M% K) M好了,到这里大家已经知道了异常是什么,当异常产生的时候,ARM核都会自动做那些事情。当然,当异常产生的时候,我们应该对异常做出处理,处理完之后,要返回异常产生之前的场景继续运行。就像,有些时候,我们在做事情的时候,生病了,我们就需要到医生那里去治疗一下,等治疗完成之后,就必须把生病前的事情接着后面干。- s+ X/ u; j( v! p/ P) x/ |" f
有些人,肯定忍不住了,我知道了异常,也知道异常产生后,pc会指向异常向量表,那异常向量表中,到底放什么东西呀,我该怎么处理我的异常呢?: [% C: d; e4 {! F
首先,回答第一个问题,异常向量表中存放的就是去医生的火箭。坐上火箭就可以到医生哪了,这叫一个字"快"。医生,火箭.....: X7 @: q6 [9 T: m* h
呵呵,别折磨大家了,我们公布答案吧!看下面* G6 p; @4 c) A+ J
从上面我们可以知道,异常向量表里面存放的都是跳转指令。有些时直接通过b指令实现的,有些时通过修改pc值实现的。这也就是刚刚我说到的"火箭"。跳转的目的是跳到一个地方对异常进行处理。也就是我说到的到医生那里去"治疗"。
3 `; s. Z1 a/ e+ s我们以irq异常为例子,来说明我们需要干的事情9 s$ M/ A- d5 t: Q( T2 K3 k
irq :
" W9 w# B' j) p5 N% N% `" OSUB LR,LR,#4 ;计算返回地址9 n1 a$ R1 o6 K% F1 K$ Z( A# D! g
STMFD SP!,{R0-R3,LR} ;保存使用到的寄存器
3 K+ Y3 D6 f: d; ^( C& i' ?3 F3 q干里你想干的事情
3 p: l% v0 E5 l) K9 s S.....8 a9 F* n7 G; _/ S1 w# P+ P- U* S! _
LDMFD SP!,{R0-R3,PC}^ ;中断返回" h/ l* f9 q; z$ q
注意:当异常结束时,异常处理程序必须:
2 ^% n; r- @6 N/ o! R5 H1.将LR中的值减去偏移量后存入PC,偏移量根据异常的类型而有所不同;& q; z5 t& R5 L- z! R' c n
2.将SPSR的值复制回CPSR;$ Q+ S" t# K/ \- O4 Q+ c2 e
3.清零中断禁止标志。
5 d" ]* T( O' j$ ~. F注:恢复CPSR的动作会将T、F和I位自动恢复为异常发生前的值。" C, ~% E/ m% g6 i
$ L" N) a$ |$ w/ `; d
( H4 k# B% r+ ^& o) ^. _ O4 [, p& f哎,终于说完了异常。下面我们用一句话总结一下:异常产生需要保存现场(ARM核已经自动为我们做了),异常返回的时候需要恢复现场(需要程序员自动完成)。
( n* C: G/ W) p2 J4 l0 v2 v% N1 @4 `__________________________________________________________________________________________________________
" G8 Y1 g2 h& V下面我们来详细说明异常产生的原因,以及异常返回时,异常模式的lr应该保存的值是多少。最后我们会以软中断实验,来给大家强化对异常的理解。# l5 {* _2 |) T8 h
重要基础知识:R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,人们习惯性约定将“正在执行的指令作为参考点”,称之为当前第一条指令,因此PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址,即:PC值=当前程序执行位置+8;
1 f" b" I+ N* R( u) h4 z注意:不管是几级流水线都统一按照三级流水线来分析,这一点已经向ARM官方求证过。
, D4 n) [: i; g- W$ Q( l(1)快速中断异常
1 k1 I7 E" e; K: A0 @: a+ K/ @快速中断请求(FIQ)适用于对一个突发事件的响应,这得益于在ARM状态中,快速中断模式有8个专用的寄存器可用来满足寄存器保护的需要(这可以加速上下文切换的速度)。, e, ?, I% R6 O; \
不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过下面的指令从中断返回:
, f+ V/ N, g7 F7 i+ L: l# G# tSUBS pc,R14_fiq,#4' L! ?& T; ?2 `, G: t" u9 R! o4 E8 g
在一个特权模式中,可以通过置位CPSR中的F位来禁止FIQ异常。8 G( _: g2 G- t( V1 T5 w
(2)中断请求异常; o2 K5 z" t4 h- [
中断请求(IRQ)异常是一个由nIRQ输入端的低电平所产生的正常中断(在具体的芯片中,nIRQ由片内外设拉低,nIRQ是内核的一个信号,对用户不可见)。IRQ的优先级低于FIQ。对于FIQ序列它是被屏蔽的。任何时候在一个特权模式下,都可通过置位CPSR中的I 位来禁止IRQ。1 @4 a; E }6 b( m
不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过执行下面的指令从中断返回:
% X. x0 Q: q2 {+ |$ E Q7 d6 g2 SSUBS PC,R14_fiq,#4
% N I/ h- m4 o2 {8 ?2 a* k2 j分析IRQ 和 FIQ异常中断处理的返回:. @+ V: C* y/ u
指令地址 对应于PC
2 Z2 D9 G3 z/ x: r g, g" ~A PC-8 执行此指令完成后(!)查询IRQ及FIQ,如果有中断请求则产生中断.- }# j' Q4 O+ V' O
A+4 PC-4
- O3 {2 {, y3 F' ]: i! n! s. SA+8 PC ;lr!
5 E) B! A2 ?8 q7 m6 m (此时PC的值已经更新,指向A+12.将当前PC-4(即A+8)
, p! B( @0 L* J+ V+ S保存到LR.返回时,要接着执行A+4(LR-4)处的指令,所以返回指令为
8 _& T9 V& X2 U* e! g \" ySUBS PC, LR,#4(PC=A+4=LR-4)" D4 e# r" B. o. n. Z2 w5 l: r; O
白话解释:对于普中断和快中断异常:9 U$ U# ^1 `4 E( |9 F4 y, I- ~
中断必须在一条指令执行完以后被检测到,如正在执行指令甲时发生了中断,不等指令甲执行完是不$ i: f& Y4 z- b! H. v0 ]' Q
会处理该中断的,发生异常时pc已经更新(A+12);
/ T0 E7 s* a1 p- M lr = pc – 4(这时处理器决定的,无法更改!)即A+8' _; P4 h. U4 |
返回后,应执行被中断而没有执行的指令(上面的A+4),所以返回时,pc = lr-43 H" I' P1 c: k6 z
(3)中止 O" i/ ]9 {+ i. w: I
中止发生在对存储器的访问不能完成时,中止包含两种类型:- z- L# B" b' x' a
预取中止 : 发生在指令预取过程中8 ?- @7 c: N$ z
数据中止 : 发生在对数据访问时
0 j) F$ t$ a8 U- WA.预取中止
+ ?2 x* r+ [1 Y) ?2 g1 C当发生预取中止时,ARM核将预取的指令标记为无效,但在指令达到流水线的执行阶段时才进入异常。如果指令在流水线中因为发生分支而没有被执行,中止将不会发生。" n6 Y$ x9 q, ^( I( v$ w G
在处理中止的原因之后,不管处于哪种处理器操作状态,处理程序都会执行下面的指令恢复PC和CPSR并重试被中止的指令:
9 d M% d6 K, b% HSUBS PC,R14_abt,#42 A* c2 B1 ]. ]$ f, G
分析指令预取中止异常中断处理的返回:0 x! r, j5 r) C' K v4 e4 z. M
指令地址
) {" N2 s8 T! E& Q* U- j3 I+ H4 H9 ^A PC-8 执行本指令时发生异常,
3 @8 n$ e' J5 S2 ?0 RA+4 PC-4 处理器将A+4(PC-4)保存到LR. ;lr!% B7 o$ v* v1 ~9 Z- y; ~
A+8 PC
0 F- |0 l3 T; ~! R a; G返回时,发生指令预取中止的指令A(PC-8)处重新执行,所以返回指令为
/ z$ i: ?" ~( ?+ v- BSUBS PC, LR,#4(PC=A=LR-4)
. S& s6 Q4 j$ }9 P% ~; M2 X5 ]! w白话解释:对于预取指令中止异常:
3 K( ?/ Y% }/ s# O, G' D 发生预取指令异常时,是在执行时发生的异常,pc未更新,即pc = A+8
! n: N5 S4 C+ K7 q lr = pc – 4(这时处理器决定的,无法更改!)即A+4
$ X7 J3 d+ d" ?" w/ e1 O& y 由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-4
4 D1 S8 K5 ?* r! F& f `B.数据中止4 i$ G. y8 N3 |5 a1 V
指令地址
0 u' o1 R0 x1 B) W+ Q! |A PC-8 本指令访问有问题的数据,产生中断时,PC的值已经更新
, M* B% W+ A3 D% a# qA+4 PC-4 中断发生时PC=A+12,处理器将A+8(PC-4)保存到LR.' T @+ t+ _8 t, G5 R( d( N; r# N
A+8 PC ;lr!
4 V9 W7 ~% Y+ N( X, h返回时,要返回到A处继续执行,所以指令为SUBS PC, LR,#8.(PC=A=LR-8)2 O: r( n% h- w F3 \3 c7 f& c
白话解释:对于数据访问中止异常:/ s# e) l( ^; r4 r- A
发生数据访问中止异常时,是在执行时访问数据错误导致的异常,pc已经更新,即pc = A+12
& ~5 Z `! D* v% d lr = pc – 4(这时处理器决定的,无法更改!)即A+81 ?! O4 E' Q6 [" `6 h+ n( \
由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-8
4 h3 e" W2 h V2 O `(4)软件中断指令
# |" z* L% e! b* `! t所有的任务都是运行在用户模式下的,因此任务只能读CPSR而不能写SPSR。任务切换到特权模式下唯一的途径就是使用一个SWI指令调用,SWI指令强迫处理器从用户模式切换到SVC管理模式,并且IRQ自动关闭,所以软件中断方式常被用于系统调用。/ F/ t$ M5 w$ v* U7 D
(5)未定义指令异常: x% }5 [0 l0 u" S
(1)当ARM在对一条未定义指令进行译码时,发现这是一条自己和系统内任何协处理器都无法执行的指令时,就会发生未定义指令异常;
6 O C3 D1 Y6 g% @. [; q3 ^9 f0 y(2)由于是在对未定义指令译码时发生异常,所以PC的值等于未定义指令的地址+4(即刚好为中断返回地址),因此R14保存的值是 中断返回地址 ,所以当异常要返回时可执行以下指令:( x7 p7 p. G# N- m6 \) f' `: I
MOVS PC,R14_und 1 t, O) d* q! L; P/ g2 P
分析:SWI和和未定义指令异常中断的返回:, y9 v2 f# H: {( `. L- D
指令地址
9 ?0 d0 f5 z9 SA PC-8 当前指令为SWI或未定义指令 此时发生异常PC的值还没有更新.
( ^. q- [! q2 T7 eA+4 PC-4 中断时处理器将PC-4保存到LR ;lr!. N% Y9 I! `9 T( O: k" O
A+8 PC 5 t3 N9 L8 L' \) L* k5 L; P
返回时,从发生中断的指令A(PC-8)的下一条指令A+4(PC-4)处开始执行,所以直接$ T9 L! G, f9 t
把LR的值赋给PC就行了,具体指令为MOVPC,LR (PC=A+4=LR)6 `5 n9 h1 C4 P% ?, l+ Z* i8 @
白话解释:对于SWI和未定义指令异常:
3 a$ t. k! w) V' {& `3 F 发生异常时pc没有更新,根据ARM的三级流水线原理,pc没有更新,仍然等于(A+8);! i' h& O/ Q5 ]* f, x' s
lr = pc – 4(这时处理器决定的,无法更改!)即A+4
; x0 l. A0 O9 V- x4 W% \1 @6 Z 由于这类异常返回后应执行下一条指令(A+4),所以返回时,pc = lr即可( C) N4 c" A5 F! z. p
最后我们来总结一下:
5 w7 ?2 r5 Y: i1 P+ z4 b 引起PC更新的原因一种是数据中止,还有就是中断了. A. X3 U% ^" o' q2 l' U6 n
中断必须是在一条指令执行完毕后才能被检测到,所以它中断的只是还未执行的那条指令(pc - 8),1 e) f+ W" o* _5 A; V% \9 o O
所以pc = lr – 4;
" b! r; c- s5 B5 H 与中断相同,SWI和未定义指令异常也是返回到下一条指令(pc - 4),只是他们在执行时,PC的值并没
# h9 e* o& }1 L) g$ k有更新,所以pc = lr;
9 }: b) \& R% m3 I3 Q; `& r7 O 预取指令中止异常,也没有发生pc更新,但它还得重新执行发生异常的那条指令,所以pc = lr – 4;" P, k: \' ]" \0 n
数据访问中止异常,发生了pc更新,并且它也需要重新执行发生异常的那条指令,所以pc = lr – 8;# c: W& [2 q; r8 A" P) n5 g( M
当多个异常同时发生时,一个固定的优先级系统决定它们被处理的顺序:
2 a" B* c% U6 x$ e9 r. l2 a Q) G______________________________________________________________________________________________________________________5 L; h" H3 v+ D) d8 g: w
二、SWI 实验1 p' C. r& H3 _$ Q" B* o8 ^; T
(1)swi指令
, f) w' {! W' `& y8 c5 B; S8 k5 }5 w' k" j( i8 k0 L. D+ o: g4 L; b
5 u6 i4 E% @* g( U5 K$ J! @& M! nSWI指令用于产生软中断,从而实现在从户模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。在其它模式下也可使用SWI指令,处理器同样地切换到管理模式。" }$ n7 p2 J/ \9 U$ Z9 z* _
该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。% d% z- d) w. p* `. ~. r
注意:* E2 x7 N. X3 J
ARM 内核不提供直接传递软中断(SWI)号到处理程序的机制:3 M0 J! O) @: R
SWI 处理程序必须定位SWI 指令,并提取SWI指令中的常数域
* X N: C6 w' L$ _1 u6 r为此, SWI 处理程序必须确定SWI 调用是在哪一种状态(ARM/Thumb).
5 D- | s4 P1 c# w6 A% {$ h. {检查 SPSR 的 T-bit: x) N$ z5 L6 w9 i% ~1 c, o X
SWI 指令在ARM 状态下在 LR-4 位置, Thumb 状态下在LR-2位置6 P; G- F2 v; t/ g1 X
SWI 指令按相应的格式译码:0 P0 |, Q8 [* }
例如:5 k+ A4 K+ j' a( B/ |
h: g; A3 z# \1 p
SWI 133 Y" [+ J. I5 Z& C
思考,当软中断产生后,怎么获取它的软中断号呢?% t* f5 X! q4 _) i( {
实验的核心代码如下:
2 V8 a! K. j/ c9 F* W& K2 q5 U4 e$ i' O3 t! [2 ^2 E
software_interrupt:) x( ^8 U9 d7 ~: L9 R" t% f
@设置软中断所在模式的栈) ]% O1 r, n- e) M6 [$ Q
ldr sp,=0x34000
. o! v+ e; y. R I O# r w5 c@保存用到的寄存器
6 [; @0 L7 C5 j+ p6 zstmfd sp!,{r0-r2,lr}+ g$ \/ q" @: X" @' Y
@获取swi指令对应的机器码7 t1 p8 y, E* W- E: j8 O1 E
ldr r0,[lr,#-4]
: u! G9 l8 n# }8 l4 l2 q9 t/ E@获取软中断号
: \6 V0 y: a* E4 ?- Ebic r0,r0,#0xff000000
* F3 K; N( `& F/ q9 {6 k@调用C处理函数,求累加和, |# w7 i; S/ E- ]9 X5 x3 T' L
bl calc_sum
7 _" e h2 J" e% x! m" A! O@获得函数返回值,存放在r11 J0 Q/ k# }% R2 w1 e G( p$ A
mov r1,r04 u2 U! V* c' B% r$ M
@恢复现场,^表示目标寄存器是pc时,传递数据给pc,同时更新CPSR
! ]" b0 }7 c% N1 T8 R2 |9 Yldmfd sp!,{r0-r2,pc}^
. a5 C2 b0 t+ A* W5 a |
|