當前位置:首頁 > 嵌入式 > 嵌入式教程
[導讀]異?;蛑袛嗍怯脩舫绦蛑凶罨镜囊环N執(zhí)行流程和形態(tài)。這部分主要對ARM架構(gòu)下的異常中斷做詳細說明。

3.4 異常中斷處理

異?;蛑袛嗍怯脩舫绦蛑凶罨镜囊环N執(zhí)行流程和形態(tài)。這部分主要對ARM架構(gòu)下的異常中斷做詳細說明。

ARM有7種類型的異常,按優(yōu)先級從高到低的排列如下:復位異常(Reset)、數(shù)據(jù)異常(Data Abort)、快速中斷異常(FIQ)、外部中斷異常(IRQ)、預取異常(Prefetch Abort)、軟件中斷(SWI)和未定義指令異常(Undefined instruction)。

注意

在ARM文檔中,使用術(shù)語Exception來描述異常。Exception主要是從處理器被動接受異常的角度出發(fā),而Interrupt帶有向處理器主動申請的色彩。在本書中,對“異常”和“中斷”不做嚴格區(qū)分,兩者都是指請求處理器打斷正常的程序執(zhí)行流程,進入特定程序循環(huán)的一種機制。

3.4.1 異常種類

ARM體系結(jié)構(gòu)中,存在7種異常處理。當異常發(fā)生時,處理器會把PC設(shè)置為一個特定的存儲器地址。這一地址放在被稱為向量表(vector table)的特定地址范圍內(nèi)。向量表的入口是一些跳轉(zhuǎn)指令,跳轉(zhuǎn)到專門處理某個異?;蛑袛嗟淖映绦颉?/p>

存儲器映射地址0x00000000是為向量表(一組32位字)保留的。在有些處理器中,向量表可以選擇定位在存儲空間的高地址(從偏移量0xffff0000開始)。一些嵌入式操作系統(tǒng),如Linux和Windows CE就要利用這一特性。

表3.4列出了ARM的7種異常。

表3.4 ARM的7種異常

異 常 類 型

處理器模式

執(zhí)行低地址

執(zhí)行高地址

復位異常(Reset)

特權(quán)模式

0x00000000

0xFFFF0000

未定義指令異常(Undefined interrupt)

未定義指令中止模式

0x00000004

0xFFFF0004

軟中斷異常(Software Abort)

特權(quán)模式

0x00000008

0xFFFF0008

預取異常(Prefetch Abort)

數(shù)據(jù)訪問中止模式

0x0000000C

0xFFFF000C

數(shù)據(jù)異常(Data Abort)

數(shù)據(jù)訪問中止模式

0x00000010

0xFFFF0010

外部中斷請求IRQ

外部中斷請求模式

0x00000018

0xFFFF0018

快速中斷請求FIQ

快速中斷請求模式

0x0000001C

0xFFFF001C

異常處理向量表如圖3.5所示。

當異常發(fā)生時,分組寄存器r14和SPSR用于保存處理器狀態(tài),操作偽指令如下。

R14_<exception_mode> = return link

SPSR_<exception_mode> = CPSR

CPSR[4∶0] = exception mode number

CPSR[5] = 0 /*進入ARM狀態(tài)*/

If <exception_mode> = = reset or FIQ then

CPSR[6] = 1 /*屏蔽快速中斷FIQ*/

CPSR[7] = 1 /*屏蔽外部中斷IRQ*/

PC = exception vector address

圖3.5 異常處理向量表

異常返回時,SPSR內(nèi)容恢復到CPSR,連接寄存器r14的內(nèi)容恢復到程序計數(shù)器PC。

1.復位異常

當處理器的復位引腳有效時,系統(tǒng)產(chǎn)生復位異常中斷,程序跳轉(zhuǎn)到復位異常中斷處理程序處執(zhí)行。復位異常中斷通常用在下面兩種情況下。

· 系統(tǒng)上電。

· 系統(tǒng)復位。

當復位異常時,系統(tǒng)執(zhí)行下列偽操作。

R14_svc = UNPREDICTABLE value

SPSR_svc = UNPREDICTABLE value

CPSR[4∶0] = 0b10011 /*進入特權(quán)模式*/

CPSR[5] = 0 /*處理器進入ARM狀態(tài)*/

CPSR[6] = 1 /*禁止快速中斷*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff0000

Else

PC = 0x00000000

復位異常中斷處理程序?qū)⑦M行一些初始化工作,內(nèi)容與具體系統(tǒng)相關(guān)。下面是復位異常中斷處理程序的主要功能。

· 設(shè)置異常中斷向量表。

· 初始化數(shù)據(jù)棧和寄存器。

· 初始化存儲系統(tǒng),如系統(tǒng)中的MMU等。

· 初始化關(guān)鍵的I/O設(shè)備。

· 使能中斷。

· 處理器切換到合適的模式。

· 初始化C變量,跳轉(zhuǎn)到應(yīng)用程序執(zhí)行。

2.未定義指令異常

當ARM處理器執(zhí)行協(xié)處理器指令時,它必須等待一個外部協(xié)處理器應(yīng)答后,才能真正執(zhí)行這條指令。若協(xié)處理器沒有相應(yīng),則發(fā)生未定義指令異常。

未定義指令異常可用于在沒有物理協(xié)處理器的系統(tǒng)上,對協(xié)處理器進行軟件仿真,或通過軟件仿真實現(xiàn)指令集擴展。例如,在一個不包含浮點運算的系統(tǒng)中,CPU遇到浮點運算指令時,將發(fā)生未定義指令異常中斷,在該未定義指令異常中斷的處理程序中可以通過其他指令序列仿真浮點運算指令。

仿真功能可以通過下面步驟實現(xiàn)。

① 將仿真程序入口地址鏈接到向量表中未定義指令異常中斷入口處(0x00000004或0xffff0004),并保存原來的中斷處理程序。

② 讀取該未定義指令的bits[27∶24],判斷其是否是一條協(xié)處理器指令。如果bits[27∶24]值為0b1110或0b110x,該指令是一條協(xié)處理器指令;否則,由軟件仿真實現(xiàn)協(xié)處理器功能,可以同過bits[11∶8]來判斷要仿真的協(xié)處理器功能(類似于SWI異常實現(xiàn)機制)。

③ 如果不仿真該未定義指令,程序跳轉(zhuǎn)到原來的未定義指令異常中斷的中斷處理程序執(zhí)行。

當未定義異常發(fā)生時,系統(tǒng)執(zhí)行下列的偽操作。

r14_und = address of next instruction after the undefined instruction

SPSR_und = CPSR

CPSR[4∶0] = 0b11011 /*進入未定義指令模式*/

