ARM系統中斷向量表的動態配置

通常情況下32位ARM嵌入式系統的中斷向量表是程序編譯前設置好的。在編寫32位ARM 嵌入式系統的中斷

服務程序、設置和修改ARM體系結構的中斷向量表時,常感到相當麻煩,不得不修改彙編代碼,對不喜歡

使用匯編代碼編程的程序員尤其如此。 當需要在程序運行過程中動態修改中斷向量的程序時會感到更爲

不便,不得不增加很多分支處理指令才能實現。爲此本文提出一種簡便高效的配置方法,實現了 ROM固

化程序在運行時動態配置ARM嵌入式系統中斷向量表的功能。
1 ARM中斷向量兩種設置方法
在32位ARM系統中,一般都是在中斷向量表中放置一條分支指令或PC寄存器加載指令,實現程序跳轉到中

斷服務例程的功能。例如:
IRQEntry B HandleIRQ ;跳轉範圍較小
B HandleFIQ
或IRQEntry LDR PC,=HandleIRQ ;跳轉的範圍是任意32位地址空間
LDR PC,=HandleFIQ
LDR僞指令等效生成1條存儲讀取指令和1條32位常數定義指令。32位常數存儲在LDR指令四周的存儲單元

中,相對偏移小於4KB。該32位數據就是要跳轉到的中斷服務程序入口地址。
之所以使用LDR僞指令,是因爲ARM的RISC指令爲單字指令,不能裝載32位的立即數 (常數),無法直接

把一個32位常數數據或地址數據裝載到寄存器中。下面一般程序與上述僞指令功能等效,但中斷向量表

描述得更爲清楚。其中 VectorTable爲相對LDR指令的偏移量:
IRQEntry LDR PC,VectorTable 0
;與LDR PC,=HandleIRQ等效
LDR PC,VectorTable 4
;與LDR PC,=HandleFIQ等效
……
VectorTable DCD HandleTRQ
DCD HandleFIQ
……
HandleIRQ
……
HandleFIQ
一般ARM嵌入式系統的程序都是固化在從00000000H開始的低端ROM空間中,中斷 向量表VectorTable也是

固化在ROM中,所以上述兩種方法都無法在程序運行時動態隨機修改中斷向量表。不論對於初學ARM處理

器的程序員還是有 經驗的程序員,設置中斷向量都相當繁瑣,必須修改ARM的C程序的啓動代碼。一段晦

澀的彙編代碼很不方便,比較輕易出錯。
2 X86與ARM處理器中斷向量表比較
實模式X86程序員都熟悉,在X86體系結構的PC系統中,不論是用匯編還是用C語言,都可以動態隨機地設

置、修改中斷向量表—只需要簡單地把中斷程序例程的入口地址寫入到中斷向量表數據區,即可完成向

量表的設置。
X86向量表設置方便的原因有兩個。其一是中斷向量表與程序代碼完全分離,中斷向量表設置 在RAM數據

空間,向量表存放的數據是純粹地址數據;而在ARM向量表中存放的是與中斷服務例程入口有關的一條分

支指令。另一個原因是,除BIOS外,大 多數PC程序都是在運行時加載到RAM中的,程序數據是不加區別

的,所以可以很輕易在程序運行的過程中從數據生成程序,並可以很輕易把CPU控制權轉到新 生成的程

序中。
表面上看,在ARM第二種中斷向量設置方法的向量表VectorTable中也是純地址數 據,不含指令代碼,似

乎可以把VectorTable設置在RAM數據段中。然而一般ARM體系的ROM代碼段和RAM數據段間的偏移遠大於2

12,故超出了LDR使用PC爲基址的相對尋址範圍。
代碼中的VectorTable是一個與當前PC間的一個偏移,LDR指令的相對地址是在 編譯時計算的,要求

VectorTable<2 12,所以VectorTable不能隨意安排在RAM空間中。VectorTable一般只能安排在中斷跳轉

指令四周的代碼區內中。
3 ARM結構中中斷向量表的動態配置方法
要在ARM結構中實現與X86中一樣方便的在中斷向量的隨機存取功能,向量表的地址數據必須可以安排在

任意32位地址的RAM空間中。爲此,中斷處理必須增加一條指令,先跳轉到向量表,然後執行向量表中動

態生成的跳轉指令,跳轉到中斷服務程序,參見下列初始化代碼:
;******向量表******
ENTRY
B ResetHandle ;原向量偏移 ,中斷號
B ReseHandle ;0x00 ,00
LDR PC,=NewVectorTable 0x08 ;0x04,未定義 ,01
LDR PC,=NeWVector Table 0x10 ;0x08,SWI,02
LDR PC,=NewVectorTable 0x18 ;0x0c,未定義 ,03
LDR PC,=NewVectorTable 0x20 ;0x10,未定義 ,04
LDR PC,=NewVectorTable 0x28;0x14,未定義 0,05
LDR PC,=NewVectorTable 0x30 ;0x18,IRQ ;06
LDR PC,=NewVectorTable 0x38 ;0x1c,FIQ ,07
……
;******代碼段******
ResetHandle

……

;***數據段,爲NewVectorTable分配數據空間***

NewVectorTable # 128;大小根據需要定義,每向量2個字(8字節);

程序運行時,中斷服務的初始化 程序必須設置好新的中斷向量表,即在NewVectorTable表中動態生成下
列指令:

NewVectorTable;表安排在RAM頂端0x0c1fff00處(由硬件設定)

