新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM1176JZF-S/S3C6410處理器的異常處理過程

ARM1176JZF-S/S3C6410處理器的異常處理過程

作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
本來準(zhǔn)備總結(jié)一下ARM1176JZF-S/S3C6410處理器異常處理過程,但是發(fā)現(xiàn)《嵌入式系統(tǒng)Linux內(nèi)核開發(fā)實(shí)戰(zhàn)指南》一書中的這一部分講解得非常簡明和清楚。所以就不再重復(fù)發(fā)明輪子,不過我會(huì)在以下的引用中做一些補(bǔ)充。

進(jìn)入異常中斷處理

本文引用地址:http://m.butianyuan.cn/article/201611/317944.htm

ARM處理器發(fā)生異常中斷,則ARM處理器進(jìn)入如下異常中斷自動(dòng)處理過程(假設(shè)發(fā)生的異常中斷對(duì)應(yīng)的模式為mode):

  1. 將當(dāng)前程序狀態(tài)寄存器CPSR的值保存到SPSR_mode中;
  2. 將CPSR中的模式位設(shè)置成mode模式,將CPSR中的bit7(I)設(shè)置為1,禁止IRQ中斷,如果是FIQ中斷,則再將CPSR中的bit6(F)設(shè)置為1,禁止FIQ中斷;
  3. 將返回地址傳給lr_mode;
  4. 將該異常中斷的向量地址傳給程序計(jì)數(shù)器pc,從而進(jìn)入異常中斷處理程序。

退出異常中斷處理

當(dāng)要從異常中斷處理程序中返回時(shí),要做以下兩步操作(假設(shè)發(fā)生的異常中斷對(duì)應(yīng)的模式為mode):

  1. 將保存在SPSR_mode中的值恢復(fù)到當(dāng)前程序狀態(tài)寄存器CPSR中;
  2. 返回到發(fā)生異常中斷的指令的下一條指令處執(zhí)行,也就是將lr_mode寄存器的值適當(dāng)?shù)胤祷氐匠绦蛴?jì)數(shù)器pc中。

但程序員只需做好上述第二步即可,第一步在完成第二步的同時(shí)由處理器自動(dòng)完成,所以我們下面講解從各種異常中斷處理返回的編程接口。

退出復(fù)位異常中斷處理(Reset)

復(fù)位異常中斷處理程序不需要返回,所以不需要這個(gè)接口。

退出未定義指令異常中斷處理(Undefined Instruction)

未定義指令異常中斷由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)未定義指令異常中斷產(chǎn)生時(shí),程序計(jì)數(shù)器pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置),當(dāng)未定義指令異常中斷發(fā)生時(shí),處理器將值(pc-4)保存到lr_und中,此時(shí)(pc-4)指向當(dāng)前指令的下一條指令,所以從未定義指令異常中斷返回可以通過如下指令來實(shí)現(xiàn):

mov pc, lr

該指令將寄存器lr_mode中的值到程序計(jì)數(shù)器pc中,實(shí)現(xiàn)程序返回,同時(shí)將SPSR_mode寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。

如果要在異常中斷處理中使用數(shù)據(jù)棧,那么可以在進(jìn)入異常中斷處理程序時(shí)保存被中斷程序的執(zhí)行現(xiàn)場,在退出異常中斷處理程序時(shí)恢復(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)識(shí)符^表示要將SPSR_mode寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。

退出軟中斷指令(SWI)異常中斷處理(Undefined Instruction)

SWI異常中斷和未定義異常中斷指令一樣,也是由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)SWI指令執(zhí)行時(shí),pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置),當(dāng)未定義指令異常中斷發(fā)生時(shí),處理器將值(pc-4)保存到lr_svc中,此時(shí)(pc-4)指向當(dāng)前指令的下一條指令,所以從SWI異常中斷處理返回的實(shí)現(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ù)取時(shí),如果目標(biāo)地址是非法的,該指令被標(biāo)記成有問題的指令,這時(shí),流水線上該指令之前的指令繼續(xù)執(zhí)行,當(dāng)執(zhí)行到該被標(biāo)記成有問題的指令時(shí),處理器產(chǎn)生指令預(yù)取中止異常中斷。發(fā)生指令預(yù)取異常中斷時(shí),程序要返回到該有問題的指令處,重新讀取并執(zhí)行該指令,因此指令預(yù)取中止異常中斷應(yīng)該返回到產(chǎn)生該指令預(yù)取中止異常中斷的指令處,而不是當(dāng)前指令的下一條指令。

指令預(yù)取中止異常中斷由當(dāng)前執(zhí)行的指令自身產(chǎn)生,當(dāng)指令預(yù)取中止異常中斷發(fā)生時(shí),程序計(jì)數(shù)器pc的值還未更新,它指向當(dāng)前指令后面第2條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加8字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加4字節(jié)的位置)。此時(shí)處理器將值(pc-4)保存到lr_abt中,它指向當(dāng)前指令的下一條指令,所以返回操作可以通過下面指令實(shí)現(xiàn):

subs pc, lr, #4

該指令將lr中的值減4后傳給程序計(jì)數(shù)器pc中,實(shí)現(xiàn)程序返回,同時(shí)將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)識(shí)符^表示要將SPSR_abt寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。

退出數(shù)據(jù)訪問中止異常中斷處理(Data Abort)

發(fā)生數(shù)據(jù)訪問異常中斷時(shí),程序要返回到該有問題的指令處,重新訪問該數(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í),程序計(jì)數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置)。此時(shí)處理器將值(pc-4)保存到lr_abt中,它指向當(dāng)前指令后面第2條指令,所以返回操作可以通過下面指令實(shí)現(xiàn):