CPSR[5] = 0 /*處理器進入ARM狀態(tài)*/

/*CPSR[6]保持不變*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff0004

Else

PC = 0x00000004

3.軟中斷SWI

軟中斷異常發(fā)生時,處理器進入特權(quán)模式,執(zhí)行一些特權(quán)模式下的操作系統(tǒng)功能。軟中斷異常發(fā)生時,處理器執(zhí)行下列偽操作。

r14_svc = address of next instruction after the SWI instruction

SPSR_und = CPSR

CPSR[4∶0] = 0b10011 /*進入特權(quán)模式*/

CPSR[5] = 0 /*處理器進入ARM狀態(tài)*/

/*CPSR[6]保持不變*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff0008

Else

PC = 0x00000008

4.預取指令異常

預取指令異常使由系統(tǒng)存儲器報告的。當處理器試圖去取一條被標記為預取無效的指令時,發(fā)生預取異常。

如果系統(tǒng)中不包含MMU時,指令預取異常中斷處理程序只是簡單地報告錯誤并退出。若包含MMU,引起異常的指令的物理地址被存儲到內(nèi)存中。

預取異常發(fā)生時,處理器執(zhí)行下列偽操作。

r14_svc = address of the aborted instruction + 4

SPSR_und = CPSR

CPSR[4∶0] = 0b10111 /*進入特權(quán)模式*/

CPSR[5] = 0 /*處理器進入ARM狀態(tài)*/

/*CPSR[6]保持不變*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff000C

Else

PC = 0x0000000C

5.數(shù)據(jù)訪問中止異常

數(shù)據(jù)訪問中止異常是由存儲器發(fā)出數(shù)據(jù)中止信號,它由存儲器訪問指令Load/Store產(chǎn)生。當數(shù)據(jù)訪問指令的目標地址不存在或者該地址不允許當前指令訪問時,處理器產(chǎn)生數(shù)據(jù)訪問中止異常。

當數(shù)據(jù)訪問中止異常發(fā)生時,處理器執(zhí)行下列偽操作。

r14_abt = address of the aborted instruction + 8

SPSR_abt = CPSR

CPSR[4∶0] = 0b10111

CPSR[5] = 0

/*CPSR[6]保持不變*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff000C10

Else

PC = 0x00000010

當數(shù)據(jù)訪問中止異常發(fā)生時,寄存器的值將根據(jù)以下規(guī)則進行修改。

① 返回地址寄存器r14的值只與發(fā)生數(shù)據(jù)異常的指令地址有關(guān),與PC值無關(guān)。

② 如果指令中沒有指定基址寄存器回寫,則基址寄存器的值不變。

③ 如果指令中指定了基址寄存器回寫,則寄存器的值和具體芯片的Abort Models有關(guān),由芯片的生產(chǎn)商指定。

④ 如果指令只加載一個通用寄存器的值,則通用寄存器的值不變。

⑤ 如果是批量加載指令,則寄存器中的值是不可預知的值。

⑥ 如果指令加載協(xié)處理器寄存器的值,則被加載寄存器的值不可預知。

6.外部中斷IRQ

當處理器的外部中斷請求引腳有效,而且CPSR寄存器的I控制位被清除時,處理器產(chǎn)生外部中斷IRQ異常。系統(tǒng)中各外部設(shè)備通常通過該異常中斷請求處理器服務(wù)。

當外部中斷IRQ發(fā)生時,處理器執(zhí)行下列偽操作。

r14_irq = address of next instruction to be executed + 4

SPSR_irq = CPSR

CPSR[4∶0] = 0b10010 /*進入特權(quán)模式*/

CPSR[5] = 0 /*處理器進入ARM狀態(tài)*/

/*CPSR[6]保持不變*/

CPSR[7] = 1 /*禁止外設(shè)中斷*/

If high vectors configured then

PC = 0xffff0018

Else

PC = 0x00000018

7.快速中斷FIQ

當處理器的快速中斷請求引腳有效且CPSR寄存器的F控制位被清除時,處理器產(chǎn)生快速中斷請求FIQ異常。

當快速中斷異常發(fā)生時,處理器執(zhí)行下列偽操作。

r14_fiq = address of next instruction to be executed + 4

SPSR_fiq = CPSR

CPSR[4∶0] = 0b10001 /*進入FIQ模式*/

CPSR[5] = 0

CPSR[6] = 1

CPSR[7] = 1

If high vectors configured then

PC= 0xffff001c

Else

PC = 0x0000001c

3.4.2 異常優(yōu)先級

每一種異常按表3.5中設(shè)置的優(yōu)先級得到處理。

表3.5 異常優(yōu)先級

優(yōu) 先 級

異 常

最高 1

復位異常

2

數(shù)據(jù)中止

3

快速中斷請求

4

中斷請求

5

預取指令異常

6

軟件中斷

最低 7

未定義指令

