文章是轉載的,,看的不是很明白,寫的也不是很明白,。當然很重要的是這是一種思路,,證明通過模式的切換,,可嵌套中斷是可以實現(xiàn)的。
傳統(tǒng)ARM中IRQ是作為一種系統(tǒng)異常出現(xiàn)的,。對于ARM核來說,,有且僅有一個稱為IRQ的系統(tǒng)異常。而ARM對于IRQ的處理一般通過異常向量找到IRQ的中斷處理程序,。當進入IRQ中斷處理程序之后,,ARM自動屏蔽IRQ,也就是說在中斷響應過程中是忽略之后到來的中斷請求的,。即使使用了VIC,,VIC也僅僅是懸起后來的中斷請求。也就是說,,傳統(tǒng)ARM的中斷是不可嵌套,、不可搶占的。 不過,,ARM給了我們一種權利,,那就是在中斷處理程序中可以手動打開IRQ,這樣在前一個IRQ響應的過程中,,就可以被后來的中斷所打斷,。就給我們提供了一種用軟件解決中斷嵌套的途徑。 中斷的過程我們都十分清楚:保護現(xiàn)場à響應中斷à恢復現(xiàn)場,。ARM對于每一種異常都有相應的堆棧寄存器,,且會自動切換,互不影響,。所以自然而然地,,在嵌套中,我們可以用SP_irq來保護現(xiàn)場和恢復現(xiàn)場,。流程如下所示:
1. 第一次進入中斷 2. ARM自動保存CPSR到SPSR_irq 3. ARM自動保存PC到LR_irq 4. ARM自動關閉IRQ使能 5. 保存通用寄存器,、LR_irq、SPSR_irq到SP_irq所指示的堆棧中 6. 打開IRQ使能位 7. 根據(jù)中斷號進行中斷服務 7.1 第二次進入中斷,,開始嵌套 7.2 ARM自動保存CPSR到SPS_irq 7.3 ARM自動保存PC到LR_irq 7.4 ARM自動關閉IRQ使能 7.5 保存通用寄存器,、LR_irq、SPSR_irq到SP_irq所指示的堆棧中 7.6 打開IRQ使能位 7.7 根據(jù)中斷號進行中斷服務 7.8 關閉IRQ使能位 7.9 從SP_irq堆棧中恢復通用寄存器,、LR_irq、SPSR_irq 7.10通過LR_irq跳轉回到7 7.11ARM自動從SPSR_irq恢復CPSR 8. 關閉IRQ使能位 9. 從SP_irq堆棧中恢復通用寄存器,、LR_irq,、SPSR_irq 10. 通過LR_irq跳轉回到到任務程序 11. ARM自動從SPSR_irq恢復CPSR
這樣就實現(xiàn)了嵌套,而且只要堆棧夠大,,可以嵌套很多層,。不考慮優(yōu)先級,或者把優(yōu)先級教給中斷控制器管理,,這樣已經不錯了吧,雖然不愿意這么說,,但是問題還是來了。 在上面的流程中,,有一步是根據(jù)中斷號進行中斷服務,。對于不同的中斷源,我們一般都會用不同函數(shù)來寫中斷服務,,這樣不僅清晰,,也利于將不同功能的模塊分割開。這樣我們就需要將這步變?yōu)椋焊鶕?jù)中斷號進入服務子程序,。這步中,,我們會牽涉到函數(shù)調用。在函數(shù)調用過程中,,一般都會先將PC保存在LR_irq中,在返回時,,再將LR_irq恢復到PC,。這也正是LR的作用所在。 正是這個事實,,導致了問題的發(fā)生,。想象這種情況:當我們進入服務子程序后,此時LR_irq正是我們程序的返回地址,。這時,,第二個中斷到來了,回憶一下中斷發(fā)生時ARM自動做了什么,,ARM將PC保存到了LR_irq中,!就這樣,LR_irq被篡改了,,因為我們無法預料到中斷什么時候到來,,我們也就根本無法保存這個被篡改的LR_irq。程序響應好第二個中斷后,,一路返回到這個LR_irq,,毫無意外的,,就跑飛了。 很掃興吧,,不過我們自然有辦法解決這個問題,。辦法就是在進入服務子程序之前,先將系統(tǒng)轉換到SVC狀態(tài),,這樣,,子程序被調用時返回地址就會被保存在LR_svc中,也就不會再被第二個中斷所篡改,。流程如下,,和第一次不同的地方都用紅色標注。 為什么要保存r0-r3, 保存lr_svc不就夠了嗎,? 1. 第一次進入中斷 2. ARM自動保存CPSR到SPSR_irq 3. ARM自動保存PC到LR_irq 4. ARM自動關閉IRQ使能 5. 保存通用寄存器,、LR_irq、SPSR_irq到SP_irq所指示的堆棧中 6. 更改系統(tǒng)狀態(tài)為SVC 7. 保存R0-R3,,LR_svc到SP_svc所指示的堆棧中 8. 打開IRQ使能位 9. 根據(jù)中斷號進入中斷服務子程序 9.1 第二次進入中斷,,開始嵌套 9.2 ARM自動保存CPSR到SPS_irq 9.3 ARM自動保存PC到LR_irq 9.4 ARM自動關閉IRQ使能 9.5 保存通用寄存器、LR_irq,、SPSR_irq到SP_irq所指示的堆棧中 9.6 更改系統(tǒng)狀態(tài)為SVC 9.7 保存R0-R3,,LR_svc到SP_svc所指示的堆棧中 9.8 打開IRQ使能位 9.9 根據(jù)中斷號進入中斷服務子程序 9.10關閉IRQ使能位 9.11從SP_svc所指示的堆棧中恢復R0-R3,LR_svc 9.12更改系統(tǒng)狀態(tài)為IRQ 9.13從SP_irq堆棧中恢復通用寄存器,、LR_irq,、SPSR_irq 9.14通過LR_irq跳轉回到9 9.15ARM自動從SPSR_irq恢復CPSR 10. 關閉IRQ使能位 11. 從SP_svc所指示的堆棧中恢復R0-R3,LR_svc 12. 更改系統(tǒng)狀態(tài)為IRQ 13. 從SP_irq堆棧中恢復通用寄存器,、LR_irq,、SPSR_irq 14. 通過LR_irq跳轉回到到任務程序 15. ARM自動從SPSR_irq恢復CPSR
這樣我們既可以用中斷服務子程序,也不怕LR被篡改了,。我們再來看一下嵌套過程中的堆棧使用情況,。在進入SVC狀態(tài)之前,使用IRQ的堆棧,,保存嵌套所需的通用寄存器,、LR_irq和SPSR_irq。進入SVC狀態(tài)之后,,使用SVC堆棧,,需要保存調用函數(shù)規(guī)定的R0-R3,LR_svc,。當然在中斷服務例程中,,也是使用SVC堆棧。可見兩個狀態(tài)的堆棧都被使用了,。當然,,因為中斷服務例程使用SVC堆棧,我們也可以考慮將嵌套所需的堆棧也放到SVC中,,這樣就不需要IRQ堆棧了,。流程上和前面這種方法很相似,只不過要將保存LR_irq和SPSR_irq的時間放到進入SVC態(tài)之后,,方法可以是通過通用寄存器拷貝,。最后也不必再返回IRQ態(tài),,可以直接通過SPSR_svc和LR_svc來推出中斷處理程序,。 程序貼在下面,用的是堆棧分開的方法,,只是示例,。
[cpp] view
plaincopy
|
|