- 深入理解嵌入式Linux設備驅動程序
- 曹國輝 曾志鵬
- 1758字
- 2018-12-29 12:02:38
1.2 嵌入式ARM系統的啟動代碼分析
1.2.1 ARM上電啟動概述
ARM系統上電啟動后,從0x0地址處開始執行,根據系統的配置0x0地址可以映射在Nor FLASH或SDRAM中,在Nor FLASH或Nand FLASH中存放有系統的啟動初始化程序,與計算機的BIOS類似,完成系統最底層的初始化工作。
ARM系統上電后,首先就運行系統初始化程序,系統初始化程序主要完成系統最基本的硬件初始化,為后面的C語言應用程序提供運行環境。ARM系統初始化啟動代碼完成的主要功能如下:
· 初始化ARM CPU異常處理向量表;
· 禁止看門狗;
· 禁止中斷;
· 初始化系統時鐘,包括CPU主頻FCLK、系統總線時鐘頻率HCLK、外設總線時鐘頻率PCLK;
· 初始化SDRAM控制器;
· 設置ARM CPU在各種模式下的棧指針(棧頂);
·設置ARM中斷向量表,安裝中斷處理程序;
· 搬運可執行映像文件的RW段到RAM中,并初始化ZI段為0;
· 跳轉到C語言應用程序的Main函數,開始執行C語言應用程序。
到此,系統的初始化啟動程序就完成了ARM系統的啟動過程。
ARM系統的初始化啟動代碼一般用匯編語言編寫,根據以上的分析,我們知道ARM系統初始化啟動程序的流程如下圖所示。