異常可以同時發(fā)生,處理器按表3.5的優(yōu)先級順序處理異常。例如,復位異常的優(yōu)先級最高,處理器上電時發(fā)生復位異常。所以當產(chǎn)生復位時,它將優(yōu)先于其他異常得到處理。同樣,當一個數(shù)據(jù)訪問中止異常發(fā)生時,它將優(yōu)先于除復位異常外的其他所有異常。

優(yōu)先級最低的2種異常是軟件中斷和未定義指令異常。因為正在執(zhí)行的指令不可能既是一條SWI指令,又是一條未定義指令,所以軟件中斷異常SWI和未定義指令異享有相同的優(yōu)先級。

3.4.3 處理器模式和異常

每一種異常都會導致內(nèi)核進入一種特定的模式。表3.6顯示了ARM處理器異常及其對應(yīng)的模式。此外,也可以通過編程改變CPSR,進入任何一種ARM處理器模式。

注意

用戶和系統(tǒng)模式是僅有的不可通過異常進入的兩種模式,也就是說,要進入這兩種模式,必須通過編程改變CPSR。

表3.6 ARM處理器異常及其對應(yīng)模式

異 常

模 式

用 途

快速中斷請求

FIQ

進行快速中斷請求處理

外部中斷請求

IRQ

進行外部中斷請求處理

SWI

SVC

進行操作系統(tǒng)的高級處理

復位

SVC

進行操作系統(tǒng)的高級處理

預取指令中止異常

ABORT

虛存和存儲器保護

數(shù)據(jù)中止異常

ABORT

虛存和存儲器保護

未定義指令

Undefined

軟件模擬硬件協(xié)處理器

3.4.4 異常響應(yīng)流程1.判斷處理器狀態(tài)

當異常發(fā)生時,處理器自動切換到ARM狀態(tài),所以在異常處理函數(shù)中要判斷在異常發(fā)生前處理器是ARM狀態(tài)還是Thumb狀態(tài)。這可以通過檢測SPSR的T位來判斷。

通常情況下,只有在SWI處理函數(shù)中才需要知道異常發(fā)生前處理器的狀態(tài)。所以在Thumb狀態(tài)下,調(diào)用SWI軟中斷異常必須注意以下兩點。

① 發(fā)生異常的指令地址為(lr-2)而不是(lr-4)。

② Thumb狀態(tài)下的指令是16位的,在判斷中斷向量號時使用半字加載指令LDRH。

下面的例子顯示了一個標準的SWI處理函數(shù),在函數(shù)中通過SPSR的T位判斷異常發(fā)生前的處理器狀態(tài)。

T_bit EQU 0x20 ; bit 5. SPSR中的ARM/Thumb狀態(tài)位,

:

:

SWIHandler

STMFD sp!, {r0-r3,r12,lr} ; 寄存器壓棧,保護程序現(xiàn)場

MRS r0, spsr ; 讀SPSR寄存器,判斷異常發(fā)生前的處理器狀態(tài)

TST r0, #T_bit ; 檢測SPSR的T位,判斷異常發(fā)生前是否為Thumb狀態(tài)

LDRNEH r0,[lr,#-2] ; 如果是Thumb狀態(tài),使用半字加載指令讀取發(fā)生異常的指令地址

BICNE r0,r0,#0xFF00 ; .提取中斷向量號.

LDREQ r0,[lr,#-4] ; 如果是ARM狀態(tài),使用字加載指令,讀取發(fā)生異常的指令地址

BICEQ r0,r0,#0xFF000000 ; 提取中斷向量號并將中斷向量號存入r0

; r0 存儲中斷向量號

CMP r0, #MaxSWI ; 判斷中斷是否超出范圍

LDRLS pc, [pc, r0, LSL#2] ; 如果未超出范圍,跳轉(zhuǎn)到軟中斷向量表Switable

B SWIOutOfRange ; 如果超出范圍,跳轉(zhuǎn)到軟中斷越界處理程序

switable

DCD do_swi_1

DCD do_swi_2

:

:

do_swi_1

; 1號軟中斷處理函數(shù)

LDMFD sp!, {r0-r3,r12,pc}^ ; Restore the registers and return.

; 恢復寄存器并返回

do_swi_2; 2號軟中斷處理函數(shù)

:

2.向量表

如前面介紹向量表時提到的,每一個異常發(fā)生時總是從異常向量表開始跳轉(zhuǎn)。最簡單的一種情況是向量表里面的每一條指令直接跳向?qū)?yīng)的異常處理函數(shù)。其中快速中斷處理函數(shù)FIQ_handler()可以直接從地址0x1C處開始,省下一條跳轉(zhuǎn)指令,如圖3.6所示。

圖3.6 異常處理向量表

但跳轉(zhuǎn)指令B的跳轉(zhuǎn)范圍為±32MB,但很多情況下不能保證所有的異常處理函數(shù)都定位在向量的32MB范圍內(nèi),需要更大范圍的跳轉(zhuǎn),而且由于向量表空間的限制,只能由一條指令完成。具體實現(xiàn)方法有下面兩種。

(1)MOV PC,#imme_value

這種辦法將目標地址直接賦值給PC。但這種方法受格式限制不能處理任意立即數(shù)。這個立即數(shù)由一個8位數(shù)值循環(huán)右移偶數(shù)位得到。

(2)LDR PC,[PC+offset]

把目標地址先存儲在某一個合適的地址空間,然后把這個存儲器單元的32位數(shù)據(jù)傳送給PC來實現(xiàn)跳轉(zhuǎn)。

這種方法對目標地址值沒有要求。但是存儲目標地址的存儲器單元必須在當前指令的±4KB空間范圍內(nèi)。

注意

在計算指令中引用offset數(shù)值的時候,要考慮處理器流水線中指令預取對PC值的影響。

3.4.5 從異常處理程序中返回

當一個異常處理返回時,一共有3件事情需要處理:通用寄存器的恢復、狀態(tài)寄存器的恢復以及PC指針的恢復。通用寄存器的恢復采用一般的堆棧操作指令即可,下面重點介紹狀態(tài)寄存器的恢復以及PC指針的恢復。

1.恢復被中斷程序的處理器狀態(tài)

PC和CPSR的恢復可以通過一條指令來實現(xiàn),下面是3個例子。

· MOVS PC,LR

· SUBS PC,LR,#4

· LDMFD SP!,{PC}^

這幾條指令是普通的數(shù)據(jù)處理指令,特殊之處在于它們把程序計數(shù)器寄存器PC作為目標寄存器,并且?guī)Я颂厥獾暮缶Y“S”或“^”。其中“S”或“^”的作用就是使指令在執(zhí)行時,同時完成從SPSR到CPSR的拷貝,達到恢復狀態(tài)寄存器的目的。

2.異常的返回地址

異常返回時,另一個非常重要的問題就是返回地址的確定。前面提到過,處理器進入異常時會有一個保存LR的動作,但是該保持值并不一定是正確中斷的返回地址。以一個簡單的指令執(zhí)行流水狀態(tài)圖來對此加以說明,如圖3.7所示。

圖3.7 3級流水線示例

在ARM架構(gòu)里,PC值指向當前執(zhí)行指令地址加8。也就是說,當執(zhí)行指令A(地址0x8000)時,PC等于0x8000+8=0x8008,即等于指令C的地址。假設(shè)指令A是BL指令,則當執(zhí)行時,會把PC值(0x8008)保存到LR寄存器。但是,接下來處理器會對LR進行一次自動調(diào)整,使LR=LR-0x4。所以,最終保存在LR里面的是圖3.5中所示的B指令地址。所以當從BL返回時,LR里面正好是正確的返回地址。

同樣的跳轉(zhuǎn)機制在所有的LR自動保存操作中都存在。當進入中斷響應(yīng)時,處理器對保存的LR也進行一次自動調(diào)整,并且跳轉(zhuǎn)動作也是LR=LR-0x04。由此,就可以對不同異常類型的返回地址依次比較。

假設(shè)在指令B處(地址0x8004)發(fā)生了異常,進入異常相應(yīng)后,LR經(jīng)過跳轉(zhuǎn)保存的地址值應(yīng)該是C的地址0x8008。

(1)軟中斷異常

如果發(fā)生軟中斷異常,即指令B為SWI指令,從SWI中斷返回后下一條執(zhí)行指令就是C,正好是LR寄存器保存的地址,所以只有直接把LR恢復給PC即可。

(2)IRQ或FIQ異常

如果發(fā)生的是IRQ或FIQ異常,因為外部中斷請求中斷了正在執(zhí)行的指令B,當中斷返回后,需要重新回到B指令執(zhí)行,也就是說,返回地址應(yīng)該是B(0x8004),需要把LR減4送PC。

(3)Data Abort數(shù)據(jù)中止異常

在指令B處進入數(shù)據(jù)異常的相應(yīng),但導致數(shù)據(jù)異常的原因卻應(yīng)該是上一條指令A。當中斷處理程序恢復數(shù)據(jù)異常后,要回到A重新執(zhí)行導致數(shù)據(jù)異常的指令,因此返回地址應(yīng)該是LR加8。

為方便起見,表3.7總結(jié)了各異常和返回地址的關(guān)系

表3.7 異常和返回地址

異 常

地 址

用 途

復位

復位沒有定義LR

數(shù)據(jù)中止

LR-8

指向?qū)е聰?shù)據(jù)中止異常的指令

FIQ

LR-4

指向發(fā)生異常時正在執(zhí)行的指令

IRQ

LR-4

指向發(fā)生異常時正在執(zhí)行的指令

預取指令中止

LR-4

指向?qū)е骂A取指令異常的那條指令

SWI

LR

執(zhí)行SWI指令的下一條指令

未定義指令

LR

指向未定義指令的下一條指令

3.4.6 在應(yīng)用程序中安裝異常處理程序1.使用匯編語言安裝異常處理程序

如果系統(tǒng)啟動不依賴于Debug或Debug monitor軟件,可以使用匯編語言在系統(tǒng)啟動時直接安裝異常處理程序。

下面的例子顯示了系統(tǒng)從0x0地址啟動,直接安裝異常處理程序的方法。

Vector_Init_Block

LDR PC, Reset_Addr

LDR PC, Undefined_Addr

LDR PC, SWI_Addr

LDR PC, Prefetch_Addr

LDR PC, Abort_Addr

NOP ;保留向量

LDR PC, IRQ_Addr

LDR PC, FIQ_Addr

Reset_Addr DCD Start_Boot

Undefined_Addr DCD Undefined_Handler

SWI_Addr DCD SWI_Handler

Prefetch_Addr DCD Prefetch_Handler

Abort_Addr DCD Abort_Handler

DCD 0 ;保留向量

IRQ_Addr DCD IRQ_Handler

FIQ_Addr DCD FIQ_Handler

有些情況下,系統(tǒng)0x0地址不一定是ROM。如果0x0地址為RAM,那么就系統(tǒng)將中斷向量表從ROM復制RAM,下面的例子顯示了這樣一個過程。

MOV R8, #0

ADR R9, Vector_Init_Block

LDMIA R9!,{r0-r7} ;復制中斷向量表 (8 words)

STMIA R8!,{r0-r7}

LDMIA R9!,{r0-r7} ;復制由偽操作 DCD定義的地址

STMIA R8!,{r0-r7}

注意

可以使用Scatter文件定義加載向量表的地址,這樣上述代碼的拷貝工作由C庫函數(shù)完成。

2.使用C語言安裝異常處理程序

程序中有時需要在main()函數(shù)中使用C語言安裝中斷向量表。這就要求指令經(jīng)編譯后的解碼能安裝在內(nèi)存的正確位置。

(1)向量表中使用跳轉(zhuǎn)指令的情況

如果在向量表中使用跳轉(zhuǎn)指令,使用下面的步驟完成向量表的安裝。

① 讀取異常處理程序的地址。

② 從異常處理程序地址中減去向量表中的偏移。

③ 為適應(yīng)指令流水線,將上一步得到的地址減8。

④ 將得到的結(jié)果右移2位,得到以字為單位的地址偏移量。

⑤ 將結(jié)果的高8位清零,得到跳轉(zhuǎn)指令的24位偏移量。

⑥ 將上一步得到的結(jié)果和0xea000000(無條件跳轉(zhuǎn)指令編碼)做邏輯與操作,從而得到要寫到向量表中的跳轉(zhuǎn)指令的正確編碼。

下面的例子顯示了這樣一個標準過程。

unsigned Install_Handler (unsigned routine, unsigned *vector)

{ unsigned vec, oldvec;

vec = ((routine - (unsigned)vector - 0x8)>>2);

if ((vec & 0xFF000000))

{

/* diagnose the fault */

prinf ("Installation of Handler failed");

exit (1);

}

vec = 0xEA000000 | vec;

oldvec = *vector;

*vector = vec;

return (oldvec);

}

(2)在向量表中使用加載PC指令

在向量表中使用加載PC指令,按照下面的步驟完成。

① 讀取異常處理程序地址。

② 從異常處理程序地址中減去向量表中的偏移。

③ 為適應(yīng)指令流水線,將上一步得到的地址減8。

④ 保留結(jié)果的后12位。

