當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > ARM異常處理
只要正常的程序流被暫時(shí)中止,處理器就進(jìn)入異常模式。例如響應(yīng)一個(gè)來自外設(shè)的中斷。在處理異常之前,ARM內(nèi)核保存當(dāng)前的處理器狀態(tài),這樣當(dāng)處理程序結(jié)束是可以恢復(fù)執(zhí)行原來的程序。
注意:如果同時(shí)發(fā)生兩個(gè)或更多異常,那么將按照固定的順序來處理異常 。
ARM支持的異常種類:
一、異常的進(jìn)入與退出
當(dāng)一種異常發(fā)生時(shí),硬件就會(huì)自動(dòng)執(zhí)行如下動(dòng)作:
(1)將CPSR保存到相應(yīng)異常模式下的SPSR中
(2)把PC寄存器保存到相應(yīng)異常模式下的LR中
(3)將CPSR設(shè)置成相應(yīng)的異常模式
(4)設(shè)置PC寄存器的值為相應(yīng)處理程序的入口地址
可以總結(jié)如下圖:
呵呵,在這里我們需要重點(diǎn)研究的是,異常產(chǎn)生后,后PC會(huì)指到哪里去呢?這個(gè)事情實(shí)際不需要我們操心,ARM核在設(shè)計(jì)的時(shí)候就已經(jīng)確定好了,也就是經(jīng)常我們所說的異常向量表。異常向量表:
在ARM7,ARM9/10等處理器,異常向量表可以存放在以 0x00000000或0xffff00000其始的地址。默認(rèn)是以零地址開始存放的?赡苡行┩瑢W(xué)還是有些暈,我們來舉個(gè)例子說明一下。
例如:ARM處理器正在執(zhí)行指令,此時(shí)外部硬件產(chǎn)生了一個(gè)中斷。此時(shí)將產(chǎn)生IRQ異常,然后ARM核就會(huì)自動(dòng)完成我們上面說的4步。完成前3步后,ARM核會(huì)強(qiáng)制將pc的值修改為0x18。修改完成后,處理器就開始從0x18這個(gè)地址取指令執(zhí)行。
從上圖可以知道,不同的異常產(chǎn)生時(shí),ARM核修改的PC值不一樣。例如:如果是swi指令引起的異常,ARM核后就會(huì)修改pc的值為0x08。
是不是異常向量表,一定要放在0x00000000或0xffff00000其始的地址呢?答案:不是,現(xiàn)在cortex-A系列的處理器可以將異常向量表放在任何位置,拿ARM核收到異常后,它怎么知道應(yīng)該將pc的值修改為多少呢?這就需要我們通過協(xié)處理指令告訴它了。
例如:在cortex-A8上,我們可以操作如下協(xié)處理指令,來告訴ARM核異常向量表的位置。
cortex-A8官方手冊(cè):3.2.68節(jié)有詳細(xì)說明
如將告訴ARM核,異常向量表存放在0x20008000
ldr r0,=0x20008000
mcr p15,0,r0,c12,c0,0
好了,到這里大家已經(jīng)知道了異常是什么,當(dāng)異常產(chǎn)生的時(shí)候,ARM核都會(huì)自動(dòng)做那些事情。當(dāng)然,當(dāng)異常產(chǎn)生的時(shí)候,我們應(yīng)該對(duì)異常做出處理,處理完之后,要返回異常產(chǎn)生之前的場(chǎng)景繼續(xù)運(yùn)行。就像,有些時(shí)候,我們?cè)谧鍪虑榈臅r(shí)候,生病了,我們就需要到醫(yī)生那里去治療一下,等治療完成之后,就必須把生病前的事情接著后面干。
有些人,肯定忍不住了,我知道了異常,也知道異常產(chǎn)生后,pc會(huì)指向異常向量表,那異常向量表中,到底放什么東西呀,我該怎么處理我的異常呢?
首先,回答第一個(gè)問題,異常向量表中存放的就是去醫(yī)生的火箭。坐上火箭就可以到醫(yī)生哪了,這叫一個(gè)字"快"。醫(yī)生,火箭.....
呵呵,別折磨大家了,我們公布答案吧!看下面
從上面我們可以知道,異常向量表里面存放的都是跳轉(zhuǎn)指令。有些時(shí)直接通過b指令實(shí)現(xiàn)的,有些時(shí)通過修改pc值實(shí)現(xiàn)的。這也就是剛剛我說到的"火箭"。跳轉(zhuǎn)的目的是跳到一個(gè)地方對(duì)異常進(jìn)行處理。也就是我說到的到醫(yī)生那里去"治療"。
我們以irq異常為例子,來說明我們需要干的事情
irq :
SUB LR,LR,#4 ;計(jì)算返回地址
STMFD SP!,{R0-R3,LR} ;保存使用到的寄存器
干里你想干的事情
.....
LDMFD SP!,{R0-R3,PC}^ ;中斷返回
注意:當(dāng)異常結(jié)束時(shí),異常處理程序必須:
1.將LR中的值減去偏移量后存入PC,偏移量根據(jù)異常的類型而有所不同;
2.將SPSR的值復(fù)制回CPSR;
3.清零中斷禁止標(biāo)志。
注:恢復(fù)CPSR的動(dòng)作會(huì)將T、F和I位自動(dòng)恢復(fù)為異常發(fā)生前的值。
哎,終于說完了異常。下面我們用一句話總結(jié)一下:異常產(chǎn)生需要保存現(xiàn)場(chǎng)(ARM核已經(jīng)自動(dòng)為我們做了),異常返回的時(shí)候需要恢復(fù)現(xiàn)場(chǎng)(需要程序員自動(dòng)完成)。
下面我們來詳細(xì)說明異常產(chǎn)生的原因,以及異常返回時(shí),異常模式的lr應(yīng)該保存的值是多少。后我們會(huì)以軟中斷實(shí)驗(yàn),來給大家強(qiáng)化對(duì)異常的理解。
重要基礎(chǔ)知識(shí):R15(PC)總是指向“正在取指”的指令,而不是指向“正在執(zhí)行”的指令或正在“譯碼”的指令。一般來說,人們習(xí)慣性約定將“正在執(zhí)行的指令作為參考點(diǎn)”,稱之為當(dāng)前第一條指令,因此PC總是指向第三條指令。當(dāng)ARM狀態(tài)時(shí),每條指令為4字節(jié)長(zhǎng),所以PC始終指向該指令地址加8字節(jié)的地址,即:PC值=當(dāng)前程序執(zhí)行位置+8;
注意:不管是幾級(jí)流水線都統(tǒng)一按照三級(jí)流水線來分析,這一點(diǎn)已經(jīng)向ARM官方求證過。
(1)快速中斷異常
快速中斷請(qǐng)求(FIQ)適用于對(duì)一個(gè)突發(fā)事件的響應(yīng),這得益于在ARM狀態(tài)中,快速中斷模式有8個(gè)專用的寄存器可用來滿足寄存器保護(hù)的需要(這可以加速上下文切換的速度)。
不管異常入口是來自ARM狀態(tài)還是Thumb狀態(tài),F(xiàn)IQ處理程序都會(huì)通過下面的指令從中斷返回:
SUBS pc,R14_fiq,#4
在一個(gè)特權(quán)模式中,可以通過置位CPSR中的F位來禁止FIQ異常。
(2)中斷請(qǐng)求異常
中斷請(qǐng)求(IRQ)異常是一個(gè)由nIRQ輸入端的低電平所產(chǎn)生的正常中斷(在具體的芯片中,nIRQ由片內(nèi)外設(shè)拉低,nIRQ是內(nèi)核的一個(gè)信號(hào),對(duì)用戶不可見)。IRQ的優(yōu)先級(jí)低于FIQ。對(duì)于FIQ序列它是被屏蔽的。任何時(shí)候在一個(gè)特權(quán)模式下,都可通過置位CPSR中的I 位來禁止IRQ。
不管異常入口是來自ARM狀態(tài)還是Thumb狀態(tài),F(xiàn)IQ處理程序都會(huì)通過執(zhí)行下面的指令從中斷返回:
SUBS PC,R14_fiq,#4
分析IRQ 和 FIQ異常中斷處理的返回:
指令地址 對(duì)應(yīng)于PC
A PC-8 執(zhí)行此指令完成后(!)查詢IRQ及FIQ,如果有中斷請(qǐng)求則產(chǎn)生中斷.
A+4 PC-4
A+8 PC ;lr!
(此時(shí)PC的值已經(jīng)更新,指向A+12.將當(dāng)前PC-4(即A+8)
保存到LR.返回時(shí),要接著執(zhí)行A+4(LR-4)處的指令,所以返回指令為
SUBS PC, LR,#4(PC=A+4=LR-4)
白話解釋:對(duì)于普中斷和快中斷異常:
中斷必須在一條指令執(zhí)行完以后被檢測(cè)到,如正在執(zhí)行指令甲時(shí)發(fā)生了中斷,不等指令甲執(zhí)行完是不
會(huì)處理該中斷的,發(fā)生異常時(shí)pc已經(jīng)更新(A+12);
lr = pc – 4(這時(shí)處理器決定的,無法更改!)即A+8
返回后,應(yīng)執(zhí)行被中斷而沒有執(zhí)行的指令(上面的A+4),所以返回時(shí),pc = lr-4
(3)中止
中止發(fā)生在對(duì)存儲(chǔ)器的訪問不能完成時(shí),中止包含兩種類型:
預(yù)取中止 : 發(fā)生在指令預(yù)取過程中
數(shù)據(jù)中止 : 發(fā)生在對(duì)數(shù)據(jù)訪問時(shí)
A.預(yù)取中止
當(dāng)發(fā)生預(yù)取中止時(shí),ARM核將預(yù)取的指令標(biāo)記為無效,但在指令達(dá)到流水線的執(zhí)行階段時(shí)才進(jìn)入異常。如果指令在流水線中因?yàn)榘l(fā)生分支而沒有被執(zhí)行,中止將不會(huì)發(fā)生。
在處理中止的原因之后,不管處于哪種處理器操作狀態(tài),處理程序都會(huì)執(zhí)行下面的指令恢復(fù)PC和CPSR并重試被中止的指令:
SUBS PC,R14_abt,#4
分析指令預(yù)取中止異常中斷處理的返回:
指令地址
A PC-8 執(zhí)行本指令時(shí)發(fā)生異常,
A+4 PC-4 處理器將A+4(PC-4)保存到LR. ;lr!
A+8 PC
返回時(shí),發(fā)生指令預(yù)取中止的指令A(yù)(PC-8)處重新執(zhí)行,所以返回指令為
SUBS PC, LR,#4(PC=A=LR-4)
白話解釋:對(duì)于預(yù)取指令中止異常:
發(fā)生預(yù)取指令異常時(shí),是在執(zhí)行時(shí)發(fā)生的異常,pc未更新,即pc = A+8
lr = pc – 4(這時(shí)處理器決定的,無法更改!)即A+4
由于這類異常返回后應(yīng)重新執(zhí)行異常的那個(gè)指令(A),所以返回時(shí),pc = lr-4
B.數(shù)據(jù)中止
指令地址
A PC-8 本指令訪問有問題的數(shù)據(jù),產(chǎn)生中斷時(shí),PC的值已經(jīng)更新
A+4 PC-4 中斷發(fā)生時(shí)PC=A+12,處理器將A+8(PC-4)保存到LR.
A+8 PC ;lr!
返回時(shí),要返回到A處繼續(xù)執(zhí)行,所以指令為SUBS PC, LR,#8.(PC=A=LR-8)
白話解釋:對(duì)于數(shù)據(jù)訪問中止異常:
發(fā)生數(shù)據(jù)訪問中止異常時(shí),是在執(zhí)行時(shí)訪問數(shù)據(jù)錯(cuò)誤導(dǎo)致的異常,pc已經(jīng)更新,即pc = A+12
lr = pc – 4(這時(shí)處理器決定的,無法更改!)即A+8
由于這類異常返回后應(yīng)重新執(zhí)行異常的那個(gè)指令(A),所以返回時(shí),pc = lr-8
(4)軟件中斷指令
所有的任務(wù)都是運(yùn)行在用戶模式下的,因此任務(wù)只能讀CPSR而不能寫SPSR。任務(wù)切換到特權(quán)模式下唯一的途徑就是使用一個(gè)SWI指令調(diào)用,SWI指令強(qiáng)迫處理器從用戶模式切換到SVC管理模式,并且IRQ自動(dòng)關(guān)閉,所以軟件中斷方式常被用于系統(tǒng)調(diào)用。
(5)未定義指令異常
(1)當(dāng)ARM在對(duì)一條未定義指令進(jìn)行譯碼時(shí),發(fā)現(xiàn)這是一條自己和系統(tǒng)內(nèi)任何協(xié)處理器都無法執(zhí)行的指令時(shí),就會(huì)發(fā)生未定義指令異常;
(2)由于是在對(duì)未定義指令譯碼時(shí)發(fā)生異常,所以PC的值等于未定義指令的地址+4(即剛好為中斷返回地址),因此R14保存的值是 中斷返回地址 ,所以當(dāng)異常要返回時(shí)可執(zhí)行以下指令:
MOVS PC,R14_und
分析:SWI和和未定義指令異常中斷的返回:
指令地址
A PC-8 當(dāng)前指令為SWI或未定義指令 此時(shí)發(fā)生異常PC的值還沒有更新.
A+4 PC-4 中斷時(shí)處理器將PC-4保存到LR ;lr!
A+8 PC
返回時(shí),從發(fā)生中斷的指令A(yù)(PC-8)的下一條指令A(yù)+4(PC-4)處開始執(zhí)行,所以直接
把LR的值賦給PC就行了,具體指令為MOV PC,LR (PC=A+4=LR)
白話解釋:對(duì)于SWI和未定義指令異常:
發(fā)生異常時(shí)pc沒有更新,根據(jù)ARM的三級(jí)流水線原理,pc沒有更新,仍然等于(A+8);
lr = pc – 4(這時(shí)處理器決定的,無法更改!)即A+4
由于這類異常返回后應(yīng)執(zhí)行下一條指令(A+4),所以返回時(shí),pc = lr即可
后我們來總結(jié)一下:
引起PC更新的原因一種是數(shù)據(jù)中止,還有就是中斷了.
中斷必須是在一條指令執(zhí)行完畢后才能被檢測(cè)到,所以它中斷的只是還未執(zhí)行的那條指令(pc - 8),
所以pc = lr – 4;
與中斷相同,SWI和未定義指令異常也是返回到下一條指令(pc - 4),只是他們?cè)趫?zhí)行時(shí),PC的值并沒
有更新,所以pc = lr;
預(yù)取指令中止異常,也沒有發(fā)生pc更新,但它還得重新執(zhí)行發(fā)生異常的那條指令,所以pc = lr – 4;
數(shù)據(jù)訪問中止異常,發(fā)生了pc更新,并且它也需要重新執(zhí)行發(fā)生異常的那條指令,所以pc = lr – 8;
當(dāng)多個(gè)異常同時(shí)發(fā)生時(shí),一個(gè)固定的優(yōu)先級(jí)系統(tǒng)決定它們被處理的順序:
二、SWI 實(shí)驗(yàn)
(1)swi 指令
SWI指令用于產(chǎn)生軟中斷,從而實(shí)現(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)號(hào)到處理程序的機(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)生后,怎么獲取它的軟中斷號(hào)呢?
實(shí)驗(yàn)的核心代碼如下:
software_interrupt:
@設(shè)置軟中斷所在模式的棧
ldr sp,=0x34000
@保存用到的寄存器
stmfd sp!,{r0-r2,lr}
@獲取swi指令對(duì)應(yīng)的機(jī)器碼
ldr r0,[lr,#-4]
@獲取軟中斷號(hào)
bic r0,r0,#0xff000000
@調(diào)用C處理函數(shù),求累加和
bl calc_sum
@獲得函數(shù)返回值,存放在r1
mov r1,r0
@恢復(fù)現(xiàn)場(chǎng),^表示目標(biāo)寄存器是pc時(shí),傳遞數(shù)據(jù)給pc,同時(shí)更新CPSR
ldmfd sp!,{r0-r2,pc}^