1.2.2 ARM上電初始化啟動代碼分析
ARM系統的初始化啟動代碼源文件為2440init.s,是匯編語言源文件。下面對匯編啟動代碼的主要部分進行分析講解。
GET option.inc GET memcfg.inc GET 2440addr.inc
程序的開始是通過GET匯編偽指令設置該源碼文件要用到的一些頭文件的,其中option.inc定義ARM系統的一些配置選項,在這里主要定義中斷向量起始地址_ISR_STARTADDRESS和系統時鐘的分頻系數M_MDIV、M_PDIV、M_SDIV等。GET偽指令也可以用INCLUDE偽指令替代。
UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800~ SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800~ UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00~ AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000~ IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000~ FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000~
接下來程序定義ARM CPU在各種工作模式下的棧頂位置,在系統初始化程序的最后階段需要設置CPU在各種工作模式下的棧頂指針(SP寄存器),為C語言程序的運行做準備。
_STACK_BASEADDRESS定義在option.inc文件中,如下所示。
; Start address of each stacks, _STACK_BASEADDRESS EQU 0x33ff8000
其中以“; ”開始的行代表注釋。
MACRO $HandlerLabel HANDLER $HandleLabel $HandlerLabel sub sp, sp, #4 ; decrement sp(to store jump address) stmfdsp! , {r0} ; PUSH the work register to stack ldr r0, =$HandleLabel ; load the address of HandleXXX to r0 ldr r0, [r0] ; load the contents(service routine start address)of HandleXXX str r0, [sp, #4] ; store the contents(ISR)of HandleXXX to stack ldmfd sp! , {r0, pc} ; POP the work register and pc(jump to ISR) MEND
上面這段代碼定義了一個宏,該宏實現的主要功能是把中斷服務程序首地址HandleLabel裝載到指令寄存器(PC寄存器)中,當中斷產生時,系統能正確執行中斷服務處理程序。在后面有關的中斷系統處理的章節中,我們會詳細分析該宏的使用方法。
接下來我們通過IMPORT偽指令導入啟動代碼中需要用到的外部符號。
;declare ARM-linker internel self-define variable and Main IMPORT —Image$$RO$$Limit— ; End of ROM code(=start of ROM data) IMPORT —Image$$RW$$Base— ; Base of RAM to initialise IMPORT —Image$$ZI$$Base— ; Base and limit of area IMPORT —Image$$ZI$$Limit— ; to zero initialise IMPORT Main
Main為C語言程序的入口函數,也可以改成別的。
接下來才是真正系統初始化程序的開始。首先使用AREA偽指令定義代碼段,段名為Init,在鏈接時,通過鏈接選項把Init段鏈接到可執行映像文件的第一個段,如下所示。
AREA Init, CODE, READONLY ENTRY b ResetHandler b HandlerUndef ; handler for Undefined mode b HandlerSWI ; handler for SWI interrupt b HandlerPabort ; handler for PAbort b HandlerDabort ; handler for DAbort b . ; reserved b HandlerIRQ ; handler for IRQ interrupt b HandlerFIQ ; handler for FIQ interrupt ;@0x20 b . ; Must be@0x20.
ENTRY偽指令定義代碼段的入口。系統啟動程序的前32字節用來存放ARM異常向量表。當異常發生時,CPU自動跳轉到異常向量表處執行異常處理程序。當系統上電時,首先執行第一條指令“b ResetHandler”,該代碼通過一條跳轉指令跳到ResetHandler處執行復位處理程序。
代碼中“b.”表示跳到當前位置,“.”表示當前指令位置。
ResetHandler ldr r0, =WTCON ; watch dog disable ldr r1, =0x0 str r1, [r0] ldr r0, =INTMSK ldr r1, =0xffffffff ; all interrupt disable str r1, [r0] ldr r0, =INTSUBMSK ldr r1, =0x3ff ; all sub interrupt disable str r1, [r0] ;To reduce PLL lock time, adjust the LOCKTIME register ldr r0, =LOCKTIME ldr r1, =0xffffff str r1, [r0] ; Added for confirm clock divide. for 2440. set pll ; Setting value Fclk:Hclk:Pclk ldr r0, =CLKDIVN ldr r1, =CLKDIV_VAL ;3=1:2:4 str r1, [r0] ;Configure UPLL ldr r0, =UPLLCON ldr r1, =((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) str r1, [r0]
系統復位處理程序ResetHandler首先禁止看門狗,再禁止中斷,然后設置CPU系統時鐘和時鐘分頻系數,最后設置USB時鐘和相應的分頻系數。
設置好系統時鐘后,緊接著開始初始化SDRAM控制器,主要設置SDRAM的總線寬度和操作時序等。設置完SDRAM控制器后,SDRAM就可以正常工作了。
;Set memory control registers ldr r0, =SMRDATA ldr r1, =BWSCON ; BWSCON Address add r2, r0, #52 ; End address of SMRDATA 0 ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne %B0 ;Initialize stacks bl InitStacks
完成SDRAM控制器的設置后,通過調用InitStacks函數來初始化ARM CPU在各種工作模式下的棧指針(SP寄存器)。
InitStacks函數的工作流程比較簡單,首先設置CPU相應的工作模式,然后把在該工作模式下的棧指針寄存器SP的值設置為對應的棧頂地址即可。通過修改當前程序狀態寄存器CPSR來修改CPU的當前工作模式。InitStacks代碼如下所示。
InitStacks ;Don't use DRAM, such as stmfd, ldmfd… ;SVCstack is initialized before ;Under toolkit ver 2.5, 'msr cpsr, r1' can be used instead of' msr cpsr_cxsf, r1' mrs r0, cpsr bic r0, r0, #MODEMASK orr r1, r0, #UNDEFMODE—NOINT msr cpsr_cxsf, r1 ; UndefMode ldr sp, =UndefStack ; UndefStack=0x33FF_5C00 orr r1, r0, #ABORTMODE—NOINT msr cpsr_cxsf, r1 ; AbortMode ldr sp, =AbortStack ; AbortStack=0x33FF_6000 orr r1, r0, #IRQMODE—NOINT msr cpsr_cxsf, r1 ; IRQMode ldr sp, =IRQStack ; IRQStack=0x33FF_7000 orr r1, r0, #FIQMODE—NOINT msr cpsr_cxsf, r1 ; FIQMode ldr sp, =FIQStack ; FIQStack=0x33FF_8000 bic r0, r0, #MODEMASK—NOINT orr r1, r0, #SVCMODE msr cpsr_cxsf, r1 ; SVCMode ldr sp, =SVCStack ; SVCStack=0x33FF_5800 ;USER mode has not be initialized. mov pc, lr
系統棧初始化完成后,接下來進行代碼的重定位。所謂代碼的重定位,就是把應用程序的代碼段和數據段從加載地址位置搬到運行地址位置。在初始化代碼中,本例選擇從Nor FLASH啟動,代碼段的加載地址和運行地址一致,所以不需要進行代碼重定位。對于RW的系統全局初始化數據段需要進行重定位,要從ROM中的位置搬到RAM中去。系統重定位代碼如下所示。
;Copy and paste RW data/zero initialized data LDR r0, =—Image$$RO$$Limit— ; Get pointer to ROM data LDR r1, =—Image$$RW$$Base— ; and RAM copy LDR r3, =—Image$$ZI$$Base— ;Zero init base => top of initialised data CMP r0, r1 ; Check that they are different BEQ %F2 1 CMP r1, r3 ; Copy init data LDRCC r2, [r0], #4 ; -->LDRCC r2, [r0]+ADD r0, r0, #4 STRCC r2, [r1], #4 ; -->STRCC r2, [r1]+ADD r1, r1, #4 BCC %B1 2 LDR r1, =—Image$$ZI$$Limit—; Top of zero init segment MOV r2, #0 3 CMP r3, r1 ; Zero init STRCC r2, [r3], #4 BCC %B3
首先把全局初始化數據段搬運到RW運行地址位置,全局初始化數據段在ROM中的位置從RO_Limit開始,數據長度為ZI_Base - RW_Base。把全局初始化數據段搬運到RW段后,緊接著初始化ZI段數據為0, ZI段的起始地址為ZI_Base,結束地址為ZI_Limit。
本例中,程序運行后(即運行時域), RO段存于ROM中,RW段和ZI段存于RAM中,加載時域(程序還沒有執行)代碼段和RW數據段存于ROM中。本例中可執行映像文件的代碼重定位如下圖所示。

代碼重定位完成后,ARM系統的啟動初始化工作基本完成了,接著通過一條跳轉指令跳到C代碼部分,開始執行C語言應用程序代碼,如下所示。
; jump to Main , Main start execute bl Main b .
Main為C語言應用程序代碼的總入口函數,系統啟動代碼把控制權提交給Main后,Main開始執行,且永不返回。到此,系統啟動初始化代碼的任務就完成了。
- Windows Server 2012 Hyper-V:Deploying the Hyper-V Enterprise Server Virtualization Platform
- Google系統架構解密:構建安全可靠的系統
- Arch Linux Environment Setup How-to
- Python基礎教程(第3版)
- Windows Server 2019 Administration Fundamentals
- Microsoft Operations Management Suite Cookbook
- 嵌入式實時操作系統:RT-Thread設計與實現
- 云原生落地:產品、架構與商業模式
- RHCSARHCE 紅帽Linux認證學習指南(第7版)EX200 & EX300
- Windows 7使用詳解(修訂版)
- Heroku Cloud Application Development
- μC/OS-III內核實現與應用開發實戰指南:基于STM32
- Ubuntu Linux操作系統實用教程
- Linux應用大全 基礎與管理
- Learning Continuous Integration with Jenkins(Second Edition)