天天看點

基于ARM的嵌入式系統Bootloader啟動流程分析

一. 引言:

    對于PC機,其開機後的初始化處理器配置、硬體初始化等操作是由BIOS(Basic Input /Output System)完成的,但對于嵌入式系統來說,出于經濟性、價格方面的考慮一般不配置BIOS,是以我們必須自行編寫完成這些工作的程式,這就是所需要的開機程式。而在嵌入式系統中,通常并沒有像 BIOS 那樣的固件程式,啟動時用于完成初始化操作的這段代碼被稱為Bootloader程式,是以整個系統的加載啟動任務就完全由Bootloader 來完成。簡單地說,通過這段程式,可以初始化硬體裝置、建立記憶體空間的映射圖(有的CPU沒有記憶體映射功能如S3C44B0),進而将系統的軟硬體環境設定在一個合适的狀态,以便為最終調用作業系統核心、運作使用者應用程式準備好正确的環境。Bootloader依賴于實際的硬體和應用環境,是以要為嵌入式系統建立一個通用、标準的Bootloader是非常困難的。Bootloader也依賴于具體的嵌入式闆級裝置的配置,這也就是說,對于兩塊不同的嵌入式主機闆而言,即使它們是基于同一

CPU 而建構,要想讓運作在一塊闆子上的 Bootloader 程式也能運作在另一塊闆子上,通常都需要修改 Bootloader 的源程式。

二. 啟動流程

    系統加電複位後,幾乎所有的 CPU都從由複位位址上取指令。比如,基于 ARM7TDMI核心的CPU在複位時通常都從位址 0x00000000處取它的第一條指令。而以微處理器為核心的嵌入式系統通常都有某種類型的固态儲存設備(比如EEPROM、FLASH等)被映射到這個預先設定好的位址上。是以在系統加電複位後,處理器将首先執行存放在複位位址處的程式。通過內建開發環境可以将Bootloader定位在複位位址開始的存儲空間内,是以Bootloader是系統加電後、作業系統核心或使用者應用程式運作之前,首先必須運作的一段程式代碼。對于嵌入式系統來說,有的使用作業系統,也有的不使用作業系統,比如功能簡單僅包括應用程式的系統,但在系統啟動時都必須執行Bootloader,為系統運作準備好軟硬體運作環境。

     系統的啟動通常有兩種方式,一種是可以直接從Flash啟動,另一種是可以将壓縮的記憶體映像檔案從Flash(為節省Flash資源、提高速度)中複制、解壓到RAM,再從RAM啟動。當電源打開時,一般的系統會去執行ROM(應用較多的是Flash)裡面的啟動代碼。這些代碼是用彙編語言編寫的,其主要作用在于初始化CPU和闆上的必備硬體如記憶體、中斷控制器等。有時候使用者還必須根據自己闆子的硬體資源情況做适當的調整與修改。

    系統啟動代碼完成基本軟硬體環境初始化後,對于有作業系統的情況下,啟動作業系統、啟動記憶體管理、任務排程、加載驅動程式等,最後執行應用程式或等待使用者指令;對于沒有作業系統的系統直接執行應用程式或等待使用者指令。

    啟動代碼是用來初始化電路以及用來為進階語言寫的軟體做好運作前準備的一小段彙編語言,在商業實時作業系統中,啟動代碼部分一般被稱為闆級支援包,英文縮寫為BSP。它的主要功能就是:電路初始化和為進階語言編寫的軟體運作做準備。系統啟動流程如圖1所示,主要的過程如下:

    1. 啟動代碼的第一步是設定中斷和異常向量。

    2. 完成系統啟動所必須的最小配置,某些處理器晶片包含一個或幾個全局寄存器,這些寄存器必須在系統啟動的最初進行配置。

    3. 設定看門狗,使用者設計的部分外圍電路如果必須在系統啟動時初始化,就可以放在這一步。

    4. 配置系統所使用的存儲器,包括Flash,SRAM和DRAM等,并為他們配置設定位址空間。如果系統使用了DRAM或其它外設,就需要設定相關的寄存器,以确定其重新整理頻率,資料總線寬度等資訊,初始化存儲器系統。有些晶片可通過寄存器程式設計初始化存儲器系統,而對于較複雜系統通常內建有MMU來管理記憶體空間。

    5. 為處理器的每個工作模式設定棧指針,ARM處理器有多種工作模式,每種工作模式都需要設定單獨的棧空間。

    6. 變量初始化,這裡的變量指的是在軟體中定義的已經賦好初值的全局變量,啟動過程中需要将這部分變量從隻讀區域,也就是Flash拷貝到讀寫區域中,因為這部分變量的值在軟體運作時有可能重新指派。還有一種變量不需要處理,就是已經賦好初值的靜态全局變量,這部分變量在軟體運作過程中不會改變,是以可以直接固化在隻讀的Flash或EEPROM中。

    7. 資料區準備,對于軟體中所有未賦初值的全局變量,啟動過程中需要将這部分變量所在區域全部清零。

    8. 最後一步是調用進階語言入口函數,比如main函數等。

三. 程式分析

    下面根據實際經過測試的代碼詳細講述系統的啟動過程。

    .text      /*将此操作符開始的代碼編譯到代碼段或代碼段子段中*/

    /* 內建開發環境(IDE)可以通過連結腳本檔案将下面的語句定位在零起始位址,系統上電後CPU從此處開始執行*/

    ENTRY:

        b ResetHandler   /*跳至ResetHandler,此句被定位在零起始位址*/

/*除使用者模式外的其他6種模式稱為特權模式。特權操作模式主要處理異常和監控調用(有時稱為軟體中斷),它們可以自由的通路系統資源和改變模式。特權模式中除系統模式以外的5種模式又稱為異常模式,下面的代碼用于出現異常時CPU就會根據以下的語句自動跳轉到對應的異常處理程式處*/

    b HandlerUndef        /* handlerUndef         */

    b HandlerSWI         /* SWI interrupt handler  */

    b HandlerPabort       /* handlerPAbort        */

    b HandlerDabort       /* handlerDAbort       */

    b .                   /* handlerReserved      */

    b HandlerIRQ

    b HandlerFIQ

… ...

ResetHandler:  /*上電後跳轉到此處開始執行*/

    Ldr r0,=WTCON  /*禁止看門狗*/

    ldr     r1,=0x0

    str     r1,[r0]

    ldr     r0,=INTMSK    /*屏蔽所有中斷請求 */

    ldr     r1,=0x07ffffff

 /*設定時鐘控制寄存器*/

    ldr  r0,=LOCKTIME

    ldr  r1,=0xfff

    str  r1,[r0]

.if PLLONSTART

 ldr  r0,=PLLCON   /* 設定PLL */

 ldr  r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV) /*Fin=8MHz,Fout=64MHz*/

 str  r1,[r0]

.endif

    ldr     r0,=CLKCON

    ldr     r1,=0x7ff8      /*所有單元時鐘允許*/

/*為BDMA設定複位值*/

    ldr     r0,=BDIDES0

    ldr     r1,=0x40000000     /* BDIDESn 複位值應為 0x40000000 */

    ldr     r0,=BDIDES1

str     r1,[r0]

    /*設定存儲器控制寄存器,存儲器的配置資料都存儲在SMRDATA為起始位址的資料表中,下面的代碼可以一次将預先配置好的初始化資料存入與存儲器控制器相關的13個寄存器,這些寄存器則是以0x01c80000為起始位址的13個連續的32位寄存器*/

    ldr     r0,=SMRDATA

    ldmia   r0,{r1-r13}

    ldr     r0,=0x01c80000    /* BWSCON存儲控制寄存器位址 */

    stmia   r0,{r1-r13}

/*初始化堆棧*/

/* CPU複位後是處于管理模式下的,是以首先要初始化管理模式下的堆棧寄存器*/

    ldr     sp, =SVCStack

    /*由于處理器的每種運作模式都要有自己獨立的實體堆棧寄存器R13,在使用者應用程式的初始化部分,一般都要初始化每種模式下的R13,使其指向該運作模式的棧空間,這樣,當程式的運作進入異常模式時,可以将需要保護的寄存器放入R13所指向的堆棧,而當程式從異常模式傳回時,則從對應的堆棧中恢複,采用這種方式可以保證異常發生後程式的正常執行*/

     bl     InitStacks   /*跳轉至其它堆棧初始化程式并傳回*/

 /*設定IRQ中斷處理*/

/*44B0有兩種中斷模式:一種是沒有中斷向量表;一種是使用了中斷向量表,使用中斷向量表隻能是IRQ方式。當使用中斷向量表的時候,中斷發生時由S3C44B0的中斷控制器根據中斷向量表,利用硬體方式自動跳轉到相應的中斷處理服務程式所在的位置;不使用中斷向量表時按下面的代碼,利用軟體方式跳轉而進行中斷處理,因為S3C44B0有30個中斷源,是以需要程式判斷以确定調用那個中斷服務程式*/

    ldr     r0,=HandleIRQ /*如果在0x18和0x1c位址處無“subs pc,lr,#4”*/

    ldr     r1,=IsrIRQ  /*為了中斷正常傳回這些語句是必須的 */

 /*拷貝讀寫區域資料/資料區準備,将系統需要讀寫的資料和變量從ROM拷貝到RAM裡。 Image_RO_Limit、Image_RW_Base、Image_ZI_Base等這些符号還會在另外的連結腳本檔案中出現,這些符号是用來定位程式各個段的參考資訊。內建開發環境在編譯連結的時候會根據我們編寫的程式,把它們轉換成用來對各個段定位的位址資訊*/

    LDR     r0, =Image_RO_Limit /*取隻讀資料區域位址指針*/

    LDR     r1, =Image_RW_Base /*準備執行拷貝操作*/

    LDR     r3, =Image_ZI_Base

     CMP     r0, r1       /*檢查是否相同*/

    BEQ     F1        /*相同則跳過拷貝操作*/

F0:

    CMP     r1, r3    /*執行拷貝操作*/

    LDRCC   r2, [r0], #4

    STRCC   r2, [r1], #4

    BCC     F0

F1:

    LDR     r1, =Image_ZI_Base /*零資料準備區起始位址*/

    MOV     r2, #0

F2:

    CMP     r3, r1       /*執行資料區清零*/

    STRCC   r2, [r3], #4

    BCC     F2

 MRS r0, CPSR

 BIC r0, r0, #NOINT     /*中斷請求允許*/

 MSR CPSR_cxsf, r0

/* 跳轉到C入口程式 */

    BL Main

    B.

四. 總結:

    啟動過程中的初始化程式就是初始化CPU内部各個關鍵的寄存器、配置外圍硬體電路相關寄存器、建立中斷向量表等,然後跳轉到一般由進階語言編寫的主函數的應用程式代碼去執行,這樣就可以利用進階語言來編寫完成系統設計所要求的各種功能。初始化的過程對大多數初學者來說,比較難了解的是中斷的處理和一些少見的操作符号,這些符号多是一些宏定義或系統用于在記憶體空間中對各個段的定位辨別符号。掌握了S3C44B0的啟動代碼之後,對系統功能程式設計會起到很大的幫助,是進行下一步程式設計的基礎。