1. 首先就是知道ARM狀態下37個寄存器包括通用寄存器、程序計數器、狀態寄存器。綠顏色的就是相應模式下的私有寄存器。就是說程序一般運行在系統和用戶模式下,使用的是系統和用戶模式下的通用寄存器,當有異常發生時,比如FIQ,那么系統將切換到FIQ模式下,相應的就會采用FIQ模式下的寄存器,其中綠顏色的就是只在FIQ模式下才會用到的寄存器。 2. 在模式切換的過程中,要保護系統和用戶模式下的通用寄存器狀態,以便在異常處理完成之后程序能正常返回。因為FIQ模式下R8-R14為其私有寄存器,所以切換到FIQ模式的過程中,系統和用戶模式下的通用寄存器的R8-R14就不用保護了,所以減少了對寄存器存取的需要,從而可以快速的進行FIQ處理,故稱為FIQ。這就是FIQ的私有寄存器比其它模式多的原因。切到FIQ模式需要保護的寄存器為R0~R7,切到SVC,IRQ,ABORT.和未定義模式需要保護的寄存器為R0~R12 3. 異常處理的動作。當然這都是CPU內核自己干的。以FIQ為例。 當CPU切入FIQ模式時, 第一,將原來執行程序的下一條指令地址保存到LR中,就是將R14保存到R14_fiq里面。 第二,拷貝CPSR到SPSR_fiq。 第三,改變CPSR模式位的值,改到FIQ模式。 第四,改變PC值,將其指向異常處理向量所指的下一條指令。 離開異常處理的時候, 第一,將LR(R14_fiq)賦給PC。 第二,將SPSR(SPSR_fiq)拷貝到CPSR。 第三,清除中斷禁止標志(如果開始時置位了)。 4.異常中斷向量 異常中斷的向量地址 地址 異常中斷類型 入口時處理器的操作模式 0x00000000 復位 超級用戶 0x00000004 未定義指令 未定義 0x00000008 軟件中斷 超級用戶 0x0000000c 中止(預取指) 中止 0x00000010 中止(數據) 中止 0x00000014 保留 保留 0x00000018 IRQ IRQ 0x0000001c FIQ FIQ 異常中斷優先級 中斷 優先級 復位 最高 數據異常 FIQ IRQ 預取指異常中斷 未定義指令和軟件中斷 最低 5.當發生IRQ中斷時 第一,模式進入到IRQ里面。 第二,PC跳到0x00000018處運行。因為這是IRQ的中斷入口。 第三, 通過0x00000018:LDR PC, IRQ_ADDR。跳轉到相應的中斷服務程序。這個里面就有個確定哪個中斷源的問題了。那就有優先級的問題了。每個中斷源會有自己的中斷服務程序。 第四,得到中斷源有硬件實現和軟件處理兩種方式。比如LPC21XX的就是利用硬件方式,為了利用向量中斷控制器的優點,IRQ中斷向量入口處代碼做了修改,變成 0x00000018:LDR PC, [PC, #-0xff0]。 這條指令從內存映射地址0xfffff030處獲得數據裝載到PC,這樣就能夠直接從硬件中獲得中斷源。這樣就減少了中斷延遲。記得,三星的S3C44B0好象采用的是用軟件確定中斷源,因此要建立中斷向量表。好久不用了,記不清了。 第五,得到中斷源,就知道要跳到哪個中斷服務程序去了。 一般都是這么定義的。Timer0_Handler HANDLER Timer0 。這種格式是調用一種宏定義,目的是保護現場,跳到中斷服務程序。
ARM中斷實現過程的個人筆記 作者: 逛逛 發布日期:2006-3-19 歡迎大家加入我的小圈子共同學習討論:www./club/embbed/ 決定開始學習嵌入式后,最先做的事情就是要熟悉ARM指令及其偽指令偽操作。ARM指令的助記符其實都是其具體功能的單次縮寫,所以學習的過程中最好利用網絡,從一些文獻或書籍中找到ARM指令助記符的全稱,這樣方便記憶。學完之后,我做了整理了一個有關這方面的筆記,有需要的朋友請郵件聯系:gmman@163.com 接下來的學習過程中,比較難以理解的是ARM的中斷過程和存儲系統。ARM中斷的實現有些書上看一兩遍也不見得能夠完全理解,當然可能只對于像我一樣跨專業的朋友來說存在這個問題。這次只談中斷。由于是初學者,難免會出錯,敬請各位指正。 當一個程序正常執行過程中,CPU可能檢測到有某個中斷源發出中斷請求,這時ARM硬件實現了程序強制跳轉,在這之前保存了相關信息,以便程序正常返回。如果是發生了Reset中斷,程序實現系統初始化設置。 開始比較難以理解的是中斷產生后,程序都進行了哪些操作。我就從跟蹤PC作為分析的主線。以發生FIQ中斷為例。(只以ROM起始地址為0為例,不為0的情況參照存儲地址映射) 最簡單的是中斷發生后,PC=0x08,在此地址處存放一個跳轉指令,跳轉到相關處理程序。當然多數情況中斷處理程序可能比較復雜,并且要處理多種中斷的情況下,采用一步映射兩步跳轉(我自己起的名字,不一定妥當)。如下圖所示:
一步映射指,在RAM地址中建立一個中斷向量表,圖中該表起始地址為0x400000,在該表中存放的是中斷處理函數的入口地址。兩步跳轉是指,當中斷發生時,由于系統硬件強制程序跳轉到了0x08處,在該地址處是一個跳轉指令,跳轉到中斷函數地址解析程序IRQ_Handler,完成一步跳轉。解析程序(IRQ_Handler)的作用無非是把中斷向量表內中斷處理函數(SystemIrqHandler)的入口地址賦值給 PC,如圖所示PC=0x003000280,完成第二步跳轉,開始處理中斷。在中斷處理函數的最后,恢復中斷開始時保存的相關寄存器的值,完成中斷。 下面以一個實例來具體說明中斷建立及實現的過程。 首先通過偽指令建立一個中斷向量表,用于存放中斷程序的入口地址(如上圖中的中斷向量表,注意,此時表中還未賦值): ;/* EXCEPTION HANDLER VECTOR TABLE */ ^ DRAM_BASE HandleReset # 4 HandleUndef # 4 HandleSwi # 4 HandlePrefetch # 4 HandleAbort # 4 HandleReserv # 4 HandleIrq # 4 HandleFiq # 4 然后定義一個連續的數據段,并把中斷處理函數的入口地址值賦給各字單元 ExceptionHandlerTable DCD UserCodeArea DCD SystemUndefinedHandler DCD SystemSwiHandler DCD SystemPrefetchHandler DCD SystemAbortHandler DCD SystemReserv DCD SystemIrqHandler DCD SystemFiqHandler 下面從程序的開始處分析: AREA Init, CO ENTRY /* ROM起始地址向量表 */ B Reset_Handler B Undefined_Handler B SWI_Handler B Prefetch_Handler B Abort_Handler NOP Reserved vector B IRQ_Handler B FIQ_Handler /* B跳轉范圍限于+ -32M內*/ /* 以下是地址解析程序 */ IRQ_Handler SUB sp, sp, #4 STMFD sp!, {r0} FD滿遞減堆棧 執行寄存器壓棧操作. LDR r0, =HandleIrq //對應程序開始處以偽指令定義的向量表 LDR r0, [r0] //中斷處理函數的地址賦給R0. STR r0, [sp, #4] //中斷處理函數的地址入棧 LDMFD sp!, {r0, pc} //實現程序跳轉,目前沒明白為什么又給r0賦值? 上面提到了還沒有給中斷向量表賦值,下面代碼把中斷處理函數的地址放到DRAM中斷向量表里 EXCEPTION_VECTOR_TABLE_SETUP LDR r0, =HandleReset LDR r1, =ExceptionHandlerTable MOV r2, #8 ExceptLoop LDR r3, [r1], #4 STR r3, [r0], #4 SUBS r2, r2, #1 Down Count BNE ExceptLoop ;; 下面是中斷處理函數 SystemIrqHandler IMP STMFD sp!, {r0-r7, lr} BL ISR_IrqHandler LDMFD sp!, {r0-r7, lr} SUBS pc, lr, #4 它實際上只調用了下面的C語言的中斷處理函數,其他什么也沒做。 void ISR_IrqHandler(void) { IntOffSet = (U32)INTOFFSET; (IntOffSet>>2) (*InterruptHandlers[IntOffSet>>2])(); // Call interrupt service routine } 以上編程思路是,先在系統初始化時重新建立一個中斷向量表,并把相關的中斷處理函數的地址放到中斷向量表中。當系統監測到有中斷源請求服務后,硬件實現pc跳轉到地址0x08處,執行一個跳轉指令B IRQ_Handler , 然后執行地址解析程序,把中斷向量表中的中斷處理函數的入口地址賦給pc,開始響應中斷。在中斷處理函數的最后,執行 LDMFD sp!, {r0-r7, lr} SUBS pc, lr, #4 實現中斷的返回
硬件平臺為ARM7內核。當有軟中斷發生(即調用2.2中某一個函數時)時,系統首先自動調轉到0x0008處執行。 1、第一級中斷向量 AREA Init,CO
ENTRY b ResetHandler ;for debug b HandlerUndef ;handlerUndef b HandlerSWI ;SWI interrupt handler b HandlerPabort ;handlerPAbort b HandlerDabort ;handlerDAbort b . ;handlerReserved b HandlerIRQ b HandlerFIQ 2、宏展開 繼續找HandlerSWI。 HandlerSWI HANDLER HandleSWI 3、內存第二級中斷向量 再找HandleSWI。 ^ _ISR_STARTADDRESS HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4 現在我們知道軟中斷的服務程序跑到了內存中(執行速度較快)去了,我們可以編寫相應的代碼來實現不同功能號下的不同功能。
對于學習ARM的朋友來說,中斷處理是一塊硬骨頭,尤其是中斷向量表以及中斷的跳轉。下面主要對中斷向量表的建立問題及重映射問題進行探討。近來做一些東西用到中斷時,總會出現一些問題。 各種Bootloader的初始化相關代碼摘要,首先來看下面的程序: ^ DRAM_BASE //DRAM的基地址 HandleReset # 4 // 空留4個單元 HandleUndef # 4 HandleSwi # 4 HandlePrefetch # 4 //用于填充地址的 HandleAbort # 4 HandleReserv # 4 HandleIrq # 4 HandleFiq # 4 注: 這里的^是RMAP,#是FIELD,分配的意思 就是在SDARM的BANK0開始的地方定義了一個中斷向量表,相當于0地址的FLASH。空留單元用于存放中斷程序的入口地址。 ExceptionHandlerTable //實際的映射地址 DCD UserCodeArea DCD SystemUndefinedHandler DCD SystemSwiHandler DCD SystemPrefetchHandler DCD SystemAbortHandler DCD SystemReserv DCD SystemIrqHandler DCD SystemFiqHandler 這個表中存放的是匯編程序中中斷處理函數的入口地址,每一項對應一個中斷函數。這次的跳轉后就進入了C服務程序。 從初始化程序的開始處來看:(各種Bootloader的初始化代碼) AREA Init, CO ENTRY //入口 B Reset_Handler B Undefined_Handler //無條件的跳轉 B SWI_Handler B Prefetch_Handler B Abort_Handler NOP Reserved vector B IRQ_Handler B FIQ_Handler
FIQ_Handler SUB sp, sp, #4 STMFD sp!, {r0} FD滿遞減堆棧 執行寄存器壓棧操作. LDR r0, =HandleFiq 匯編里的處理函數地址,然后跳到C中,在DRAM。 LDR r0, [r0] 中斷向量地址給R0. STR r0, [sp, #4] 中斷向量地址給PC LDMFD sp!, {r0, pc} 稍微解釋一下: 首先執行了壓棧,然后給出了中斷入口地址.這個HandleFiq就是我們前面的在DRAM中建立的中斷向量其中一個的地址。 在HandleFiq開始的四個字節中,放著匯編中斷處理函數的入口地址。 那么匯編中斷處理函數的地址是如何放到DRAM中斷向量表里的呢? 上面的第一個表就起作用了。看下面這段程序: EXCEPTION_VECTOR_TABLE_SETUP LDR r0, =HandleReset LDR r1, =ExceptionHandlerTable MOV r2, #8
ExceptLoop LDR r3, [r1], #4 STR r3, [r0], #4 SUBS r2, r2, #1 //填充8個地址 BNE ExceptLoop //從表里取出來給了HandleReset后面的空間 這一段把ExceptionHandlerTable里的中斷處理函數的地址拷給了SDRAM里的中斷向量表。這樣兩者就聯系起來了。 在執行程序開始的跳轉之后就自然跳到了*****Handler.真正的處理函數如下: 它實際上只調用了C語言的中斷處理函數,其他什么也沒做。 SystemFiqHandler IMP STMFD sp!, {r0-r7, lr} BL ISR_FiqHandler //真正的中斷處理服務函數 LDMFD sp!, {r0-r7, lr} SUBS pc, lr, #4 它實際上只調用了C語言的中斷處理函數,其他什么也沒做。寫中斷處理服務程序其實就只寫C中相應的處理部分就好了。 void ISR_FiqHandler(void) { IntOffSet = (U32)INTOFFSET; (IntOffSet>>2) (*InterruptHandlers[IntOffSet>>2])(); // Call interrupt service routine } 其實就是將中斷向量表重映射,以提高中斷的響應速度。代碼在SDRAM的運行速度要比在FLASH中運行速度快。 |
|