找回密码
 注册
关于网站域名变更的通知
查看: 437|回复: 1
打印 上一主题 下一主题

ARM异常处理

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-5-6 10:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
ARM异常处理:/ A& M8 g' a/ }3 h$ l
只要正常的程序流被暂时中止,处理器就进入异常模式。例如响应一个来自外设的中断。在处理异常之前,ARM内核保存当前的处理器状态,这样当处理程序结束是可以恢复执行原来的程序。" A6 n: h+ ]! p+ x0 r5 s6 c3 M
注意:如果同时发生两个或更多异常,那么将按照固定的顺序来处理异常 。
( [5 z' w6 [3 I' o% `3 _/ [ARM支持的异常种类:4 Y9 X( |% q( p4 ^" X9 G7 Q
一、异常的进入与退出
6 Q& W; ^& M/ }7 I  x$ u% H当一种异常发生时,硬件就会自动执行如下动作:0 H% b4 h9 T9 a& v# l3 v
(1)将CPSR保存到相应异常模式下的SPSR中
: b3 p. C* Z8 p" o7 ^! _(2)把PC寄存器保存到相应异常模式下的LR中$ U  ?/ L" D3 Z8 G* B  }
(3)将CPSR设置成相应的异常模式4 F! R) e5 G- w
(4)设置PC寄存器的值为相应处理程序的入口地址
; C, B. Y% Y/ `& d. U$ i# P1 ~' u( f可以总结如下:8 H/ ~# a" x4 O
   在这里我们需要重点研究的是,异常产生后,最后PC会指到哪里去呢?这个事情实际不需要我们操心,ARM核在设计的时候就已经确定好了,也就是经常我们所说的异常向量表。异常向量表:
+ m& \, k" }8 `4 `在ARM7,ARM9/10等处理器,异常向量表可以存放在以 0x00000000或0xffff00000其始的地址。默认是以零地址开始存放的。可能有些同学还是有些晕,我们来举个例子说明一下。( B( S7 S7 S% F0 @* A) c3 v* p% T
例如:ARM处理器正在执行指令,此时外部硬件产生了一个中断。此时将产生IRQ异常,然后ARM核就会自动完成我们上面说的4步。完成前3步后,ARM核会强制将pc的值修改为0x18。修改完成后,处理器就开始从0x18这个地址取指令执行。3 T/ ?3 k  g" d1 b2 {& Z( N! d# b
从上图可以知道,不同的异常产生时,ARM核修改的PC值不一样。例如:如果是swi指令引起的异常,ARM核最后就会修改pc的值为0x08。) ^! F* _1 p  M7 b2 i1 l- t  e3 B
是不是异常向量表,一定要放在0x00000000或0xffff00000其始的地址呢?答案:不是,现在cortex-A系列的处理器可以将异常向量表放在任何位置,拿ARM核收到异常后,它怎么知道应该将pc的值修改为多少呢?这就需要我们通过协处理指令告诉它了。
. P# w: c4 }- d  _3 e8 [3 |$ y例如:在cortex-A8上,我们可以操作如下协处理指令,来告诉ARM核异常向量表的位置。
# a: p" e# {9 t, O1 b* E' {cortex-A8官方手册:3.2.68节有详细说明
5 ~6 p- k/ \0 y- E5 [1 Y如将告诉ARM核,异常向量表存放在0x20008000
3 i2 H4 }' @- a" z- f9 K, j. ?2 Sldr r0,=0x20008000
3 v  _- W6 |! I! `mcr p15,0,r0,c12,c0,0
" @& H1 k* T+ W* P7 G3 C% q. U0 u好了,到这里大家已经知道了异常是什么,当异常产生的时候,ARM核都会自动做那些事情。当然,当异常产生的时候,我们应该对异常做出处理,处理完之后,要返回异常产生之前的场景继续运行。就像,有些时候,我们在做事情的时候,生病了,我们就需要到医生那里去治疗一下,等治疗完成之后,就必须把生病前的事情接着后面干。+ ~- ~4 y5 ?* |3 x! K2 [' V
有些人,肯定忍不住了,我知道了异常,也知道异常产生后,pc会指向异常向量表,那异常向量表中,到底放什么东西呀,我该怎么处理我的异常呢?6 B* f2 ^* W& j4 d5 ?& D
首先,回答第一个问题,异常向量表中存放的就是去医生的火箭。坐上火箭就可以到医生哪了,这叫一个字"快"。医生,火箭...... u% x# K$ C/ ?2 K4 g
呵呵,别折磨大家了,我们公布答案吧!看下面
  |# m9 w  v" Q0 ~从上面我们可以知道,异常向量表里面存放的都是跳转指令。有些时直接通过b指令实现的,有些时通过修改pc值实现的。这也就是刚刚我说到的"火箭"。跳转的目的是跳到一个地方对异常进行处理。也就是我说到的到医生那里去"治疗"。) o$ M( ^# G+ @' S
我们以irq异常为例子,来说明我们需要干的事情; K& f0 a* p6 ]# I3 z/ z% U4 S
irq :
$ V) [+ ]. ?( V" v  KSUB    LR,LR,#4 ;计算返回地址
2 V0 H; W  l/ W9 aSTMFD  SP!,{R0-R3,LR} ;保存使用到的寄存器
2 M6 }# {: n. O: K; z干里你想干的事情' \% \7 N7 X4 r& p
.....& ^8 h4 x) j8 B2 Y
LDMFD  SP!,{R0-R3,PC}^ ;中断返回
1 o" C- e0 \8 I注意:当异常结束时,异常处理程序必须:1 g( R- O1 n6 Z5 w/ n9 m
1.将LR中的值减去偏移量后存入PC,偏移量根据异常的类型而有所不同;
# B$ Q* H% Z0 E# q( E1 T2 o2.将SPSR的值复制回CPSR;
: b" V0 q8 m8 p6 s3.清零中断禁止标志。+ Q! n% O4 n0 N4 {$ R
注:恢复CPSR的动作会将T、F和I位自动恢复为异常发生前的值。  L: {2 E, A# [+ L" S  P" L
1 q. g6 }2 L$ N8 |1 Y5 Z- q5 }: B

. U3 }  H* V* Z哎,终于说完了异常。下面我们用一句话总结一下:异常产生需要保存现场(ARM核已经自动为我们做了),异常返回的时候需要恢复现场(需要程序员自动完成)。* B( G9 D, \2 [4 X7 D3 ~3 ]8 S
__________________________________________________________________________________________________________
9 d# v+ B% o9 Y- M) y下面我们来详细说明异常产生的原因,以及异常返回时,异常模式的lr应该保存的值是多少。最后我们会以软中断实验,来给大家强化对异常的理解。* @, t8 }( B) e  E8 c" n+ C! \9 {, \
重要基础知识:R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,人们习惯性约定将“正在执行的指令作为参考点”,称之为当前第一条指令,因此PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址,即:PC值=当前程序执行位置+8;
4 ?& o% v- ]& ?. O/ q注意:不管是几级流水线都统一按照三级流水线来分析,这一点已经向ARM官方求证过。' \. Q9 D# f# q
(1)快速中断异常; w6 B6 q* M: C& G2 i
快速中断请求(FIQ)适用于对一个突发事件的响应,这得益于在ARM状态中,快速中断模式有8个专用的寄存器可用来满足寄存器保护的需要(这可以加速上下文切换的速度)。
- O, ?) N4 T* Q不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过下面的指令从中断返回:
& y1 M9 ~. \7 W. QSUBS pc,R14_fiq,#4
! r; J; N% r! p& e在一个特权模式中,可以通过置位CPSR中的F位来禁止FIQ异常。
* [3 a2 @3 x& b. m; _& @+ p(2)中断请求异常
6 k9 |) r  T/ l; U5 v/ B中断请求(IRQ)异常是一个由nIRQ输入端的低电平所产生的正常中断(在具体的芯片中,nIRQ由片内外设拉低,nIRQ是内核的一个信号,对用户不可见)。IRQ的优先级低于FIQ。对于FIQ序列它是被屏蔽的。任何时候在一个特权模式下,都可通过置位CPSR中的I 位来禁止IRQ。
9 D5 d0 K( `+ f不管异常入口是来自ARM状态还是Thumb状态,FIQ处理程序都会通过执行下面的指令从中断返回:2 b0 R2 D3 B. Z, l, R
SUBS    PC,R14_fiq,#4
) a. t% x+ ?: J# ~- o4 x* u4 F分析IRQ 和 FIQ异常中断处理的返回:
, ^$ }1 Q2 U, R0 @- d指令地址  对应于PC* b; r9 S1 c& ~: f) ?2 M+ O! t
A           PC-8      执行此指令完成后(!)查询IRQ及FIQ,如果有中断请求则产生中断.5 M- j) P+ y* O' z) {" u! e
A+4       PC-4
  y- e& E8 G% b+ V$ x) YA+8       PC   ;lr!
3 W' |" [% T' V                    (此时PC的值已经更新,指向A+12.将当前PC-4(即A+8). [  U- P7 H7 I% _7 h" z! o  t
保存到LR.返回时,要接着执行A+4(LR-4)处的指令,所以返回指令为
: X1 X+ _/ f3 }9 M, ^9 f% xSUBS PC, LR,#4(PC=A+4=LR-4)7 I7 y7 U6 t5 A" G, U& E! K
白话解释:对于普中断和快中断异常:
* B* ~+ R- p/ j/ q3 f  中断必须在一条指令执行完以后被检测到,如正在执行指令甲时发生了中断,不等指令甲执行完是不  f: j; ^, k4 a+ N
会处理该中断的,发生异常时pc已经更新(A+12);
3 @$ U% Y9 O  G4 q  lr = pc – 4(这时处理器决定的,无法更改!)即A+8
# f3 o0 v2 r" a3 ?* X  返回后,应执行被中断而没有执行的指令(上面的A+4),所以返回时,pc = lr-4
  A+ ~5 `! b5 t4 P/ n(3)中止
: `4 n$ U/ s* B2 \% }( R  t2 n中止发生在对存储器的访问不能完成时,中止包含两种类型:; U8 W" a& D. T5 V) ?* m( u( _
预取中止   :   发生在指令预取过程中0 I- U# ^. E% C+ T* D& I
数据中止   :   发生在对数据访问时
' c( j3 W* [- k. vA.预取中止
6 R0 Z5 y' z) p" j/ a( ]当发生预取中止时,ARM核将预取的指令标记为无效,但在指令达到流水线的执行阶段时才进入异常。如果指令在流水线中因为发生分支而没有被执行,中止将不会发生。
8 g' k1 k+ F% M% y在处理中止的原因之后,不管处于哪种处理器操作状态,处理程序都会执行下面的指令恢复PC和CPSR并重试被中止的指令:
7 E3 Z  v8 O7 q4 B/ a5 a/ FSUBS  PC,R14_abt,#4
9 l9 t: Y$ s9 ?# r1 h* d, G分析指令预取中止异常中断处理的返回:7 T; s# D' y: s3 t$ _, X5 B, g
指令地址9 N) U, I6 y4 D3 j
A       PC-8     执行本指令时发生异常,  
# W+ D6 l7 k% LA+4   PC-4     处理器将A+4(PC-4)保存到LR.  ;lr!7 k0 R$ z6 J9 M* \# S% P; \, a
A+8   PC+ }' j: R/ D; H" |" U* B
返回时,发生指令预取中止的指令A(PC-8)处重新执行,所以返回指令为$ p( V8 b* W1 E6 o9 L: i: I' K
SUBS PC, LR,#4(PC=A=LR-4)" u: H6 h& {+ x) @
白话解释:对于预取指令中止异常:4 f: j1 p* @6 y
  发生预取指令异常时,是在执行时发生的异常,pc未更新,即pc = A+8  P$ W9 _/ E& B) T) b
  lr = pc – 4(这时处理器决定的,无法更改!)即A+4
% [% G  j2 }2 [2 x  t) o  由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-49 c3 N: z5 t5 A' n. n8 v! l' `
B.数据中止8 E0 x5 b5 L- M
指令地址
$ h( ^' r+ r, @A           PC-8    本指令访问有问题的数据,产生中断时,PC的值已经更新   
, x9 X  K) a3 L- x# _A+4        PC-4  中断发生时PC=A+12,处理器将A+8(PC-4)保存到LR.
: r9 }; z/ N: S, sA+8        PC   ;lr!
7 }1 l8 c) J, v2 O1 x+ H返回时,要返回到A处继续执行,所以指令为SUBS PC,  LR,#8.(PC=A=LR-8)9 m; }  S% S0 A
白话解释:对于数据访问中止异常:, a2 b' x' {- W  H- s6 a: l* T
  发生数据访问中止异常时,是在执行时访问数据错误导致的异常,pc已经更新,即pc = A+12
' `  ]- b5 o* h) T  lr = pc – 4(这时处理器决定的,无法更改!)即A+8  @7 O, @3 n, r
  由于这类异常返回后应重新执行异常的那个指令(A),所以返回时,pc = lr-8
  Y' y. g9 r( j- K/ }; ~* K9 i/ ^; R# ]' u(4)软件中断指令
