(linux)BSP板級支援包開發理解
1. 概述
嵌入式系統由硬體環境、嵌入式作業系統和應用程式組成,硬體環境是作業系統和應用程式執行的硬體平臺,它隨應用的不同而有不同的要求。硬體平臺的多樣性是嵌入式系統的主要特點,如何使嵌入式作業系統在不同的硬體平臺上有效地執行,是嵌入式系統開發中需要解決的關鍵問題。解決的方法是在硬體平臺和作業系統之間提供硬體相關層來遮蔽這些硬體的差異,給作業系統提供統一的執行環境,這種硬體相關層就是嵌入式系統中的板級支援包BSP(Board Support Package,簡稱BSP)。
2. BSP及其作用
BSP是嵌入式系統中介於硬體平臺和作業系統之間的中間層軟體,主要目的是為了遮蔽底層硬體的多樣性,根據作業系統的要求完成對硬體的直接操作,向作業系統提供底層硬體資訊並最終啟動作業系統。BSP具有硬體相關性和作業系統相關性的特點,其主要作用包括:
- 初始化底層硬體,為作業系統提供底層硬體資訊;
- 初始化相關硬體裝置,主要是儲存裝置、通訊裝置;
- 檢測系統硬體是否正常;
- 載入作業系統並啟動系統執行。
3. 嵌入式Linux系統BSP的實現
BSP是相對於作業系統而言的,不同的作業系統有不同定義形式的BSP,要求BSP所實現的功能也有所不同。在嵌入式Linux系統中,主要是初始化底層硬體並引導作業系統;同時,BSP又是和硬體相關的,還要考慮對硬體的初始化操作。
在不同的開發階段,因為核心和檔案系統所處的位置不同,BSP所要完成的工作也有所不同:
在開發除錯階段,BSP要能夠與主機通訊並從主機下載核心;
在目標產品中,BSP要能夠從非易失儲存裝置中載入核心。
3.1 開發除錯階段BSP的實現
開發初期由於除錯系統的需要,BSP需把核心和檔案系統從主機直接下載到目標板的記憶體中執行.
BSP要完成如下工作:
1. 硬體的初始化和配置
2. 通訊裝置的初始化
BSP需與主機通訊,從主機下載核心和檔案系統,因此要完成相應通訊裝置的初始化。與主機通訊的裝置一般是網絡卡和串列埠。
串列埠間通訊要遵循一定的協議,包括資料格式、同步方式、傳輸速率、糾錯方式等。對串列埠的初始化就是對這些協議進行設定,使通訊雙方處於相同的傳輸模式。在目標板上初始化串列埠是通過設定其暫存器實現的:設定串列埠的行控制暫存器確定串列埠接收資料的格式,設定串列埠的波特率產生暫存器確定串列埠接收資料的速率。設定串列埠的通訊協議為:八個資料位、一個停止位,沒有奇偶校驗位,9600波特率。串列埠初始化後就可以從其資料接收寄 存器中讀取資料。
對網絡卡初始化也是通過設定其暫存器實現的,設定控制暫存器,使網絡卡處於接收模式。用網絡卡與主機通訊時,主機端的通訊程式要知道目標板上網絡卡的MAC地址才能傳送資料。因此,我們要把網絡卡的MAC地址設定為指定值。
從網絡卡的資料接收暫存器讀取資料時要把資料包中的非資料資訊(包的狀態、長度、原地址、目的地址和型別)丟掉。
BSP從主機接收檔案,因此必須要提供主機與BSP通訊的程式。主機端的通訊程式可用作業系統提供的系統呼叫直接設定串列埠的屬性,使主機端串列埠的通訊協議與目標板串列埠的通訊協議一致。主機端的程式通過與目標板連線的串列埠線將資料寫到目標板串列埠的資料暫存器中。用網絡卡時,用原始套介面對網絡卡進行寫操作,把資料包傳送到目標板上網絡卡的資料接收快取暫存器中。
3. 從主機接收核心和檔案系統,啟動核心執行
系統加電時,BSP從位於0地址的非易失儲存器FLASH中執行,和執行在主機上的程式通訊,從串列埠或網絡卡的資料暫存器中讀取資料,把核心和檔案系統下載到記憶體中指定的位置,最後將CPU中的程式計數器PC置為核心在記憶體中的起始地址,實現核心啟動。但是,程式在FLASH中執行時不能對變數進行寫操作,為了使程式能正確執行,BSP必須將自己重定位(即把自己搬運到)到記憶體中,並且在進入c語言函式執行前要設定好堆疊指標。
其主要實現過程的偽碼如下:
硬體(cup、記憶體等)初始化;
通訊裝置(網絡卡、串列埠)初始化;
將自己重定位到記憶體中;
設定系統的堆疊指標;
跳轉到從串列埠讀取核心和檔案系統的函式;
從串列埠讀取核心和檔案系統的函式(void)
{
while(核心沒讀取完){
while(串列埠的接收資料暫存器為空)(等待);
從串列埠的資料暫存器讀取資料到記憶體中;
核心大小減去已讀取的大小,確定核心是否讀取完;
if(核心讀取完){
-asm{mov pc,一核心在記憶體中的起始地址;}
}
}
3.2. 目標產品中BSP的實現
3.2.1 BSP獨立實現
把核心和檔案系統直接從主機下載到記憶體中執行,只適用於開發除錯階段。目標產品中核心和檔案系統燒寫在非易失性儲存裝置上,因此BSP要從這些裝置中載入並啟動核心。此時,BSP不需要與主機通訊,可以將其單獨實現在產品中。將BSP單獨實現時,可以根據需要向其中靈活地新增多種功能:啟動核心前檢測記憶體是否能被正確讀寫,通過判斷網絡卡、音效卡等硬體的屬性暫存器確定硬體裝置是否正常等。
此時,BSP要完成的工作如下:
- 初始化硬體及儲存裝置。
- 測試硬體裝置是否正常。
- 從相應的儲存裝置中載入核心到記憶體中,並啟動核心.
硬體的初始化和配置與前面相同,主要完成CPU和記憶體的初始化。此時,BSP要從儲存裝置中載入並啟動核心,因此要對儲存裝置(一般是FLASH或CF卡)進行初始化,使其能被正確定址。BSP中讀取核心的程式碼與具體的作業系統及檔案格式無關,不能從檔案系統層把核心作為一個檔案讀進來,只能從硬體介面來實現具體的操作,把核心從儲存裝置讀入記憶體,然後把核心的開頭當作一段程式的起點,使CPU轉入核心執行.
非易失性儲存器FLASH和記憶體統一定址,對它的訪問和訪問記憶體是一樣的,可以利用暫存器將核心直接從FALSH讀取到記憶體。CF卡相對記憶體來說屬於外設,對其進行讀取操作是通過控制其暫存器(資料暫存器、狀態與控制暫存器)來實現的,向其控制暫存器釋出ATA命令OA何處讀取,讀取資料的大小等)後,判斷其狀態暫存器是否準備好,才能從其資料暫存器中讀取資料到記憶體中。把核心從非易失性儲存裝置讀到記憶體中後,將CPU中的程式計數器PC置為核心在記憶體中的起始地址,實現系統的啟動。
對應的偽碼如下:
硬體(cup、記憶體等)初始化;
儲存裝置(FLASH、CF卡)初始化;
測試硬體裝置是否正常;
根據CF卡的屬性暫存器判斷CF卡是否存在;
如果存在,跳轉到從CF卡載入核心到記憶體的函式;
如果不存在,直接將核心從FLASH載入到記憶體中;
mov pc,一核心在記憶體中的起始地址;
從CF卡載入核心到記憶體的函式(void)
{
while(核心沒載入完){
向CF卡釋出ATA命令,確定從CF卡的哪一塊開始讀,
讀多少塊
while(CF卡的狀態沒準備好){等待;}
從CF卡的資料暫存器讀資料到記憶體中;
核心大小減去所讀的塊數乘以塊大小;
if(核心載入完){
-asm{mow pc,=核心在記憶體中的起始地址;}
}
}
3.2.2 在核心中實現BSP
BSP單獨實現易於修改,當硬體改變時,只需要對相關硬體的初始化程式碼進行修改並重新編譯。但是,對於嵌入式系統的儲存介質FLASH,有些檔案系統對它的分割槽有位元組對齊性的要求,也就是說分割槽必須是特定大小或特定大小的倍數才能有寫訪問許可權,如果把BSP單獨寫在一個分割槽中會造成儲存空間的浪費。實質上,BSP是屬於作業系統的一部分且和作業系統繫結在一起執行,在開發板硬體固定的情況下,可以將其實現在Linux核心中。在核心中實現BSP時,BSP應能向核心提供必要的底層硬體資訊,實現核心從非易失性儲存器FLASH到記憶體的載入和啟動。
在嵌入式系統的儲存介質FLASH中沒有所謂的引導扇區,相應的嵌入式Linux核心映象zImage也沒有如PC機上的引導扇區程式碼bootsect及輔助程式碼setup,而是由head.o、misc.o、head-xscale.o和piggy.o幾個檔案順序連線而成。其中,head.o是由/arch/arm/boot/compressed/head.S彙編而成,是核心最先執行的程式碼,主要作用是用misc.o解壓縮核心並使CPU轉到核心解壓縮後所在的記憶體地址執行。head-xscale.o是體系結構相關的程式碼。piggy.o是系統啟動後保留在記憶體中的全部有用程式[3],也就是head.o要解壓縮的程式碼。由以上分析可知,嵌入式Linux的核心不能自啟動,要啟動核必須滿足以下兩個條件:
- 系統硬體已被正確初始化;
- 核心在記憶體中,並且CPU中的程式計數器被置為核心在記憶體中的起始地址。
在核心中實現BSP時,BSP必須位於核心程式碼的最前面,即將其實現在head.S檔案的最前面,完成如PC 機上的BIOS、bootsect和setup的功能。同時,要保證核心在載入到記憶體後必須能跳轉到和沒有BSP時核心中相同的地方執行。重新編譯核心後,就將BSP實現在核心映象zImage中的開始程式碼中。把核心燒到FLASH的0地址,系統啟動時首先執行上述程式碼,將核心載入到記憶體中執行,實現核心的自啟動。