ARM1176JZF-S/S3C6410處理器的異常處理過程
進(jìn)入異常中斷處理
本文引用地址:http://butianyuan.cn/article/201611/317944.htmARM處理器發(fā)生異常中斷,則ARM處理器進(jìn)入如下異常中斷自動處理過程(假設(shè)發(fā)生的異常中斷對應(yīng)的模式為mode):
- 將當(dāng)前程序狀態(tài)寄存器CPSR的值保存到SPSR_mode中;
- 將CPSR中的模式位設(shè)置成mode模式,將CPSR中的bit7(I)設(shè)置為1,禁止IRQ中斷,如果是FIQ中斷,則再將CPSR中的bit6(F)設(shè)置為1,禁止FIQ中斷;
- 將返回地址傳給lr_mode;
- 將該異常中斷的向量地址傳給程序計數(shù)器pc,從而進(jìn)入異常中斷處理程序。
退出異常中斷處理
當(dāng)要從異常中斷處理程序中返回時,要做以下兩步操作(假設(shè)發(fā)生的異常中斷對應(yīng)的模式為mode):
- 將保存在SPSR_mode中的值恢復(fù)到當(dāng)前程序狀態(tài)寄存器CPSR中;
- 返回到發(fā)生異常中斷的指令的下一條指令處執(zhí)行,也就是將lr_mode寄存器的值適當(dāng)?shù)胤祷氐匠绦蛴嫈?shù)器pc中。
但程序員只需做好上述第二步即可,第一步在完成第二步的同時由處理器自動完成,所以我們下面講解從各種異常中斷處理返回的編程接口。
退出復(fù)位異常中斷處理(Reset)
復(fù)位異常中斷處理程序不需要返回,所以不需要這個接口。
退出未定義指令異常中斷處理(Undefined Instruction)
未定義指令異常中斷由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)未定義指令異常中斷產(chǎn)生時,程序計數(shù)器pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置),當(dāng)未定義指令異常中斷發(fā)生時,處理器將值(pc-4)保存到lr_und中,此時(pc-4)指向當(dāng)前指令的下一條指令,所以從未定義指令異常中斷返回可以通過如下指令來實現(xiàn):
mov pc, lr
該指令將寄存器lr_mode中的值到程序計數(shù)器pc中,實現(xiàn)程序返回,同時將SPSR_mode寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
如果要在異常中斷處理中使用數(shù)據(jù)棧,那么可以在進(jìn)入異常中斷處理程序時保存被中斷程序的執(zhí)行現(xiàn)場,在退出異常中斷處理程序時恢復(fù)被中斷程序的執(zhí)行現(xiàn)場,編程如下:
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場
上面的register_list,是異常中斷處理程序中使用的寄存器列表,標(biāo)識符^表示要將SPSR_mode寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
退出軟中斷指令(SWI)異常中斷處理(Undefined Instruction)
SWI異常中斷和未定義異常中斷指令一樣,也是由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)SWI指令執(zhí)行時,pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置),當(dāng)未定義指令異常中斷發(fā)生時,處理器將值(pc-4)保存到lr_svc中,此時(pc-4)指向當(dāng)前指令的下一條指令,所以從SWI異常中斷處理返回的實現(xiàn)方法與從未定義指令異常中斷處理返回一樣:
mov pc, lr
使用數(shù)據(jù)棧的方法與未定義指令異常中斷處理中的方法也一樣:
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場
退出指令預(yù)取中止異常中斷處理(Prefetch Abort)
在指令預(yù)取時,如果目標(biāo)地址是非法的,該指令被標(biāo)記成有問題的指令,這時,流水線上該指令之前的指令繼續(xù)執(zhí)行,當(dāng)執(zhí)行到該被標(biāo)記成有問題的指令時,處理器產(chǎn)生指令預(yù)取中止異常中斷。發(fā)生指令預(yù)取異常中斷時,程序要返回到該有問題的指令處,重新讀取并執(zhí)行該指令,因此指令預(yù)取中止異常中斷應(yīng)該返回到產(chǎn)生該指令預(yù)取中止異常中斷的指令處,而不是當(dāng)前指令的下一條指令。
指令預(yù)取中止異常中斷由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)指令預(yù)取中止異常中斷發(fā)生時,程序計數(shù)器pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置)。此時處理器將值(pc-4)保存到lr_abt中,它指向當(dāng)前指令的下一條指令,所以返回操作可以通過下面指令實現(xiàn):
subs pc, lr, #4
該指令將lr中的值減4后傳給程序計數(shù)器pc中,實現(xiàn)程序返回,同時將SPSR_abt寄存器的內(nèi)容到當(dāng)前程序狀態(tài)寄存器CPSR中。
如果要在指令預(yù)取中止異常中斷處理中使用數(shù)據(jù)棧,可以用以下方法保護(hù)、恢復(fù)被中斷程序的執(zhí)行現(xiàn)場:
subs lr, lr, #4
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場
上面的register_list是異常中斷處理程序中使用的寄存器列表,標(biāo)識符^表示要將SPSR_abt寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
退出數(shù)據(jù)訪問中止異常中斷處理(Data Abort)
發(fā)生數(shù)據(jù)訪問異常中斷時,程序要返回到該有問題的指令處,重新訪問該數(shù)據(jù),因此數(shù)據(jù)訪問異常中斷應(yīng)該返回到產(chǎn)生該數(shù)據(jù)訪問中止異常中斷的指令處,而不是當(dāng)前指令的下一條指令。
數(shù)據(jù)訪問異常中斷由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)數(shù)據(jù)訪問異常中斷發(fā)生時,程序計數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置)。此時處理器將值(pc-4)保存到lr_abt中,它指向當(dāng)前指令后面第2條指令,所以返回操作可以通過下面指令實現(xiàn):
subs pc, lr, #8
該指令將lr中的值減8后傳給程序計數(shù)器pc中,實現(xiàn)程序返回,同時將SPSR_abt寄存器內(nèi)容到當(dāng)前程序狀態(tài)寄存器CPSR中;
如果要在數(shù)據(jù)訪問異常中斷處理中使用數(shù)據(jù)棧,可以用以下方法保護(hù)、恢復(fù)被中斷程序的執(zhí)行現(xiàn)場:
subs lr, lr, #8
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場;
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場;
上面的register_list是異常中斷處理程序中使用的寄存器列表,標(biāo)識符^表示要將SPSR_abt寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
退出IRQ異常中斷處理程序(IRQ)
通常處理器執(zhí)行完當(dāng)前指令后,查詢IRQ中斷引腳,并查看是否允許IRQ中斷,如果某個中斷引腳有效,并且系統(tǒng)允許該中斷產(chǎn)生,處理器將產(chǎn)生IRQ異常中斷,當(dāng)IRQ異常中斷產(chǎn)生時,程序計數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置),當(dāng)IRQ異常中斷產(chǎn)生時,處理器將值(pc-4)保存到IRQ異常模式下的寄存器lr_irq中,它指向當(dāng)前指令之后的第2條指令,因此返回操作可以通過下面指令實現(xiàn):
subs pc, lr, #4
該指令將lr中的值減4后傳給程序計數(shù)器pc中,實現(xiàn)程序返回,同時將SPSR_irq寄存器的內(nèi)容到當(dāng)前程序狀態(tài)寄存器CPSR中。
如果要在IRQ異常中斷處理中使用數(shù)據(jù)棧,可以用以下方法保護(hù)、恢復(fù)被中斷程序的執(zhí)行現(xiàn)場:
subs lr, lr, #4
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場
上面的register_list是異常中斷處理程序中使用的寄存器列表,標(biāo)識符^表示要將SPSR_irq寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
退出FIQ異常中斷處理程序(FIQ)
與IRQ異常中斷一樣,處理器執(zhí)行完當(dāng)前指令后,查詢FIQ中斷引腳,并查看是否允許FIQ中斷,如果中斷引腳有效,并且系統(tǒng)允許該中斷產(chǎn)生,處理器將產(chǎn)生FIQ異常中斷,當(dāng)FIQ異常中斷產(chǎn)生時,程序計數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置),當(dāng)FIQ異常中斷產(chǎn)生時,處理器將值(pc-4)保存到IRQ異常模式下的寄存器lr_fiq中,它指向當(dāng)前指令之后的第2條指令,因此返回操作可以通過下面指令實現(xiàn):
subs pc, lr, #4
該指令將lr中的值減4后傳給程序計數(shù)器pc中,實現(xiàn)程序返回,同時將SPSR_fiq寄存器的內(nèi)容到當(dāng)前程序狀態(tài)寄存器CPSR中。
如果要在FIQ異常中斷處理中使用數(shù)據(jù)棧,可以用以下方法保護(hù)、恢復(fù)被中斷程序的執(zhí)行現(xiàn)場:
subs lr, lr, #4
stmfd sp!, {register_list, lr} ;保存被中斷程序的執(zhí)行現(xiàn)場
; . . .
ldmfd sp!, {register_list, pc}^ ;恢復(fù)被中斷程序的執(zhí)行現(xiàn)場
上面的register_list是異常中斷處理程序中使用的寄存器列表,標(biāo)識符^表示要將SPSR_fiq寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。
補充:關(guān)于程序返回地址(PC)的取值
上文中提到,在進(jìn)入異常處理之后,CPU會自動根據(jù)pc的值來設(shè)置lr的值(一般是減4),而對于不同各類的異常來說,這個值還不能直接用做異常的返回地址,可能還需要再減4或減8等等,這樣做的原因是什么呢?
答案在于ARM處理器在處理指令時所使用的三級流水線機制。
CPU執(zhí)行一條指令的過程可以分為三個步驟:取指令、翻譯和執(zhí)行。執(zhí)行每一個步驟都需要一個指令周期的時間,所以完整地執(zhí)行完一條指令實際上就需要3個周期。為了加快程序的運行,現(xiàn)代CPU都會采用多級流程線的技術(shù)。以三級流水線為例,一條專門負(fù)責(zé)取指,一條專門翻譯,還有一條負(fù)責(zé)執(zhí)行,三條流水線并行工作,每一條流水線在每一個周期內(nèi)都不會空閑,所以平均來看,執(zhí)行每條指令都只要一個周期的時間。
從二進(jìn)制指令的角度來看,當(dāng)前指令在執(zhí)行的時候,下一條指令已經(jīng)在被翻譯,再下一條指令已經(jīng)正在被讀取。注意pc寄存器總是指向正在被讀取的那條指令,而不是正在被執(zhí)行的指令。如下圖所示:
箭頭方向是指令運行的方向,左側(cè)是低地址,右側(cè)是高地址,當(dāng)A指令是正在運行的指令,pc寄存器現(xiàn)在正指向C指令的位置。
下面,分別以軟中斷異常和數(shù)據(jù)異常為例來解釋一下上文中所講的內(nèi)容:
在軟中斷發(fā)生時,指令流水線的結(jié)構(gòu)與上圖完全一樣。軟中斷是由正在執(zhí)行的指令A(yù)觸發(fā)的,它的任務(wù)已經(jīng)完成,所以在中斷處理結(jié)束之后,A指令不需要再被執(zhí)行一次,應(yīng)該直接執(zhí)行B指令。而在進(jìn)入中斷處理程序之前,CPU已經(jīng)自動將(pc-4)的值存入lr,這正是B指定的位置。所以在中斷返回時,直接把lr的值賦給pc就行了。
在數(shù)據(jù)訪問異常發(fā)生時,指令流水線的結(jié)構(gòu)與上圖不太一樣,正在執(zhí)行的指令仍然是A,pc已經(jīng)更新,即指向了D指令。在中斷處理結(jié)束時,數(shù)據(jù)的問題已經(jīng)解決(可以訪問),A指令還需要再重新執(zhí)行一次,所以pc需要指向A指令處。而在進(jìn)入中斷處理程序之前,CPU已經(jīng)自動將(pc-4)的值存入lr,這是C指令的位置,所以我們需要手動調(diào)整pc的位置,把它再減8,這才是A指令的位置。
評論