Boot Loader

1 Boot Loader概述

簡單地說,在操作系統內核運行之前,通過一小程序,可以初始化硬件設備、建立內存空間的映射圖等,從而將系統的軟硬件環境帶到一個合適的狀態,以便爲最終調用操作系統內核配置好相應的環境,也可以下載文件到系統板上的SDRAM,對Flash進行擦除與編程,這個小程序一般稱爲Boot Loader。可以說,一個功能完善的Boot Loader已經相當於一個微型的操作系統了。

Boot Loader作爲系統復位或上電後首先運行的代碼,一般應寫入Flash存儲器並從起始物理地址0x0開始。Boot Loader是非常依賴於硬件而實現的,而且根據實現的功能不同,其複雜程度也各不相同。一個簡單的Boot Loader可以只完成USB口的初始化,而功能完善的Boot Loader可以支持比較複雜的命令集,對系統的軟硬件資源進行合理的配置與管理。因此,建立一個通用的Boot Loader幾乎是不可能的。

儘管Boot Loader的功能各不相同,我們仍然可以對Boot Loader歸納出一些通用的概念來,以指導用戶對特定要求的Boot Loader設計與實現。Boot Loader一般應包括系統初始化過程,然後用戶根據其系統的自身需求,具體設計。

系統初始化代碼直接對ARM微處理器內核及硬件控制器編程,多采用彙編語言編程,初始化代碼一般應包括如下典型任務:

1.       定義程序入口點;

2.       設置異常和中斷向量表;

3.       初始化存儲設備;

4.       初始化堆棧指針寄存器;

5.       初始化用戶執行環境;

6.       呼叫主應用程序。

1.1 定義程序入口

    初始化代碼必須定義整個程序的入口點。通過僞指令Entry指定編譯器保留該段代碼,同時配合鏈接器的設置,確定整個程序的入口點。

1.2 設置異常和中斷向量表

異常和中斷處理是嵌入式系統的重要核心部分。它們負責處理錯誤、中斷和其它由外部系統觸發的事件。

ARM要求異常向量表必須放置在從0地址開始,連續32(8X4)字節的空間內,各異常的位置如表1所示。


每當一個異常發生以後,系統執行完當前指令後,ARM微處理器會執行以下幾步操作:

(1)          把cpsr保存到相應異常模式下的spsr;

(2)          把pc保存到相應異常模式下的lr;

(3)          設置cpsr爲相應的異常模式(異常及其對應的模式見圖1);


 

(4)          設置pc爲相應異常處理程序的入口地址。

 

因爲每個異常只佔據向量表中1個字的存儲空間,只能放置一條ARM指令,所以該指令必須使程序跳轉到存儲器存儲異常處理程序的地方,再執行異常處理程序。

執行完異常處理程序,要從異常中斷處理程序中返回。

從異常中斷處理程序中返回包括下面兩個基本操作:

(1)          恢復被屏蔽的程序的處理器狀態

(2)          返回到發生異常中斷的指令的下一條指令處繼續執行。

當異常發生時,程序計數器PC所指的位置對於各種不同的異常是不同的,同樣,返回地址對於各種不同的異常中斷也是不同的。例外的是,復位異常中斷處理程序不需要返回,因爲整個應用系統是從復位異常中斷處理程序開始執行的。

總的來說,異常處理過程爲:當異常發生時,系統執行完當前指令後,將跳轉到相應的異常處理程序處執行。當異常處理程序執行完成後,程序返回到發生異常的指令的下一條指令處執行。在進入異常處理程序時,要保存被中斷的程序的執行現場。從異常處理程序退出時,要恢復被中斷的程序的執行現場。

 

IRQ和FIQ異常是用來處理外圍設備中斷的,不同的ARM芯片生產商,生產的芯片會有所不同。

1.3初始化存儲設備

初始化存儲器系統主要是對系統存儲器控制器的初始化,如果系統具有存儲器管理單元,也必須對其進行初始化。

ARM微處理器架構的理論尋址能力爲4GB,但是對於一個特定的系統來說,所配備的實際物理存儲器遠沒有那麼多。出於對芯片的面積、成本、使用靈活性等方面的考慮,這麼大容量的存儲器如果設計在芯片內部是不能被接受的。因此,基於ARM微處理器的系統一般都需要外擴大容量的存儲器,這些存儲器是由專門的存儲器控制器控制的。

因爲存儲器控制器並非標準ARM微處理器架構的一部分,所以不同的ARM微處理器,設計也會各不相同,初始化代碼自然也會有一些差異,但一般包含如下兩個方面:

1.       存儲器類型和時序的配置

ARM微處理器一般都設計有多種類型的存儲器接口,需要根據具體的系統設計加以正確配置,且對於同在一種類型的存儲器,也會因爲訪問速度的差異進行不同的時序設置。

存儲器接口時序優化是非常重要的,這會影響到整個系統的性能。因爲一般系統運行的速度瓶頸都存在於存儲器訪問,所以存儲器訪問時序應儘可能的快;而同時又要考慮到由此帶來的穩定性問題,應根據不同的情況來配置。一般一個特定的系統,需要進行多次測試,才能確定最佳的時序配置。

2.       存儲器的地址分配與地址重映射

