星期三, 4月 09, 2008

BootLoader 開發經驗談

BootLoader開發經驗談


作者:


何宗鍵,微軟Windows Embedded MVP,同濟大學軟件學院的講師。目前負責"微軟——同濟移動與嵌入式中心"的工作。主要興趣為基於Windows


CE的嵌入式系統與Windows Mobile移動設備。熟悉Platform Builder和Windows CE內核以及BSP開發。


原文地址: http://www.blogcn.com/User5/omale/blog/43575050.html


嵌入式操作系統在嵌入式軟件開發中佔的重要性越來越大。而提到嵌入式操作系統,Boot Loader恐怕是不可或缺的話題,無論是 Windows CE還是嵌入式Linux,抑或是其他的嵌入式操作系統。大多數都需要Boot Loader來加載操作系統。當我們拿到一塊新的板子,希望在上面運行某個嵌入式操作系統,通常第一件需要我們來做的事情就是移植該操作系統相對應的Boot Loader。而據有關文章分析,在嵌入式開發中,通常會在Boot Loader上消耗大量的時間人力和物力。



為什麼Bootloader的開發這麼複雜?為什麼作為嵌入式操作系統輔助而存在的Bootloader反而會佔據這麼多的開發時間?Bootloader複雜在什麼地方?我們該如何克服這些困難,去更快更好的開發 Bootloader?本文就以Windows CE這一廣泛應用的嵌入式操作系統為例,向讀者分析和解決上面提出來的問題。本文不會介紹 Bootloader的結構功能和啟動流程等信息,如果讀者對Windows CE的Bootloader不甚瞭解,希望補充這方面的知識,可以參考其他書籍。



1. Bootloader開發難點分析



(1) 硬件初始化:


當硬件上電之後,運行的第一條指令就是Bootloader的代碼(如果各式各樣的BIOS忽略不計的話)。Bootloader需要完成硬件初始化的一系列工作。然後才可以進入正常的邏輯,例如加載OS Image等。對於軟件工程師而言,硬件的初始化工作是很冗長乏味的。需要詳細閱讀各類硬件的資料規範。然後就是一系列的對寄存器的操作。雖然Bootloader中不需要初始化板子上的所有的硬件,而只需要初始化最基本的可以讓Loader正常工作的硬件就可以,有一些外設可以放到 OS啟動的時候,甚至驅動加載的時候再進行初始化不遲。即使是這樣,要初始化的硬件也不在少數,對於一個典型的ARM系統而言,有可能要做的事:初始化內存控制器,初始化MMU,配置GPIO口,配置調試串口,對RTC進行讀寫操作,如果要通過以太網下載,還需要驅動網絡接口……這一系列的工作,沒有一個不是體力活。需要細心的琢磨,很有可能對寄存器某一個bit的粗心設置,就會導致整個Bootloader無法工作。所以這一部分內容通常馬虎不得,需要耐心完成。



(2) 代碼編寫和構建:


由於Bootloader是最底層的代碼,彙編語言肯定是少不了的了。就連一些整天喊「彙編已死」的人也不能否認,系統啟動的那一段代碼,還是需要彙編語言的。而彙編語言通常也是軟件工程師們不太希望去碰的「硬骨頭」。這也增加了Bootloader編寫的困難。


代碼寫好之後,當然要編譯成機器碼才可以在板子上運行。目前的編譯器大多數隻會把代碼編譯成某些流行的可執行文件格式,例如Windows上的PE和 *Nix上的ELF等。這些帶有格式的可執行文件,也沒有辦法再目標設備上直接運行。所以,通常OS都會提供一些工具,把這些可執行文件去頭去尾,轉成純二進制格式,這樣才可以在目標設備上運行。例如ADS提供的FromELF工具與Windows CE提供的ROMImage工具就是完成這類工作的。通常,我們需要為這類工具做一些配置,例如告訴這些工具代碼段放在什麼地方,起始地址是多少等等。如果這些參數沒有配置正確,很有可能最終生成的 Bootloader的映像是不可用的。那麼燒寫到目標設備上,自然也就無法運行。同樣,對於這些參數的配置,也需要仔細檢查核對,一步步進行。



(3) 開發的效率:


Bootloader 的另外一個開發困難的原因是它的開發效率。通常當我們做了一些代碼修改之後,都需要把修改後的二進制文件使用燒寫工具燒寫到目標設備的Flash中,無論是NAND還是NOR Flash,燒寫的過程都不快。所以,即使是改了一行代碼,也需要經過編譯->燒寫->運行這樣一個完整的流程。一般而言再快也要10分鐘左右。這樣算算,一個鐘頭可以修改個5次代碼,一天可以修改個50次代碼就相當不錯了。機械的重複這一過程,經常會使開發人員感到開發效率低下,從而產生反感和牴觸情緒。這也是Bootloader開發的一大劣勢。一個解決的方法是使用硬件調試工具把Bootloader的映像直接灌到RAM裡面運行,往RAM裡面灌通常比燒寫Flash要快。但是這樣需要調試工具來初始化RAM,又有很多的其他邏輯上的和細節上的事情要做。



(4) 調試


