當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > ARM異常處理
ARM異常處理
時間:2018-09-29 來源:未知
只要正常的程序流被暫時中止,處理器就進(jìn)入異常模式。例如響應(yīng)一個來自外設(shè)的中斷。在處理異常之前,ARM內(nèi)核保存當(dāng)前的處理器狀態(tài),這樣當(dāng)處理程序結(jié)束是可以恢復(fù)執(zhí)行原來的程序。
注意:如果同時發(fā)生兩個或更多異常,那么將按照固定的順序來處理異常 。
ARM支持的異常種類:

一、異常的進(jìn)入與退出
當(dāng)一種異常發(fā)生時,硬件就會自動執(zhí)行如下動作:
(1)將CPSR保存到相應(yīng)異常模式下的SPSR中
(2)把PC寄存器保存到相應(yīng)異常模式下的LR中
(3)將CPSR設(shè)置成相應(yīng)的異常模式
(4)設(shè)置PC寄存器的值為相應(yīng)處理程序的入口地址
可以總結(jié)如下圖:

呵呵,在這里我們需要重點研究的是,異常產(chǎn)生后,后PC會指到哪里去呢?這個事情實際不需要我們操心,ARM核在設(shè)計的時候就已經(jīng)確定好了,也就是經(jīng)常我們所說的異常向量表。異常向量表:

在ARM7,ARM9/10等處理器,異常向量表可以存放在以 0x00000000或0xffff00000其始的地址。默認(rèn)是以零地址開始存放的。可能有些同學(xué)還是有些暈,我們來舉個例子說明一下。
例如:ARM處理器正在執(zhí)行指令,此時外部硬件產(chǎn)生了一個中斷。此時將產(chǎn)生IRQ異常,然后ARM核就會自動完成我們上面說的4步。完成前3步后,ARM核會強(qiáng)制將pc的值修改為0x18。修改完成后,處理器就開始從0x18這個地址取指令執(zhí)行。
從上圖可以知道,不同的異常產(chǎn)生時,ARM核修改的PC值不一樣。例如:如果是swi指令引起的異常,ARM核后就會修改pc的值為0x08。
是不是異常向量表,一定要放在0x00000000或0xffff00000其始的地址呢?答案:不是,現(xiàn)在cortex-A系列的處理器可以將異常向量表放在任何位置,拿ARM核收到異常后,它怎么知道應(yīng)該將pc的值修改為多少呢?這就需要我們通過協(xié)處理指令告訴它了。
例如:在cortex-A8上,我們可以操作如下協(xié)處理指令,來告訴ARM核異常向量表的位置。
cortex-A8官方手冊:3.2.68節(jié)有詳細(xì)說明

如將告訴ARM核,異常向量表存放在0x20008000
ldr r0,=0x20008000
mcr p15,0,r0,c12,c0,0
好了,到這里大家已經(jīng)知道了異常是什么,當(dāng)異常產(chǎn)生的時候,ARM核都會自動做那些事情。當(dāng)然,當(dāng)異常產(chǎn)生的時候,我們應(yīng)該對異常做出處理,處理完之后,要返回異常產(chǎn)生之前的場景繼續(xù)運(yùn)行。就像,有些時候,我們在做事情的時候,生病了,我們就需要到醫(yī)生那里去治療一下,等治療完成之后,就必須把生病前的事情接著后面干。
有些人,肯定忍不住了,我知道了異常,也知道異常產(chǎn)生后,pc會指向異常向量表,那異常向量表中,到底放什么東西呀,我該怎么處理我的異常呢?
首先,回答第一個問題,異常向量表中存放的就是去醫(yī)生的火箭。坐上火箭就可以到醫(yī)生哪了,這叫一個字"快"。醫(yī)生,火箭.....
呵呵,別折磨大家了,我們公布答案吧!看下面

從上面我們可以知道,異常向量表里面存放的都是跳轉(zhuǎn)指令。有些時直接通過b指令實現(xiàn)的,有些時通過修改pc值實現(xiàn)的。這也就是剛剛我說到的"火箭"。跳轉(zhuǎn)的目的是跳到一個地方對異常進(jìn)行處理。也就是我說到的到醫(yī)生那里去"治療"。
我們以irq異常為例子,來說明我們需要干的事情
irq :
SUB LR,LR,#4 ;計算返回地址
STMFD SP!,{R0-R3,LR} ;保存使用到的寄存器
干里你想干的事情
.....
LDMFD SP!,{R0-R3,PC}^ ;中斷返回
注意:當(dāng)異常結(jié)束時,異常處理程序必須:
1.將LR中的值減去偏移量后存入PC,偏移量根據(jù)異常的類型而有所不同;
2.將SPSR的值復(fù)制回CPSR;
3.清零中斷禁止標(biāo)志。
注:恢復(fù)CPSR的動作會將T、F和I位自動恢復(fù)為異常發(fā)生前的值。
哎,終于說完了異常。下面我們用一句話總結(jié)一下:異常產(chǎn)生需要保存現(xiàn)場(ARM核已經(jīng)自動為我們做了),異常返回的時候需要恢復(fù)現(xiàn)場(需要程序員自動完成)。
下面我們來詳細(xì)說明異常產(chǎn)生的原因,以及異常返回時,異常模式的lr應(yīng)該保存的值是多少。后我們會以軟中斷實驗,來給大家強(qiáng)化對異常的理解。
重要基礎(chǔ)知識:R15(PC)總是指向“正在取指”的指令,而不是指向“正在執(zhí)行”的指令或正在“譯碼”的指令。一般來說,人們習(xí)慣性約定將“正在執(zhí)行的指令作為參考點”,稱之為當(dāng)前第一條指令,因此PC總是指向第三條指令。當(dāng)ARM狀態(tài)時,每條指令為4字節(jié)長,所以PC始終指向該指令地址加8字節(jié)的地址,即:PC值=當(dāng)前程序執(zhí)行位置+8;
注意:不管是幾級流水線都統(tǒng)一按照三級流水線來分析,這一點已經(jīng)向ARM官方求證過。
(1)快速中斷異常
快速中斷請求(FIQ)適用于對一個突發(fā)事件的響應(yīng),這得益于在ARM狀態(tài)中,快速中斷模式有8個專用的寄存器可用來滿足寄存器保護(hù)的需要(這可以加速上下文切換的速度)。
不管異常入口是來自ARM狀態(tài)還是Thumb狀態(tài),F(xiàn)IQ處理程序都會通過下面的指令從中斷返回:
SUBS pc,R14_fiq,#4
在一個特權(quán)模式中,可以通過置位CPSR中的F位來禁止FIQ異常。
(2)中斷請求異常
中斷請求(IRQ)異常是一個由nIRQ輸入端的低電平所產(chǎn)生的正常中斷(在具體的芯片中,nIRQ由片內(nèi)外設(shè)拉低,nIRQ是內(nèi)核的一個信號,對用戶不可見)。IRQ的優(yōu)先級低于FIQ。對于FIQ序列它是被屏蔽的。任何時候在一個特權(quán)模式下,都可通過置位CPSR中的I 位來禁止IRQ。
不管異常入口是來自ARM狀態(tài)還是Thumb狀態(tài),F(xiàn)IQ處理程序都會通過執(zhí)行下面的指令從中斷返回:
SUBS PC,R14_fiq,#4
分析IRQ 和 FIQ異常中斷處理的返回:
指令地址 對應(yīng)于PC
A PC-8 執(zhí)行此指令完成后(!)查詢IRQ及FIQ,如果有中斷請求則產(chǎn)生中斷.
A+4 PC-4
A+8 PC ;lr!
(此時PC的值已經(jīng)更新,指向A+12.將當(dāng)前PC-4(即A+8)
保存到LR.返回時,要接著執(zhí)行A+4(LR-4)處的指令,所以返回指令為
SUBS PC, LR,#4(PC=A+4=LR-4)
白話解釋:對于普中斷和快中斷異常:
中斷必須在一條指令執(zhí)行完以后被檢測到,如正在執(zhí)行指令甲時發(fā)生了中斷,不等指令甲執(zhí)行完是不
會處理該中斷的,發(fā)生異常時pc已經(jīng)更新(A+12);
lr = pc – 4(這時處理器決定的,無法更改!)即A+8
返回后,應(yīng)執(zhí)行被中斷而沒有執(zhí)行的指令(上面的A+4),所以返回時,pc = lr-4
(3)中止
中止發(fā)生在對存儲器的訪問不能完成時,中止包含兩種類型:
預(yù)取中止 : 發(fā)生在指令預(yù)取過程中
數(shù)據(jù)中止 : 發(fā)生在對數(shù)據(jù)訪問時
A.預(yù)取中止
當(dāng)發(fā)生預(yù)取中止時,ARM核將預(yù)取的指令標(biāo)記為無效,但在指令達(dá)到流水線的執(zhí)行階段時才進(jìn)入異常。如果指令在流水線中因為發(fā)生分支而沒有被執(zhí)行,中止將不會發(fā)生。
在處理中止的原因之后,不管處于哪種處理器操作狀態(tài),處理程序都會執(zhí)行下面的指令恢復(fù)PC和CPSR并重試被中止的指令:
SUBS PC,R14_abt,#4
分析指令預(yù)取中止異常中斷處理的返回:
指令地址
A PC-8 執(zhí)行本指令時發(fā)生異常,
A+4 PC-4 處理器將A+4(PC-4)保存到LR. ;lr!
A+8 PC
返回時,發(fā)生指令預(yù)取中止的指令A(yù)(PC-8)處重新執(zhí)行,所以返回指令為
SUBS PC, LR,#4(PC=A=LR-4)
白話解釋:對于預(yù)取指令中止異常:
發(fā)生預(yù)取指令異常時,是在執(zhí)行時發(fā)生的異常,pc未更新,即pc = A+8
lr = pc – 4(這時處理器決定的,無法更改!)即A+4
由于這類異常返回后應(yīng)重新執(zhí)行異常的那個指令(A),所以返回時,pc = lr-4
B.數(shù)據(jù)中止
指令地址
A PC-8 本指令訪問有問題的數(shù)據(jù),產(chǎn)生中斷時,PC的值已經(jīng)更新
A+4 PC-4 中斷發(fā)生時PC=A+12,處理器將A+8(PC-4)保存到LR.
A+8 PC ;lr!
返回時,要返回到A處繼續(xù)執(zhí)行,所以指令為SUBS PC, LR,#8.(PC=A=LR-8)
白話解釋:對于數(shù)據(jù)訪問中止異常:
發(fā)生數(shù)據(jù)訪問中止異常時,是在執(zhí)行時訪問數(shù)據(jù)錯誤導(dǎo)致的異常,pc已經(jīng)更新,即pc = A+12
lr = pc – 4(這時處理器決定的,無法更改!)即A+8
由于這類異常返回后應(yīng)重新執(zhí)行異常的那個指令(A),所以返回時,pc = lr-8
(4)軟件中斷指令
所有的任務(wù)都是運(yùn)行在用戶模式下的,因此任務(wù)只能讀CPSR而不能寫SPSR。任務(wù)切換到特權(quán)模式下唯一的途徑就是使用一個SWI指令調(diào)用,SWI指令強(qiáng)迫處理器從用戶模式切換到SVC管理模式,并且IRQ自動關(guān)閉,所以軟件中斷方式常被用于系統(tǒng)調(diào)用。
(5)未定義指令異常
(1)當(dāng)ARM在對一條未定義指令進(jìn)行譯碼時,發(fā)現(xiàn)這是一條自己和系統(tǒng)內(nèi)任何協(xié)處理器都無法執(zhí)行的指令時,就會發(fā)生未定義指令異常;
(2)由于是在對未定義指令譯碼時發(fā)生異常,所以PC的值等于未定義指令的地址+4(即剛好為中斷返回地址),因此R14保存的值是 中斷返回地址 ,所以當(dāng)異常要返回時可執(zhí)行以下指令:
MOVS PC,R14_und
分析:SWI和和未定義指令異常中斷的返回:
指令地址
A PC-8 當(dāng)前指令為SWI或未定義指令 此時發(fā)生異常PC的值還沒有更新.
A+4 PC-4 中斷時處理器將PC-4保存到LR ;lr!
A+8 PC
返回時,從發(fā)生中斷的指令A(yù)(PC-8)的下一條指令A(yù)+4(PC-4)處開始執(zhí)行,所以直接
把LR的值賦給PC就行了,具體指令為MOV PC,LR (PC=A+4=LR)
白話解釋:對于SWI和未定義指令異常:
發(fā)生異常時pc沒有更新,根據(jù)ARM的三級流水線原理,pc沒有更新,仍然等于(A+8);
lr = pc – 4(這時處理器決定的,無法更改!)即A+4
由于這類異常返回后應(yīng)執(zhí)行下一條指令(A+4),所以返回時,pc = lr即可
后我們來總結(jié)一下:
引起PC更新的原因一種是數(shù)據(jù)中止,還有就是中斷了.
中斷必須是在一條指令執(zhí)行完畢后才能被檢測到,所以它中斷的只是還未執(zhí)行的那條指令(pc - 8),
所以pc = lr – 4;
與中斷相同,SWI和未定義指令異常也是返回到下一條指令(pc - 4),只是他們在執(zhí)行時,PC的值并沒
有更新,所以pc = lr;
預(yù)取指令中止異常,也沒有發(fā)生pc更新,但它還得重新執(zhí)行發(fā)生異常的那條指令,所以pc = lr – 4;
數(shù)據(jù)訪問中止異常,發(fā)生了pc更新,并且它也需要重新執(zhí)行發(fā)生異常的那條指令,所以pc = lr – 8;
當(dāng)多個異常同時發(fā)生時,一個固定的優(yōu)先級系統(tǒng)決定它們被處理的順序:

二、SWI 實驗
(1)swi 指令
SWI指令用于產(chǎn)生軟中斷,從而實現(xiàn)在從戶模式變換到管理模式,并且將CPSR保存到管理模式的SPSR中,然后程序跳轉(zhuǎn)到SWI異常入口。在其它模式下也可使用SWI指令,處理器同樣地切換到管理模式。
該指令主要用于用戶程序調(diào)用操作系統(tǒng)的系統(tǒng)服務(wù),操作系統(tǒng)在SWI異常處理程序中進(jìn)行相應(yīng)的系統(tǒng)服務(wù)。
注意:
ARM 內(nèi)核不提供直接傳遞軟中斷(SWI)號到處理程序的機(jī)制:
SWI 處理程序必須定位SWI 指令,并提取SWI指令中的常數(shù)域
為此, SWI 處理程序必須確定SWI 調(diào)用是在哪一種狀態(tài)(ARM/Thumb).
檢查 SPSR 的 T-bit
SWI 指令在ARM 狀態(tài)下在 LR-4 位置, Thumb 狀態(tài)下在 LR-2位置
SWI 指令按相應(yīng)的格式譯碼:

例如:
SWI 13
思考,當(dāng)軟中斷產(chǎn)生后,怎么獲取它的軟中斷號呢?
實驗的核心代碼如下:
software_interrupt:
@設(shè)置軟中斷所在模式的棧
ldr sp,=0x34000
@保存用到的寄存器
stmfd sp!,{r0-r2,lr}
@獲取swi指令對應(yīng)的機(jī)器碼
ldr r0,[lr,#-4]
@獲取軟中斷號
bic r0,r0,#0xff000000
@調(diào)用C處理函數(shù),求累加和
bl calc_sum
@獲得函數(shù)返回值,存放在r1
mov r1,r0
@恢復(fù)現(xiàn)場,^表示目標(biāo)寄存器是pc時,傳遞數(shù)據(jù)給pc,同時更新CPSR
ldmfd sp!,{r0-r2,pc}^