/ \; O7 @% F4 |* R: H7 J' B! I/ Y所有的任务都是运行在用户模式下的,因此任务只能读CPSR而不能写SPSR。任务切换到特权模式下唯一的途径就是使用一个SWI指令调用,SWI指令强迫处理器从用户模式切换到SVC管理模式,并且IRQ自动关闭,所以软件中断方式常被用于系统调用。% V+ V, o$ P& s5 m6 Q1 V
(5)未定义指令异常
6 B7 e9 C! y* O! j& s(1)当ARM在对一条未定义指令进行译码时,发现这是一条自己和系统内任何协处理器都无法执行的指令时,就会发生未定义指令异常;
; @% y7 f, R+ C  G$ |; |# S5 ](2)由于是在对未定义指令译码时发生异常,所以PC的值等于未定义指令的地址+4(即刚好为中断返回地址),因此R14保存的值是 中断返回地址 ,所以当异常要返回时可执行以下指令:3 K# O; f- B) y4 f+ B  `
MOVS   PC,R14_und   0 b, x1 Z& G7 H3 `& v9 M& x
分析:SWI和和未定义指令异常中断的返回:
# K1 Y, M* c2 T, h5 K* i指令地址$ s$ B6 m+ t' ^$ |3 t
A         PC-8 当前指令为SWI或未定义指令 此时发生异常PC的值还没有更新.
8 |. ?, r" d- j4 V% i$ WA+4       PC-4 中断时处理器将PC-4保存到LR   ;lr!2 Y5 a( X" O0 k- V; q) o. K
A+8       PC  6 S  G; ~7 D$ r6 V2 p, L+ |
返回时,从发生中断的指令A(PC-8)的下一条指令A+4(PC-4)处开始执行,所以直接; \7 m: v/ Q' h& M+ G
把LR的值赋给PC就行了,具体指令为MOVPC,LR  (PC=A+4=LR)
% O* |6 M  \5 }" Z6 X白话解释:对于SWI和未定义指令异常:( K( f2 E, C: k4 j; N- n' m8 d
  发生异常时pc没有更新,根据ARM的三级流水线原理,pc没有更新,仍然等于(A+8);
- u0 m2 I) \6 q" e4 I  lr = pc – 4(这时处理器决定的,无法更改!)即A+4% Y( |; U" E% W9 |! y+ \7 l
  由于这类异常返回后应执行下一条指令(A+4),所以返回时,pc = lr即可# M+ {9 U( e) U6 R
最后我们来总结一下:! c  g" j5 M. g# p7 x2 F
  引起PC更新的原因一种是数据中止,还有就是中断了.* W4 ?* T" C7 ?9 N1 n& H  @
  中断必须是在一条指令执行完毕后才能被检测到,所以它中断的只是还未执行的那条指令(pc - 8),
) u& l* T0 d! M; |+ v- @, _所以pc = lr – 4;
) l2 ?, ~' m. `  与中断相同,SWI和未定义指令异常也是返回到下一条指令(pc - 4),只是他们在执行时,PC的值并没
0 W6 `+ l% o' P+ n7 M有更新,所以pc = lr;+ y" W, b! `# ]) P! V2 M1 z# [
  预取指令中止异常,也没有发生pc更新,但它还得重新执行发生异常的那条指令,所以pc = lr – 4;$ \4 {0 V- }* j& ~, f  }, T) t
  数据访问中止异常,发生了pc更新,并且它也需要重新执行发生异常的那条指令,所以pc = lr – 8;  x6 }" I& N" s( r/ @" ?0 [* g8 U
当多个异常同时发生时,一个固定的优先级系统决定它们被处理的顺序:; v1 S+ a& d3 q. B. l" [
______________________________________________________________________________________________________________________# `0 T: E& r* T3 L
二、SWI 实验
+ F0 A3 ~3 J6 O$ d5 t: O; q$ q' [6 ~0 U7 X(1)swi指令0 T* s' c8 \3 A! g! U& `, t

5 i- H) v$ `! _0 e5 U2 @* c# o4 I6 O5 r4 }/ U
SWI指令用于产生软中断,从而实现在从户模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。在其它模式下也可使用SWI指令,处理器同样地切换到管理模式。/ d/ h  G5 X+ z* I! C( g6 o
该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。: A6 {2 _0 J8 `) @5 j
注意:
( D( V+ }0 h! s; q! i! A0 F4 {ARM 内核不提供直接传递软中断(SWI)号到处理程序的机制:; F* m2 ]7 F+ @+ F* i9 v# m9 T
SWI 处理程序必须定位SWI 指令,并提取SWI指令中的常数域$ G1 Z8 R6 i4 w6 |+ \0 ^. l; g
为此, SWI 处理程序必须确定SWI 调用是在哪一种状态(ARM/Thumb).
8 E+ U. m4 i4 |- k检查 SPSR 的 T-bit! P) l7 m- k1 ~- O! o
SWI 指令在ARM 状态下在 LR-4 位置, Thumb 状态下在LR-2位置
% J$ n* B9 l( r4 F0 Y! xSWI 指令按相应的格式译码:
* K* t- X. Y: u! q8 E4 {* f! w; ^例如:! n/ q# c) `* D# P$ i% \! l

7 d$ O( n8 V- Y" b- Z/ @SWI 13! n$ v$ k- i. ?+ f6 _7 T1 V
思考,当软中断产生后,怎么获取它的软中断号呢?6 i+ [7 U0 }9 R3 t/ a! p7 [
实验的核心代码如下:# T. y, {8 K: \& i

6 _8 v+ q5 R1 rsoftware_interrupt:
& z2 s+ F! q- t@设置软中断所在模式的栈: ~2 H. t7 S$ [4 g. J4 p- B
ldr sp,=0x34000
; `! d( @. V, I0 T0 _2 [@保存用到的寄存器
* O/ T# M0 D) S. e! G& w# mstmfd sp!,{r0-r2,lr}
  d, u& N: Q3 L( Y* i+ a) Q@获取swi指令对应的机器码
% H3 c1 `) i2 `ldr r0,[lr,#-4]. ~5 R" F& f3 ~7 ?
@获取软中断号4 }, q5 m  X0 q+ h
bic r0,r0,#0xff000000
" j8 L3 ^( F+ e@调用C处理函数,求累加和
( c3 Q$ g) x+ |9 l. Q" Y# B! K- ]# a6 Ubl calc_sum
. e) C- c* k0 @% U@获得函数返回值,存放在r15 W1 T# t1 Z4 V2 O& F' ^
mov r1,r0
4 _  K) Q! O3 G+ [* q7 f@恢复现场,^表示目标寄存器是pc时,传递数据给pc,同时更新CPSR
# \, \* K3 \" P% bldmfd sp!,{r0-r2,pc}^

: y  D1 F8 x4 R( R$ V# K0 m9 `
  • TA的每日心情
    开心
    2022-12-26 15:46
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-5-6 13:44 | 只看该作者
    ARM异常处理,描述的很详细
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 15:30 , Processed in 0.156250 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表