上面說的幾大問題,其實還都是可以克服的問題。其實在我看來,開發Bootloader最大的問題還是調試問題。試想:無論彙編多麼難,我還是寫好了,無論燒寫多麼煩,我還是燒寫下去了,但是當我懷著激動的心情按下Reset鍵的時候,整個硬件設備毫無反應。我怎麼知道我的代碼寫的正確還是不正確呢?如果不正確,我又怎麼能定位到我的錯誤呢?現在的軟件開發中,無論是編譯型語言還是script,一般都會提供相應的Debugger,讓開發人員來定位代碼錯誤。「摸黑」寫代碼是軟件工程師們最害怕的事情。代碼出了問題,如果沒有行之有效的手段來做問題定位,十有八九會造成項目「卡殼」。如果定位準確,那麼問題也就解決了一大半了。所以歸根結底,還是調試的方法論問題。Bootloader中難以調試,是因為可以使用的手段非常少,也不常規。在OS下開發應用程序用到的那些調試手段手段,在Bootloader的開發中通常都用不上。需要有「非常」手段來調試。下面的專題,就向大家介紹一些 Bootloader的常用調試方法。



2. 一些調試技巧


(1) 硬件調試器


相信很多從應用開發走過來的開發人員,都會對Set Breakpoint,Step into, Step over等這些調試手段相當懷念。但是那些東西都是調試器在搞鬼。到了裸板上,可沒有Debugger來幫我們了。那怎麼辦?好在我們有硬件調試器。很多CPU體系結構都提供了相應的硬件調試器,例如ARM CPU的仿真器。借助硬件調試器,我們可以完成彙編級的Set Breakpoint,Step into, Step over這些操作。這對於調試Bootloader 來說,實在是太重要了。至少我們可以看到我們燒寫下去的代碼是否正確,然後可以看到是否在運行。這對於調試Reset後的第一段代碼來說,實在是雨後春筍一般的珍貴。


但是硬件調試器也有很多不足:首先,它們一般都價格不菲,不用國內自己的D的話,幾W是少不了了。其次,大多數硬件仿真器只能實現彙編級的調試,當代碼進入了C語言之後,硬件調試器就顯得力不從心了。第三,一劣質的仿真器,通常還無法對MMU打開後的虛擬內存進行仿真。所以限制也是很多的。



(2) 一閃一閃亮晶晶——LED燈


如果我們買不起硬件仿真器,或者手頭上根本就沒有。那麼只好通過硬件的一些輸出端口來輸出一些信息,讓我們知道代碼到底運行到哪裡了。在諸多的端口中,最簡單的恐怕就是LED燈了。一般而言,用個幾句彙編,就可以讓LED閃爍起來。這樣,我們就可以在代碼中安插讓LED閃爍的語句。來看我們的代碼到底在哪裡跑飛或者掛掉的。可惜LED等只能閃爍,想通過LED來獲得更多的消息,是很困難的。例如我們想知道某個變量的值,用LED的閃爍表示出來,恐怕就很傷腦筋。用LED來閃爍出摩爾斯代碼是一種可行方案,但是除了開開玩笑之外,恐怕真的去實踐的人是不存在的……


(3) 終於可以Printf了——調試串口


目前為止,在Bootloader甚至整個OS開發過程中,串口可能是使用最廣泛的調試端口了。它的設備簡約卻不簡單。用一根串口線,把PC跟目標設備連接起來,這樣我們可以通過串口來輸出一些字符,在PC一端接收。終於,在有OS的應用程序開發中最傳統的,最原始的調試方法printf就可以用了。在Bootloader的開發中,如果可以通過串口輸出調試信息,那調試的手段就前進了一大步了。至少我們可以通過printf來跟蹤目前代碼在哪個函數裡,分支語句走了哪個流程,某個變量的值到底是不是我們想要的……世界真美好。串口的問題是它不像LED那樣想用就用,還是需要初始化的。好多嵌入式CPU都把串口控制器集成到了裡面,甚至有一些會在CPU上電的時候自動初始化串口,這使得串口初始化相對簡單。但是如果有一些串口是外掛的控制器,那麼初始化一個串口有可能也需要耗費你半天時間。


(4) 走進新時代——內核調試器


隨著開發進一步複雜,串口輸出調試信息恐怕又不能滿足我們的要求了。首先,輸出的信息一多,很容易亂套。其次,輸出信息業是需要花費時間的。在中斷處理函數等一些時間敏感的地方使用串口輸出,有時候依然不明智。如果能把遠程調試器接上,那對BootLoader的調試跟對應用程序的調試就沒有什麼二致了(源碼級設置斷點,一步步跟,隨時看某些數據結構的值,都成了可能)。Linux提供了GDB,Windows CE提供了KD。但是這都需要實現相當多的工作,實現一些調試器Stub。GDB可能串口就可走。但CE的KD通常是要通過KITL,通過以太網的。雖然複雜,長遠來看還是非常有必要的,所以,當感覺其它手段力不從心的時候,可以考慮啟用更高級別的調試器了。


(5) 其它歪門邪道


如果我的板子上沒有硬件調試器,沒有LED,串口也不工作,更別提內核調試器了,那我怎麼辦?別著急,只要你的硬件有輸入輸出設備,我們都是可以想出辦法來的。只要能想辦法往輸出設備中輸出一點東西,並且可以通過一些手段得到這些輸出信息,我們調試的目的就達到了。例如:往喇叭裡面輸出一段雜音,往塊設備上寫一個扇區,往屏幕上畫一些亂七八糟的東西……代碼運行起來之後,只要不是當「瞎子」,都可以實現調試的目的。再走投無路的時候,這些手段不妨想想。



3. 讓Bootloader更完美



以前看過一篇文章說,開發一個好的Bootloader,其工作量可能跟開發一個簡單的操作系統相提並論。到這裡為止,我們才剛剛開始,後面還有很長的路要走。


沒有留言: