傳統(tǒng)ARM中可嵌套的IRQ程序
不過,ARM給了我們一種權(quán)利,那就是在中斷處理程序中可以手動打開IRQ,這樣在前一個IRQ響應(yīng)的過程中,就可以被后來的中斷所打斷。就給我們提供了一種用軟件解決中斷嵌套的途徑。
本文引用地址:http://m.butianyuan.cn/article/201611/317863.htm中斷的過程我們都十分清楚:保護(hù)現(xiàn)場à響應(yīng)中斷à恢復(fù)現(xiàn)場。ARM對于每一種異常都有相應(yīng)的堆棧寄存器,且會自動切換,互不影響。所以自然而然地,在嵌套中,我們可以用SP_irq來保護(hù)現(xiàn)場和恢復(fù)現(xiàn)場。流程如下所示:
1.2.3.4.5.6.7.7.17.27.37.47.57.67.77.87.97.10通過LR_irq跳轉(zhuǎn)回到7
7.11ARM自動從SPSR_irq恢復(fù)CPSR
8.9.10.11.
這樣就實現(xiàn)了嵌套,而且只要堆棧夠大,可以嵌套很多層。不考慮優(yōu)先級,或者把優(yōu)先級教給中斷控制器管理,這樣已經(jīng)不錯了吧,雖然不愿意這么說,但是問題還是來了。
在上面的流程中,有一步是根據(jù)中斷號進(jìn)行中斷服務(wù)。對于不同的中斷源,我們一般都會用不同函數(shù)來寫中斷服務(wù),這樣不僅清晰,也利于將不同功能的模塊分割開。這樣我們就需要將這步變?yōu)椋焊鶕?jù)中斷號進(jìn)入服務(wù)子程序。這步中,我們會牽涉到函數(shù)調(diào)用。在函數(shù)調(diào)用過程中,一般都會先將PC保存在LR_irq中,在返回時,再將LR_irq恢復(fù)到PC。這也正是LR的作用所在。
正是這個事實,導(dǎo)致了問題的發(fā)生。想象這種情況:當(dāng)我們進(jìn)入服務(wù)子程序后,此時LR_irq正是我們程序的返回地址。這時,第二個中斷到來了,回憶一下中斷發(fā)生時ARM自動做了什么,ARM將PC保存到了LR_irq中!就這樣,LR_irq被篡改了,因為我們無法預(yù)料到中斷什么時候到來,我們也就根本無法保存這個被篡改的LR_irq。程序響應(yīng)好第二個中斷后,一路返回到這個LR_irq,毫無意外的,就跑飛了。
很掃興吧,不過我們自然有辦法解決這個問題。辦法就是在進(jìn)入服務(wù)子程序之前,先將系統(tǒng)轉(zhuǎn)換到SVC狀態(tài),這樣,子程序被調(diào)用時返回地址就會被保存在LR_svc中,也就不會再被第二個中斷所篡改。流程如下,和第一次不同的地方都用紅色標(biāo)注。
1.2.3.4.5.6.7.8.9.9.19.29.39.49.59.69.79.89.99.10關(guān)閉IRQ使能位
9.11從SP_svc所指示的堆棧中恢復(fù)R0-R3,LR_svc
9.12更改系統(tǒng)狀態(tài)為IRQ
9.13從SP_irq堆棧中恢復(fù)通用寄存器、LR_irq、SPSR_irq
9.14通過LR_irq跳轉(zhuǎn)回到9
9.15ARM自動從SPSR_irq恢復(fù)CPSR
10.11.12.13.14.15.
這樣我們既可以用中斷服務(wù)子程序,也不怕LR被篡改了。我們再來看一下嵌套過程中的堆棧使用情況。在進(jìn)入SVC狀態(tài)之前,使用IRQ的堆棧,保存嵌套所需的通用寄存器、LR_irq和SPSR_irq。進(jìn)入SVC狀態(tài)之后,使用SVC堆棧,需要保存調(diào)用函數(shù)規(guī)定的R0-R3,LR_svc。當(dāng)然在中斷服務(wù)例程中,也是使用SVC堆棧。可見兩個狀態(tài)的堆棧都被使用了。當(dāng)然,因為中斷服務(wù)例程使用SVC堆棧,我們也可以考慮將嵌套所需的堆棧也放到SVC中,這樣就不需要IRQ堆棧了。流程上和前面這種方法很相似,只不過要將保存LR_irq和SPSR_irq的時間放到進(jìn)入SVC態(tài)之后,方法可以是通過通用寄存器拷貝。最后也不必再返回IRQ態(tài),可以直接通過SPSR_svc和LR_svc來推出中斷處理程序。
程序貼在下面,用的是堆棧分開的方法,只是示例。
- __asmvoidIRQ_Handler(void){
- PRESERVE8
- IMPORThandler1
- //STORELR_irq&SPSR_irq
- SUBLR,LR,#4
- MRSR0,SPSR
- STMFDSP!,{R0,LR}
- //INTOSVCMODE
- MRSR0,CPSR
- BICR0,#0x1f
- ORRR0,#0x13
- MSRCPSR_C,R0
- //STOREREGISTORSOFSVCMODE
- STMFDSP!,{R0-R3,LR}
- //ENABLEIRQ
- MRSR0,CPSR
- BICR0,#0x80
- MSRCPSR_C,R0
- //GOTOHANDLER
- BLhandler1
- //RESTOREREGISTORSOFSVCMODE
- LDMFDSP!,{R0-R3,LR}
- //DISABLEIRQ
- MRSR0,CPSR
- ORRR0,#0x80
- MSRCPSR_C,R0
- //INTOIRQMODE
- MRSR0,CPSR
- BICR0,#0x1f
- ORRR0,#0x12
- MSRCPSR_C,R0
- //RESTORELR_irq&SPSR_irq
- LDMFDSP!,{R0,LR}
- MSRSPSR_CFX,R0
- //EXITIRQ
- MOVSPC,LR
- }
評論