⑤ 將結(jié)果與0xe59ff000(LDR PC, [PC,#offset])做邏輯或操作,從而得到要寫到向量表中的跳轉(zhuǎn)指令的正確編碼。

⑥ 將異常處理程序的地址放到相應(yīng)的存儲單元。

下面的例子顯示了一個標準的C語言過程。

unsigned Install_Handler (unsigned location, unsigned *vector)

{ unsigned vec, oldvec;

vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000;

oldvec = *vector;

*vector = vec;

return (oldvec);

}

3.4.7 FIQ和IRQ中斷處理函數(shù)的設(shè)計1.中斷分支

ARM內(nèi)核只有兩個外部中斷輸入信號nFIQ和nIRQ。但對于一個系統(tǒng)來說,中斷源可能多達幾十個。為此,在系統(tǒng)集成的時候,一般都會有一個異??刂破鱽硖幚懋惓P盘?,如圖3.8所示。

圖3.8 中斷系統(tǒng)

這時候用戶程序可能存在多個IRQ/FIQ的中斷處理函數(shù)。為了使從向量表開始的跳轉(zhuǎn)始終能找到正確的處理函數(shù)入口,需要設(shè)置一套處理機制和方法。

多數(shù)情況下是由軟件來處理異常分支的,因為軟件可以通過讀取中斷控制器來獲得中斷源的信息,如圖3.9所示。

有些芯片可能支持特殊的硬件分支功能,這需要查看具體的芯片說明。

因為軟件的靈活性,可以設(shè)計出比圖3.9更好的流程控制方法,如圖3.10所示。

Int_vector_table是用戶自己開辟的一塊存儲器空間,里面按次序存放異常處理函數(shù)的地址。IRQ_Handler()從中斷控制器獲取中斷源信息,然后再從Int_vector_table中的對應(yīng)地址單元得到異常處理函數(shù)的入口地址,完成一次異常響應(yīng)的跳轉(zhuǎn)。這種方法的好處是用戶程序在運行過程中,能夠很方便地動態(tài)改變異常服務(wù)內(nèi)容。

圖3.9 軟件控制中斷分支

圖3.10 靈活的軟件分支設(shè)計

進入異常處理程序后,用戶可以完全按照自己的意愿來進行程序設(shè)計,包括調(diào)用Thumb狀態(tài)的函數(shù)等。但對于絕大多數(shù)的系統(tǒng)來說,有兩個步驟必須處理,一是現(xiàn)場保護,二是要把中斷控制器中對應(yīng)的中斷狀態(tài)標識清除,表明該中斷請求已經(jīng)得到響應(yīng),否則,中斷函數(shù)退出以后,又會被再一次觸發(fā),從而進入周而復始的死循環(huán)。

2.ARM編譯器對中斷處理函數(shù)編寫的擴展

考慮到中斷處理函數(shù)在現(xiàn)場保護和返回地址的處理上與普通函數(shù)的不同之處,不能直接把普通函數(shù)體連接到異常向量表上,需要在上面加上一層封裝,下面是一個例子。

IRQ_Handler ;中斷相應(yīng)函數(shù)

STMFD SP!,{r0-r12,lr} ;保護現(xiàn)場,一般只需要保護{r0-r3,LR}

BL IrqHandler ;進入普通處理函數(shù),C或匯編均可

……

LDMFD sp!,{r0-r12,LR} ;恢復現(xiàn)場

SUBS pc,lr,#4 ;中斷返回,注意返回地址

為了方便使用高級語言直接編寫異常處理函數(shù),ARM編譯器對此做了特定的擴展,可以使用函數(shù)聲明關(guān)鍵字_irq,這樣編譯出來的函數(shù)就可以滿足異常響應(yīng)對現(xiàn)場保護和恢復的需要,并且自動加入LR減4的處理,符合IQR和FIQ中斷處理的要求。

下面的例子顯示了使用_irq對中斷處理函數(shù)產(chǎn)生的影響。

C語言源程序如下。

__irq void IRQHandler (void)

{

volatile unsigned int *base = (unsigned int *) 0x80000000;

if (*base == 1)

{

/*調(diào)用C語言中斷處理函數(shù)*/

C_int_handler();

}

/*清楚中斷標志*/

*(base+1) = 0;

}

使用armcc編譯出的匯編代碼如下。

IRQHandler PROC

STMFD sp!,{r0-r4,r12,lr}

MOV r4,#0x80000000

LDR r0,[r4,#0]

SUB sp,sp,#4

CMP r0,#1

BLEQ C_int_handler

MOV r0,#0

STR r0,[r4,#4]

ADD sp,sp,#4

LDMFD sp!,{r0-r4,r12,lr}

SUBS pc,lr,#4

ENDP

如果不使用_irq子程序聲明關(guān)鍵字,編譯出的匯編代碼如下。

IRQHandler PROC

STMFD sp!,{r4,lr}

MOV r4,#0x80000000

LDR r0,[r4,#0]

CMP r0,#1

BLEQ C_int_handler

MOV r0,#0

STR r0,[r4,#4]

LDMFD sp!,{r4,pc}

ENDP

3.可重入中斷設(shè)計

在缺省情況下,ARM中斷是不可重入的。因為一旦進入異常響應(yīng)狀態(tài),ARM自動關(guān)閉中斷使能。如果在異常處理過程中,簡單地打開中斷使能而發(fā)生中斷嵌套時,顯然新的異常處理將破壞原來的中斷現(xiàn)場而導致出錯。但有時需要中斷必須是可重入的,因此要通過程序設(shè)計來解決這個問題。其中有兩個關(guān)鍵問題。

① 新中斷使能之前,必須要保護好前一個中斷的現(xiàn)場信息。比如LR_irq和SPSR_irq等,這一點比較容易做的。

② 中斷處理過程中對BL進行保護。

在中斷處理函數(shù)中發(fā)生函數(shù)調(diào)用BL是很常見的,假設(shè)有下面一種情況。

IRQ_Handler:

……

BL Foo

ADD

其中,

Foo:

STMFD SP!,{r0-r3,LR}

……

LDMFD SP!{r0-r3,PC}

上述程序,在IRQ處理函數(shù)IRQ_Handler()中調(diào)用了函數(shù)Foo()。若是在IRQ_Handler()里面中斷可重入的話,可能發(fā)生問題,考察下面的情況:當新的中斷請求恰好在“BL Foo”指令執(zhí)行完成后發(fā)生。這時候LR_irq寄存器(因在IRQ模式下,所以是LR_irq)的值將調(diào)整為BL指令的下一條指令(ADD)地址,使其能從Foo()正確返回;但是因為這時候發(fā)生了中斷請求,接下來要進行新中斷的響應(yīng),處理器在新中斷響應(yīng)過程中也要進行LR_irq保存。這次對LR_irq的操作發(fā)生了沖突,當新中斷返回后,往下執(zhí)行STMFD指令,這時候壓棧的LR已不是原來的ADD指令地址,從而使子程序Foo()無法正確返回。

這個問題無法通過增加額外的現(xiàn)場保護指令來解決。一個辦法就是在重新使能中斷之前改變處理器模式,也就是使上面程序的“BL Foo”指令不要運行在IRQ模式下。這樣當新的中斷發(fā)生時,就不會造成LR寄存器的沖突??紤]ARM的所有運行模式,采用SYSTEM模式是比較合適的,因為它是特權(quán)模式,不是IRQ模式,與中斷響應(yīng)無關(guān)。

下面的例子顯示了標準的IRQ/FIQ異常中斷處理程序。

PRESERVE8

AREA INTERRUPT, CODE, READONLY

IMPORT C_irq_handler

IRQ

SUB lr, lr, #4 ;跳轉(zhuǎn)返回地址

STMFD sp!, {lr} ;保存返回地址

MRS r14, SPSR ;讀取SPSR

STMFD sp!, {r12, r14} ;保存寄存器

; 清除中斷源

MSR CPSR_c, #0x1F ;切換到SYSTEM模式,

STMFD sp!, {r0-r3, lr} ;保存lr_USR 和其他使用到的寄存器

BL C_irq_handler ;跳轉(zhuǎn)到C中斷處理函數(shù)

LDMFD sp!, {r0-r3, lr} ;恢復用戶模式寄存器

MSR CPSR_c, #0x92 ;切換回irq模式

LDMFD sp!, {r12, r14}

MSR SPSR_cf, r14

LDMFD sp!, {pc}^

END

3.4.8 SWI異常處理函數(shù)的設(shè)計

本小節(jié)主要介紹編寫SWI處理程序時需要注意的幾個問題,包括下面內(nèi)容。

· 判斷SWI中斷號。

· 使用匯編語言編寫SWI異常處理函數(shù)。

· 使用C語言編寫SWI異常處理函數(shù)。

· 在特權(quán)模式下使用SWI異常中斷處理。

· 從應(yīng)用程序中調(diào)用SWI。

· 從應(yīng)用程序中動態(tài)調(diào)用SWI。

1.判斷SWI中斷號

當發(fā)生SWI異常,進入異常處理程序時,異常處理程序必須提取SWI中斷號,從而得到用戶請求的特定SWI功能。

在SWI指令的編碼格式中,后24位稱為指令的“comment field”。該域保存的24位數(shù),即為SWI指令的中斷號,如圖3.11所示。

圖3.11 SWI指令編碼格式

第一級的SWI處理函數(shù)通過LR寄存器內(nèi)容得到SWI指令地址,并從存儲器中得到SWI指令編碼。通常這些工作通過匯編語言、內(nèi)嵌匯編來完成。

下面的例子顯示了提取中斷向量號的標準過程。

PRESERVE8

AREA TopLevelSwi, CODE, READONLY ;第一級SWI處理函數(shù).

EXPORT SWI_Handler

SWI_Handler

STMFD sp!,{r0-r12,lr} ;保存寄存器

LDR r0,[lr,#-4] ;計算SWI指令地址.

BIC r0,r0,#0xff000000 ;提取指令編碼的后24位

;

; 提取出的中斷號放r0寄存器,函數(shù)返回

;

LDMFD sp!, {r0-r12,pc}^ ;恢復寄存器

END

例子中,使用LR-4得到SWI指令的地址,再通過“BIC r0, r0, #0xFF000000”指令提取SWI指令中斷號。

2.匯編語言編寫SWI異常處理函數(shù)

最簡單的方法是利用得到的中斷向量號,使用跳轉(zhuǎn)表直接跳轉(zhuǎn)到實現(xiàn)相應(yīng)SWI功能的處理程序。

下面的例子,使用匯編語言實現(xiàn)了這種跳轉(zhuǎn)。

CMP r0,#MaxSWI ;中斷向量范圍檢測

LDRLS pc, [pc,r0,LSL #2]

B SWIOutOfRange

SWIJumpTable

DCD SWInum0

DCD SWInum1

; 使用DCD 定義各功能函數(shù)入口地址

SWInum0 ;0號中斷

B EndofSWI

SWInum1 ;1號中斷

B EndofSWI

;

EndofSWI

3.使用C語言編寫SWI異常處理函數(shù)

雖然第一級SWI處理函數(shù)(完成中斷向量號的提?。┍仨氂脜R編語言完成,但第二級中斷處理函數(shù)(根據(jù)提取的中斷向量號,跳轉(zhuǎn)到具體處理函數(shù))就可以使用C語言來完成。

因為第一級的中斷處理函數(shù)已經(jīng)將中斷號提取到寄存器r0中,所以根據(jù)AAPCS函數(shù)調(diào)用規(guī)則,可以直接使用BL指令跳轉(zhuǎn)到C語言函數(shù),而且中斷向量號作為第一個參數(shù)被傳遞到C函數(shù)。

例如匯編中使用了“BL C_SWI_Handler”跳轉(zhuǎn)到C語言的第二級處理函數(shù),則第二級的C語言函數(shù)示例如下所示。

void C_SWI_handler (unsigned number)

{

switch (number)

{

case 0 : /* SWI number 0 code */

break;

case 1 : /* SWI number 1 code */

break;

...

default : /* Unknown SWI - report error */

}

}

另外,如果需要傳遞的參數(shù)多于1個,那么可以使用堆棧,將堆棧指針作為函數(shù)的參數(shù)傳遞給C類型的二級中斷處理程序,就可以實現(xiàn)在兩級中斷之間傳遞多個參數(shù)。

例如:

MOV r1, sp ;將傳遞的第二個參數(shù)(堆棧指針)放到r1中

BL C_SWI_Handler ;調(diào)用C函數(shù)

相應(yīng)的C函數(shù)的入口變?yōu)椋?/p>

void C_SWI_handler(unsigned number, unsigned *reg)

同時,C函數(shù)也可以通過堆棧返回操作的結(jié)果。

4.在特權(quán)模式下使用SWI異常處理

在特權(quán)模式下使用SWI異常處理,和IRQ/FIQ中斷嵌套基本類似。當執(zhí)行SWI指令后,處理器執(zhí)行下面操作。

① 處理器進入特權(quán)模式。

② 將程序狀態(tài)字內(nèi)容CPSR保存到SPSR_svc。

③ 返回地址放入LR_svc。

如果處理器已經(jīng)處于特權(quán)模式,再發(fā)生SWI異常,則LR_svc和SPSR_svc寄存器的值將丟失。

所以在特權(quán)模式下,調(diào)用SWI軟中斷異常,必須先將LR_svc和SPSR_svc寄存器的值壓棧保護。下面的例子顯示了一個可以在特權(quán)模式下調(diào)用的SWI處理函數(shù)。

AREA SWI_Area, CODE, READONLY

PRESERVE8

EXPORT SWI_Handler

IMPORT C_SWI_Handler

T_bit EQU 0x20

SWI_Handler

STMFD sp!,{r0-r3,r12,lr} ;寄存器壓棧保護

MOV r1, sp ;堆棧指針放r1作為參數(shù)傳遞.

MRS r0, spsr ;讀取spsr.

STMFD sp!, {r0, r3} ;將spsr壓棧保護

;

;

LDR r0,[lr,#-4] ;計算SWI指令地址.

BIC r0,r0,#0xFF000000 ;讀取SWI中斷向量號.

; r0存放中斷向量號

; r1 堆棧指針

BL C_SWI_Handler ;調(diào)用C程序的SWI處理函數(shù).

LDMFD sp!, {r0, r3} ;從堆棧中讀取spsr.

MSR spsr_cf, r0 ;恢復spcr

LDMFD sp!, {r0-r3,r12,pc}^ ;恢復其他寄存器并返回.

END

5.從應(yīng)用程序中調(diào)用SWI

可從匯編語言或 C/C++ 中調(diào)用 SWI。

(1)從匯編應(yīng)用程序中調(diào)用SWI

從匯編語言程序中調(diào)用SWI,只要遵循AAPCS標準即可。調(diào)用前,設(shè)定所有必須的值并發(fā)出相關(guān)的 SWI。例如:

MOV r0, #65 ; 將軟中斷的子功能號放到r0中

SWI 0x0

注意

SWI指令和其他所以ARM指令一樣,可以被條件執(zhí)行。

(2)從C應(yīng)用程序中調(diào)用SWI

在C或C++應(yīng)用程序中調(diào)用SWI,要將C語言的子程序用編譯器擴展_swi聲明,例如:

__swi(0) void my_swi(int);

……

……

……

my_swi(65);

編譯器擴展_swi確保了SWI以內(nèi)聯(lián)方式進行編譯,而沒有額外的開銷。但有如下的AAPCS限制。

· 函數(shù)調(diào)用參數(shù)只能使用r0~r3傳遞。

· 函數(shù)返回值只能通過r0~r3傳遞。

向內(nèi)聯(lián)的SWI函數(shù)傳遞參數(shù)和向?qū)嶋H的子函數(shù)傳遞參數(shù)基本類似。但返回值的情況比較復雜。如果有兩到四個返回值,則必須告訴編譯程序返回值是以結(jié)構(gòu)形式返回的,并使用__value_in_regs 偽操作聲明。這是因為基于結(jié)構(gòu)值的函數(shù)通常被處理為一個void(空)型函數(shù),且第一個自變量必須為存放結(jié)果結(jié)構(gòu)的地址。

下面的例子顯示了對編號為0x0、0x1、0x2和0x3的SWI軟中斷的調(diào)用。其中,SWI0x0和SWI0x1傳遞兩個整型參數(shù)并返回一個單一結(jié)果;SWI0x2傳遞4個參數(shù)并返回一個單一結(jié)果;而SWI0x3傳遞4個參數(shù)并通過結(jié)構(gòu)體返回4個結(jié)果。

#include <stdio.h>

#include "swi.h"

unsigned *swi_vec = (unsigned *)0x08;

extern void SWI_Handler(void);

int main( void )

{

int result1, result2;

struct four_results res_3;

Install_Handler( (unsigned) SWI_Handler, swi_vec );

printf("result1 = multiply_two(2,4) = %dn", result1 = multiply_two(2,4));

printf("result2 = multiply_two(3,6) = %dn", result2 = multiply_two(3,6));

printf("add_two( result1, result2 ) = %dn", add_two( result1, result2 ));

printf("add_multiply_two(2,4,3,6) = %dn", add_multiply_two(2,4,3,6));

res_3 = many_operations( 12, 4, 3, 1 );

printf("res_3.a = %dn", res_3.a );

printf("res_3.b = %dn", res_3.b );

printf("res_3.c = %dn", res_3.c );

printf("res_3.d = %dn", res_3.d );

return 0;

}

__swi(0) int multiply_two(int, int);

__swi(1) int add_two(int, int);

__swi(2) int add_multiply_two(int, int, int, int);

struct four_results

{

int a;

int b;

int c;

int d;

};

__swi(3) __value_in_regs struct four_results many_operations(int, int, int, int);

(3)應(yīng)用程序中動態(tài)調(diào)用SWI

在某些情形下,需要調(diào)用直到運行時才會知道其編號的 SWI。例如,當有很多相關(guān)操作可在同一目標上執(zhí)行,并且每一個操作都有其自己的 SWI 時,就會發(fā)生這種情況。在此情況下,上一小節(jié)的方法不適用。

解決的方法有兩種。

· 在運行時得到SWI功能號,然后構(gòu)造出相應(yīng)的SWI指令的編碼,將該編碼保存在某個存儲單元中,將PC指針指向該單元,執(zhí)行指令。

· 使用一個通用的SWI異常中斷處理程序,將運行時需要調(diào)用的SWI功能號作為參數(shù)傳遞給該通用的SWI異常處理程序,通用的SWI異常中斷處理程序根據(jù)參數(shù)值調(diào)用相應(yīng)的SWI處理程序完成需要的操作。

通過匯編語言可以實現(xiàn)第二種解決辦法:通過寄存器(通常為r0或r12)傳遞所需要的操作數(shù),這樣可以重新編寫SWI處理程序,對相應(yīng)寄存器中的值進行處理。

但有些情況下,為了節(jié)省程序開銷,需要直接使用SWI中斷號對程序調(diào)用。例如,操作系統(tǒng)可能會使用單一的一條SWI指令并用寄存器來傳遞所需運算的編號。這使得其他SWI空間可用于特定應(yīng)用程序的SWI。在一個特定的應(yīng)用程序中,如果從指令中提取SWI編號的開銷太大,就可使用這個方法。ARM(0x123456)和Thumb(0xAB)半主機方式的SWI就是這樣實現(xiàn)的。

下面的例子顯示了如何使用_swi將C函數(shù)調(diào)用映射到半主機方式的SWI。

#ifdef __thumb

/* Thumb 狀態(tài)的Semihosting軟中斷處理*/

#define SemiSWI 0xAB

#else

/* ARM狀態(tài)下的Semihosting的軟中斷處理*/

#define SemiSWI 0x123456

#endif

/* 使用Semihosting軟中斷輸出一個字符*/

__swi(SemiSWI) void Semihosting(unsigned op, char *c);

#define WriteC(c) Semihosting (0x3,c)

void write_a_character(int ch)

{

char tempch = ch;

WriteC( &tempch );

}

編譯程序含有一個機制,用以支持使用r12來傳遞所需運算的值。根據(jù)AAPCS標準,r12為IP寄存器,并且專用于函數(shù)調(diào)用。其他時間內(nèi)可將其用作暫存寄存器。如前面所述,通用SWI參數(shù)和返回值通過r0~r3寄存器傳遞。而r12可用于傳遞通用SWI調(diào)用的中斷功能編號。

下面的例子顯示了通用SWI的C語言程序框架。

__swi_indirect(0x80)

unsigned SWI_ManipulateObject(unsigned operationNumber,

unsigned object,unsigned parameter);

unsigned DoSelectedManipulation(unsigned object,

unsigned parameter, unsigned operation)

{

return SWI_ManipulateObject(operation, object, parameter);

}

生成的匯編代碼如下。

DoSelectedManipulation PROC

STMFD sp!,{r3,lr}

MOV r12,r2

SWI 0x80

LDMFD sp!,{r3,pc}

ENDP

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