LDR PC,[PC,#4];指令代碼爲0xe51ff004,功能爲PC〈-[PC 4]

nVt00 DCD ISR_RESET_HANDLE

LDR PC,[PC,#4];與LDR PC,nVt01指令等效

nVt01 DCD ISR_UNDEF_HANDLE

LDR PC,[PC,#4]

nVt02 DCD ISR_SWI_HANDLE

LDR pC,[PC,#4]

nVt03 DCD ISR_UNDEF_HANDLE

LDR PC,[PC,#4]

nVt04 DCD ISR_UNDEF_HANDLE

LDR PC,[PC,#4]

nVt05 DCD ISR_UNDEF_HANDLE

LDR PC,[PC,#4]

NVt06 DCD ISR_IRQ_HANDLE

LDR PC,[PC,#4]

nVt07 DCD ISR_FIQ_HANDLE

……

可用C函數在NweVectorTable中生成含上述指令的向量表,具體實現如下:

#define VECTOR_TABLE 0x0c1fff00

//向量表首地址,根據實際硬件來配置

#define INSTRUCTION_LDR_PC 0xe51ff004

//加載PC寄存器的指令碼

//設置向量C函數,ISR_Handle中斷服務程序地址

void SetVector(unsigned char no,unsigned long int ISR_Handle){

unsigned long int * pVectorTable;

//定義32位無符號數指令,指向向量表

pVectorTable=((unsigned long int *)(VECTOR_TABLE (no<<3)));

*pVectorTable =INSTRUCTION_LDR_PC;

//在向量表中放置LDR PC,[PC,#4]指令

*pVectorTable=ISR_Handle;//設置中斷服務例程入口地址}

//讀取向量C函數,no代表中斷號

unsigned long int GetVector(unsigned char no){

unsigned long int *pVectorTable;

pVectorTable=((unsigned long int *)(VECTOR_TABLE (no<<3)));

return *( pVectorTable);//返回中斷處理程序入口地址

}

使用上述初始化代碼和向量設置函數,除復位向量外,其它所有中斷向量都可以指向了在RAM 數據區中
的新向量表,並給定一個統一的中斷編號。中斷服務程序可以放在任何模塊文件中編譯連接,不需要修
改原向量表代碼,但在打開中斷使用中斷服務例程前必須使用C函數SetVector()設置中斷向量。

4 結論

本文提出的中斷向量表配置策略和實現方法,簡便高效,僅比標準處理方法增加一條指令的執行時間。
當把ARM的C初始化彙編代碼中所有中斷源(包括擴展的內外部中斷源)的向量都指向了新向量表,並統
一編號,此後編寫任何中斷服務程序幾乎不需要修改彙編代碼,C初始化代碼完全可以對C程序員隱藏起
來,並可以像在X86體系下一樣動態地設置和修改中斷向量。初始化應用程序執行環境
   映像一開始總是存儲在ROM/Flash裏面的,其RO部分即可以在ROM/Flash裏面執行,也可以轉移到
速度更快的RAM中執行;而RW和ZI這兩部分是必須轉移到可寫的RAM裏去。所謂應用程序執行環境的初始化
,就是完成必要的從ROM到RAM的數據傳輸和內容清零。

下面是在ADS下,一種常用存儲器模型的直接實現:
LDR  r0,=|Image$$RO$$Limit| 得到RW數據源的起始地址
LDR  r1,=|Image$$RW$$Base| RW區在RAM裏的執行區起始地址
LDR  r2,=|Image$$ZI$$Base| ZI區在RAM裏面的起始地址
CMP  r0,r1         比較它們是否相等
   BEQ  %F1
0   CMP  r1,r3
   LDRCC r2,[r0],#4STRCC r2,[r1],#4
   BCC  %B0
1   LDR  r1,=|Image$$ZI$$Limit|
   MOV  r2,#0
2   CMP  r3,r1
   STRCC r2,[r3],#4
   BCC  %B2
   程序實現了RW數據的拷貝和ZI區域的清零功能。其中引用到的4個符號是由鏈接器第一輸出的。
|Image$$RO$$Limit|:表示RO區末地址後面的地址,即RW數據源的起始地址
|Image$$RW$$Base|:RW區在RAM裏的執行區起始地址,也就是編譯器選項RW_Base指定的地址
|Image$$ZI$$Base|:ZI區在RAM裏面的起始地址
|Image$$ZI$$Limit|:ZI區在RAM裏面的結束地址後面的一個地址

   程序先把ROM裏|Image$$RO$$Limt|開始的RW初始數據拷貝到RAM裏面|Image$$RW$$Base|開始的地
址,當RAM這邊的目標地址到達|Image$$ZI$$Base|後就表示RW區的結束和ZI區的開始,接下去就對這片ZI
區進行清零操作,直到遇到結束地址|Image$$ZI$$Limit|

改變處理器模式
   因爲在初始化過程中,許多操作需要在特權模式下才能進行(比如對CPSR的修改),所以要特別注
意不能過早的進入用戶模式。

   內核級的中斷使能也可以考慮在這一步進行。如果系統中另外存在一個專門的中斷控制器,這麼做
總是安全的。

呼叫主應用程序

   當所有的系統初始化工作完成之後,就需要把程序流程轉入主應用程序。最簡單的一種情況是:
IMPORT main


B   main
直接從啓動代碼跳轉到應用程序的主函數入口,當然主函數名字可以由用戶隨便定義。
在ARM ADS環境中,還另外提供了一套系統級的呼叫機制。
IMPORT __main

B   __main
__main()是編譯系統提供的一個函數,負責完成庫函數的初始化和初始化應用程序執行環境,最後自動跳
轉到main()函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章