官术网_书友最值得收藏!

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開始執行,且永不返回。到此,系統啟動初始化代碼的任務就完成了。

主站蜘蛛池模板: 会同县| 潜山县| 长武县| 仁怀市| 霍林郭勒市| 贵德县| 明光市| 阿坝县| 工布江达县| 抚顺县| 平湖市| 电白县| 天柱县| 阳城县| 稷山县| 保定市| 繁峙县| 黄陵县| 晋宁县| 东兰县| 德兴市| 锡林郭勒盟| 朔州市| 洪湖市| 凤台县| 嘉黎县| 瑞昌市| 斗六市| 曲松县| 临海市| 潼关县| 阿鲁科尔沁旗| 深州市| 鹿邑县| 白水县| 澎湖县| 四子王旗| 鹤庆县| 玉龙| 武平县| 新密市|