subs pc, lr, #8

該指令將lr中的值減8后傳給程序計(jì)數(shù)器pc中,實(shí)現(xiàn)程序返回,同時(shí)將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)識(shí)符^表示要將SPSR_abt寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。

退出IRQ異常中斷處理程序(IRQ)

通常處理器執(zhí)行完當(dāng)前指令后,查詢IRQ中斷引腳,并查看是否允許IRQ中斷,如果某個(gè)中斷引腳有效,并且系統(tǒng)允許該中斷產(chǎn)生,處理器將產(chǎn)生IRQ異常中斷,當(dāng)IRQ異常中斷產(chǎn)生時(shí),程序計(jì)數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置),當(dāng)IRQ異常中斷產(chǎn)生時(shí),處理器將值(pc-4)保存到IRQ異常模式下的寄存器lr_irq中,它指向當(dāng)前指令之后的第2條指令,因此返回操作可以通過下面指令實(shí)現(xiàn):

subs pc, lr, #4

該指令將lr中的值減4后傳給程序計(jì)數(shù)器pc中,實(shí)現(xiàn)程序返回,同時(shí)將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)識(shí)符^表示要將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í),程序計(jì)數(shù)器pc的值已經(jīng)更新,它指向當(dāng)前指令后面第3條指令(對(duì)于ARM指令,它指向當(dāng)前指令地址加12字節(jié)的位置;對(duì)于Thumb指令,它指向當(dāng)前指令地址加6字節(jié)的位置),當(dāng)FIQ異常中斷產(chǎn)生時(shí),處理器將值(pc-4)保存到IRQ異常模式下的寄存器lr_fiq中,它指向當(dāng)前指令之后的第2條指令,因此返回操作可以通過下面指令實(shí)現(xiàn):

subs pc, lr, #4

該指令將lr中的值減4后傳給程序計(jì)數(shù)器pc中,實(shí)現(xiàn)程序返回,同時(shí)將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)識(shí)符^表示要將SPSR_fiq寄存器中的值到當(dāng)前程序狀態(tài)寄存器CPSR中。

補(bǔ)充:關(guān)于程序返回地址(PC)的取值

上文中提到,在進(jìn)入異常處理之后,CPU會(huì)自動(dòng)根據(jù)pc的值來設(shè)置lr的值(一般是減4),而對(duì)于不同各類的異常來說,這個(gè)值還不能直接用做異常的返回地址,可能還需要再減4或減8等等,這樣做的原因是什么呢?

答案在于ARM處理器在處理指令時(shí)所使用的三級(jí)流水線機(jī)制。

CPU執(zhí)行一條指令的過程可以分為三個(gè)步驟:取指令、翻譯和執(zhí)行。執(zhí)行每一個(gè)步驟都需要一個(gè)指令周期的時(shí)間,所以完整地執(zhí)行完一條指令實(shí)際上就需要3個(gè)周期。為了加快程序的運(yùn)行,現(xiàn)代CPU都會(huì)采用多級(jí)流程線的技術(shù)。以三級(jí)流水線為例,一條專門負(fù)責(zé)取指,一條專門翻譯,還有一條負(fù)責(zé)執(zhí)行,三條流水線并行工作,每一條流水線在每一個(gè)周期內(nèi)都不會(huì)空閑,所以平均來看,執(zhí)行每條指令都只要一個(gè)周期的時(shí)間。

從二進(jìn)制指令的角度來看,當(dāng)前指令在執(zhí)行的時(shí)候,下一條指令已經(jīng)在被翻譯,再下一條指令已經(jīng)正在被讀取。注意pc寄存器總是指向正在被讀取的那條指令,而不是正在被執(zhí)行的指令。如下圖所示:

箭頭方向是指令運(yùn)行的方向,左側(cè)是低地址,右側(cè)是高地址,當(dāng)A指令是正在運(yùn)行的指令,pc寄存器現(xiàn)在正指向C指令的位置。

下面,分別以軟中斷異常和數(shù)據(jù)異常為例來解釋一下上文中所講的內(nèi)容:

在軟中斷發(fā)生時(shí),指令流水線的結(jié)構(gòu)與上圖完全一樣。軟中斷是由正在執(zhí)行的指令A(yù)觸發(fā)的,它的任務(wù)已經(jīng)完成,所以在中斷處理結(jié)束之后,A指令不需要再被執(zhí)行一次,應(yīng)該直接執(zhí)行B指令。而在進(jìn)入中斷處理程序之前,CPU已經(jīng)自動(dòng)將(pc-4)的值存入lr,這正是B指定的位置。所以在中斷返回時(shí),直接把lr的值賦給pc就行了。

在數(shù)據(jù)訪問異常發(fā)生時(shí),指令流水線的結(jié)構(gòu)與上圖不太一樣,正在執(zhí)行的指令仍然是A,pc已經(jīng)更新,即指向了D指令。在中斷處理結(jié)束時(shí),數(shù)據(jù)的問題已經(jīng)解決(可以訪問),A指令還需要再重新執(zhí)行一次,所以pc需要指向A指令處。而在進(jìn)入中斷處理程序之前,CPU已經(jīng)自動(dòng)將(pc-4)的值存入lr,這是C指令的位置,所以我們需要手動(dòng)調(diào)整pc的位置,把它再減8,這才是A指令的位置。



評(píng)論


技術(shù)專區(qū)

關(guān)閉