ARM微處理器架構的理論尋址能力爲4GB,但是對於一個特定的系統來說,所配備的實際物理存儲器遠沒有那麼多。因此,存儲器的地址分配也就是將物理存儲器定位在4GB地址空間的哪個具體位置。

    ARM微處理器通常採用兩種方式來完成地址分配:一種方式採用固定的存儲器地址分配,即物理存儲器的起始地址是固定的;另一種方式採用存儲器重映射的手段,使物理存儲器的起始地址可以在有效的地址空間內任意改變,即物理存儲器的地址是不固定的。

    基於固定存儲器地址分配的ARM微處理器系統,當系統的硬件設計完成以後,物理存儲器地址也就確定了,在軟件設計的程序流程中考慮就簡單一些。而對於具有存儲器地址重映射功能的系統,這個過程就複雜的多。

    一種典型的存儲器地址重映射過程描述如下:當系統上電或復位以後,PC指針指向0x0,程序從0x0地址開始執行,因此,爲了能正確讀取代碼,要求此時Flash(或其它類型的ROM)的起始地址爲0x0。但Flash(或其它類型的ROM)的訪問速度大大低於RAM,每次產生異常後,都要從Flash(或其它類型的ROM)的異常向量表調轉到相應的處理程序,會影響異常的響應速度,因此,系統便提供一種靈活的地址重映射方法,在系統完成必要地初始化以後,將RAM安排到0x0地址處,而將原來位於0x0處的Flash(或其它類型的ROM)安排到其他的地方上去,加快異常的響應速度。

    這個過程中最容易出錯的地方是如何保證程序執行流程的連續性。因爲PC指針最初在Flash裏取指令執行,在進行地址重映射以後,Flash(或其它類型的ROM)被安排到其他地址上去了,而當前地址被安排爲RAM,如果事先沒有對RAM的內容進行正確地設置,在往下取指令執行就會出錯,即程序的連續性被存儲器地址重映射這種變化所打斷。

    常用的處理方法是:先將Flash(或其它類型的ROM)的內容全部複製到RAM中,然後再進行地址重映射。此時儘管Flash(或其它類型的ROM)和RAM的物理地址發生了變化,但由於RAM中的內容與原來的Flash(或其它類型的ROM)是一樣的,PC指針就可以繼續取得正確地指令執行,從而保證了程序流程的連續性。

1.4 初始化堆棧

因爲ARM有7種執行狀態,每一種狀態的堆棧指針寄存器(SP)都是獨立的(注意System和User模式使用同一個SP)。所以,對程序中需要用到的每一種模式都要給SP定義一個堆棧地址。方法是改變狀態寄存器內的狀態位,使處理器切換到不同的狀態,然後給SP賦值。注意:不要切換到User模式進行User模式的堆棧設置,因爲進入User模式後就不能再操作CPSR回到別的模式了,可能會對接下去的程序執行造成影響。

1.5 初始化用戶執行環境

主要包括初始化臨界I/O設備,初始化應用程序執行環境,改變處理器的運行模式和狀態,使能中斷4部分。

初始化臨界I/O設備。臨界I/O設備是指哪些使能中斷之前必須進行初始化的設備。如果不對這些設備進行必要的初始化,它們可能會在使能中斷後產生一些沒有意義的中斷請求,從而影響程序的運行。

初始化應用程序執行環境。程序代碼通過編譯、鏈接後生成可執行映像文件,一個ARM映像文件由RO,RW和ZI三個段組成,其中RO爲代碼段,RW是已初始化的全局變量,ZI是未初始化的全局變量。映像一開始總是存儲在ROM/Flash裏面的,其RO部分即可以在ROM/Flash裏面執行,也可以轉移到速度更快的RAM中執行;而RW和ZI這兩部分是必須轉移到可寫的RAM裏去。所謂應用程序執行環境的初始化,就是完成必要的從ROM到RAM的數據傳輸和內容清零。

改變處理器的運行模式和狀態。ARM微處理器在復位或上電狀態下的默認模式爲系統管理模式,而在初始化代碼中可能需要切換到其它模式進行必要的操作,如初始化各個模式下的堆棧指針寄存器。因此,在系統的初始化過程中處理器模式一般會經歷如圖2所示的變化。同時,ARM微處理器在復位後總是處於ARM狀態,對於兼容Thumb指令的處理器如果應用程序的入口點對應Thumb指令,則必須將微處理器切換到Thumb狀態。


使能中斷。如果系統需要使用中斷,初始化代碼應該使能中斷。中斷使能可以通過清除CPRS中的中斷禁止位來完成。

1.6 呼叫主應用程序

當所有的系統初始化工作完成之後,就需要把程序流程轉入主應用程序。如果主應用程序是由C代碼編寫,可以通過以下兩種方式進入C代碼運行,最簡單的情況如下:

 

IMPORT         C_Entry       ;定義一個外部標號,最好不使用main

B              C_Entry       ;跳轉到該處執行

 

在ARM的ADS編譯環境中,還另外提供了一種進入C代碼的機制:

 

IMPORT         __main

B              __main

 

__main()是編譯器提供的一個函數,負責完成庫函數的初始化和對C代碼運行環境的初始化,最後自動調轉到main()函數執行,此時應用程序的主函數名必須是main()。

用戶可以根據需要選擇是否使用main()函數,如果想讓系統自動完成初始化過程,可以使用main()函數;如果所有的初始化過程都由用戶自己完成,則不使用main()。
[]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章