uCOS-II的中斷-ARM7實(shí)現(xiàn)中斷嵌套的方法探究【@.1 中斷嵌套與CPU支持】 在uCOS-II,或者是任何一個(gè)可剝奪型OS系統(tǒng)中,中斷嵌套是一個(gè)必須要解決的問題。從結(jié)論上來說,并不是所有的CPU都支持中斷嵌套的,即便是ARM系列內(nèi)核。對(duì)于ARM7系列,例如LPC2xxx系列芯片,硬件上是不支持中斷嵌套的,而對(duì)于新的CortexM3系列,中斷嵌套是可配置的,但是中斷嵌套時(shí)保存現(xiàn)場的操作并不完整,并沒有把R0~R15所有寄存器都保存到堆棧中,而是只保存R0~R4。這就需要我們手動(dòng)軟件實(shí)現(xiàn)全部或部分的中斷現(xiàn)場保護(hù)機(jī)制。不過首先通過分析ARM7系列芯片來看看中斷嵌套的硬件要求。 ARM7系列的內(nèi)核一共有7中模式,如下圖所示:
其中 (1) User Mode:用戶模式。唯一非特權(quán)模式,可執(zhí)行指令受限。(讀寫CPSR禁止) (4) FIQ Mode:快速中斷模式??纱驍郔RQ模式 (5) Supervisor Mode:監(jiān)視模式。軟中斷(SWI)處理函數(shù)在這種模式下執(zhí)行。 每種模式都有自己的一套R(shí)0~R14,以及CPSR,這樣能保證跳轉(zhuǎn)其他模式時(shí)能直接保存現(xiàn)場。而如果我們要手動(dòng)保存現(xiàn)場時(shí)還必須將這些寄存器壓入到堆棧中,這樣就用C或匯編寫一些比較底層的代碼了。當(dāng)進(jìn)入中斷或異常時(shí)會(huì)自動(dòng)進(jìn)入響應(yīng)的模式進(jìn)行處理。而這些都是由程序狀態(tài)寄存器CPSR保存的。每個(gè)CPU內(nèi)部的狀態(tài)寄存器可能結(jié)構(gòu)不同,但是對(duì)于ARM來說大體的結(jié)構(gòu)差不多。ARM7中的CPSR的結(jié)構(gòu)如下:
其中CPSR的末尾幾位即保存了當(dāng)前的CPU模式。 當(dāng)每種異常發(fā)生,硬件上會(huì)做以下操作: 1.CPSR的Mode位置位成當(dāng)前模式,控制位置位 2.保存打斷前模式的R0~R14到對(duì)應(yīng)寄存器,CPSR到SPSR。 3.新模式的PC跳轉(zhuǎn)到內(nèi)存指定地址,即異常入口地址。
這就需要我們在啟動(dòng)代碼的對(duì)應(yīng)地址段協(xié)商地址偏移的標(biāo)簽。下面對(duì)幾個(gè)常用的異常進(jìn)行介紹: 一.Reset。發(fā)生在首次上電或者手動(dòng)按下Reset鍵(CPU的Reset引腳給個(gè)低電平):
上電或Reset之后一般會(huì)進(jìn)入固化在flash中的bootloader,bootloader的工作流程比較復(fù)雜,大致上需要判斷一些引腳電平來進(jìn)入相應(yīng)的模式。比如若判斷到ISP引腳(P0.14 in LPC2119)低電平,會(huì)進(jìn)入ISP模式燒寫程序。判斷Boot0和Boot1的電平組合可以進(jìn)入對(duì)應(yīng)的外部flash的啟動(dòng)。正常情況下會(huì)進(jìn)入flash中的0x00000000地址的Reset入口。按照啟動(dòng)代碼的編寫進(jìn)行一些必要的寄存器操作,以及初始化各個(gè)模式的堆棧,最后進(jìn)入到main函數(shù)。 二.Irq中斷。一般中斷可以注冊成irq或者fiq中斷。若注冊為irq中斷時(shí)的流程如下:
1.CPSR模式位置為IRQ模式 2.將當(dāng)前的PC存入到irq的R14中,也就是程序的返回地址,當(dāng)退出irq時(shí)會(huì)從這里取出地址給PC,程序回到原處。 3.CPSR中irq使能位清零,禁止irq中斷響應(yīng)??梢钥吹竭@就是不能中斷嵌套的一個(gè)重要原因,因?yàn)橛布蠒?huì)禁止irq的響應(yīng),除非你手動(dòng)將其打開。 4.程序跳轉(zhuǎn)到irq的入口地址,按照啟動(dòng)代碼執(zhí)行操作。比如Keil中會(huì)跳轉(zhuǎn)到VicVectAddr地址,即向量中斷的入口地址,之后交給向量中斷控制器來進(jìn)行操作。 三.FIQ
Fiq異?;靖鷌rq異常的流程一致,除了僅僅會(huì)將CPSR中的irq和fiq使能位都清零,即不能再響應(yīng)irq和fiq中斷了,并且跳入到fiq的入口地址。當(dāng)然按照Keil中的啟動(dòng)代碼這里是一個(gè)死循環(huán),即若不修改啟動(dòng)代碼是不能使用fiq中斷的。不過對(duì)于fiq這種快速中斷機(jī)制,若我們能手動(dòng)實(shí)現(xiàn)中斷嵌套完全可以不用fiq中斷,全部利用我們自己的代碼來實(shí)現(xiàn)。比如uCOS-II中,所有中斷都是irq中斷,不需要fiq中斷。 四.SWI(軟件中斷)
軟件中斷不同于其他的中斷或異常,這是需要在程序中自己編程調(diào)用軟件中斷命令來觸發(fā)的。調(diào)用后會(huì)將CPSR置為SVC(supervisor)管理員模式,存入PC到R14_swi,禁止irq和fiq中斷,并且跳轉(zhuǎn)到swi異常入口。這種方式的特點(diǎn)就是進(jìn)入了一個(gè)沒有權(quán)限限制的SVC模式,這樣就可以進(jìn)行對(duì)CPSR的讀寫操作了。這種方式帶給我們很多遐想,我們可以用軟件中斷進(jìn)行模式切換,進(jìn)而進(jìn)行寄存器的保存操作即保存中斷現(xiàn)場,從而可以實(shí)現(xiàn)中斷嵌套。 【@.2 中斷嵌套的方法探究】 中斷嵌套的核心思想是,保存當(dāng)前CPU寄存器到堆棧中,即保存中斷現(xiàn)場,并且打開中斷使能位允許中斷再次響應(yīng)。但是由于CPU不同模式的權(quán)限區(qū)分導(dǎo)致這一想法不能簡單的實(shí)現(xiàn)。比如,很多啟動(dòng)代碼中在進(jìn)入CPU前會(huì)進(jìn)入沒有權(quán)限的User模式,但是User模式是沒辦法對(duì)CPSR進(jìn)行讀寫操作的,也就是說沒辦法直接在User模式中將CPSR壓入到堆棧,同時(shí)不能通過向CPSR的模式位寫入模式代碼切換到其他的模式。
僅僅考慮irq中斷,我們來回憶一下整個(gè)中斷響應(yīng)的流程。首先中斷有輸入,CPSR模式置位為irq模式,將當(dāng)前PC值保存到irq模式的R14中,并且清除CPSR的irq標(biāo)志位使得不能再次響應(yīng)irq中斷,最后跳轉(zhuǎn)到irq中斷入口地址。進(jìn)過啟動(dòng)代碼和我們的程序配置,最終會(huì)進(jìn)入到我們編寫中斷服務(wù)程序中,當(dāng)這個(gè)程序結(jié)束時(shí)會(huì)讀取之前保存在R14_irq中的地址值給PC,程序回到原來中斷處繼續(xù)運(yùn)行。在此基礎(chǔ)上我們考慮集中方法實(shí)現(xiàn)中斷嵌套。(以下假設(shè)主程序在User mode下運(yùn)行)
方法一: 中斷產(chǎn)生時(shí)會(huì)進(jìn)入irq模式,所以當(dāng)然有權(quán)限操作CPSR,因此如果我們僅僅清除CPSR的中斷使能位使之能夠在此響應(yīng)中斷的話看看有什么結(jié)果。圖中進(jìn)入中斷的紅色部分就是我們手動(dòng)添加的代碼。當(dāng)IRQ1響應(yīng)時(shí),我們進(jìn)入中斷服務(wù)程序之前手動(dòng)打開CPSR的irq中斷使能位,那么如果當(dāng)運(yùn)行到一定時(shí)間新的中斷IRQ2響應(yīng)時(shí),也會(huì)做同樣的操作。而其中將PC存入到R14_irq這一步硬件幫我們做的操作就會(huì)出問題。因?yàn)檫@兩個(gè)中斷都是進(jìn)入了irq模式,所以他們的R14_irq是同一個(gè)寄存器,中斷2響應(yīng)時(shí)會(huì)將原本中斷1響應(yīng)時(shí)保存在R14中的返回地址值修改,變成中斷2響應(yīng)之前的地址值,而這個(gè)值正是中斷1的服務(wù)程序運(yùn)行到被中斷2打斷的地方。當(dāng)中斷2結(jié)束返回時(shí)讀取R14_irq的值,返回到中斷1的被打斷出,中斷1繼續(xù)運(yùn)行,當(dāng)返回時(shí)又讀取R14_irq的值,這時(shí)候的值卻是被中斷2打斷時(shí)的地址值,所以中斷1會(huì)進(jìn)入一個(gè)死循環(huán)。 所以,僅僅打開中斷使能位而不進(jìn)行現(xiàn)場保護(hù)操作是絕對(duì)不行的。
方法二: 在中斷操作進(jìn)入中斷服務(wù)之前手動(dòng)打開CPSR中斷使能位,保存所有寄存器到堆棧,中斷返回之前手動(dòng)恢復(fù)堆棧中保存的寄存器。但是這種方法實(shí)際上并不可行,如果僅僅有IRQ1時(shí)是OK的,但是若有IRQ2,打斷IRQ1,當(dāng)IRQ2返回時(shí)此時(shí)硬件已經(jīng)進(jìn)入U(xiǎn)ser Mode,而CPSR中保存的是之前IRQ1時(shí)的CPSR,其中mode位是irq。這就產(chǎn)生了沖突,能不能繼續(xù)運(yùn)行下都是個(gè)問題。其實(shí)可以這樣理解,當(dāng)IRQ2返回時(shí)已經(jīng)進(jìn)入U(xiǎn)ser Mode模式,而若想返回到剛才的IRQ1,相當(dāng)于要從User Mode跳到Irq模式執(zhí)行IRQ1的剩余部分,而User Mode是無法自發(fā)跳轉(zhuǎn)到其他模式的,除非調(diào)用軟件中斷,這其實(shí)就涉及到下面的方法:
方法三: 每次中斷返回時(shí)調(diào)用軟件中斷指令,進(jìn)入swi軟件中斷模式,這時(shí)其實(shí)已經(jīng)進(jìn)入SVC模式,所以有權(quán)限對(duì)CPSR進(jìn)行操作,所以這種方法可以實(shí)現(xiàn)中斷嵌套,而且實(shí)際上這也是很多OS在特定CPU上實(shí)現(xiàn)中斷現(xiàn)場保護(hù)的方法。不過回過頭想,所有這一切其實(shí)都是因?yàn)閁ser Mode沒有權(quán)限對(duì)CPSR進(jìn)行操作所造成的,那么我們換一種思維,直接讓主程序運(yùn)行在有權(quán)限操作CPSR的SVC管理員模式不就好了嗎?
方法四: 可以通過修改啟動(dòng)代碼,放棄對(duì)User模式的使用,使進(jìn)入main函數(shù)之前處于SVC模式,這樣一來整個(gè)程序從頭到尾都是處于SVC模式,具有全部權(quán)限進(jìn)行寄存器的操作,這樣一來,在中斷返回時(shí)也不用像方法三一樣,特意進(jìn)入swi的異常入口進(jìn)入SVC模式,并且需要保存進(jìn)入SVC模式的現(xiàn)場。方法四中斷返回時(shí)直接就是主程序運(yùn)行的SVC模式,不用新進(jìn)入另一個(gè)模式進(jìn)行恢復(fù)現(xiàn)場操作,這樣中斷恢復(fù)時(shí)的時(shí)間也會(huì)快一些。實(shí)際上,這也是uCOS-II在ARM7系列上移植的推薦模式,很多移植的實(shí)例也是采用全程SVC模式的方式進(jìn)行中斷嵌套操作的。當(dāng)然你也可以自己寫程序用方法三的軟件中斷進(jìn)行現(xiàn)場保護(hù),是可以實(shí)現(xiàn)的,只是機(jī)器周期上需要多幾步軟件中斷本身的模式切換操作。 【@.3 中斷嵌套對(duì)于CPU硬件的反思】 最后我們可以分析,之所以ARM7要花這么大代價(jià)實(shí)現(xiàn)中斷嵌套,完全是因?yàn)镃PU內(nèi)核的模式與特權(quán)造成的。所以如果CPU的內(nèi)核沒有那么多的限制,或者說CPU內(nèi)核本身就支持簡單的中斷嵌套,我們的工作也就會(huì)好做很多了。我們可以看看Cortex M3系列的STM32F10x內(nèi)核,模式和特權(quán)上就簡單很多了:
Cortex的中斷機(jī)制也跟ARM7截然不同。
可以看到這種內(nèi)核模式與中斷機(jī)制上簡化了許多,很適合與OS在其上面的移植。 @.[FIN] @.date->Mar 27, 2013 @.author->apollius |
|