與傳統(tǒng)的4/8位單片機(jī)相比,ARM的性能和處理能力當(dāng)然是遙遙領(lǐng)先的,但與之相應(yīng),ARM的系統(tǒng)設(shè)計(jì)復(fù)雜度和難度,較之傳統(tǒng)的設(shè)計(jì)方法也大大提升了。本文旨在通過(guò)討論系統(tǒng)程序設(shè)計(jì)中的幾個(gè)基本方面,來(lái)說(shuō)明基于ARM的嵌入式系統(tǒng)程序開發(fā)的一些特點(diǎn),并提出和解決了一些常見的問(wèn)題。 本文分成幾個(gè)相對(duì)獨(dú)立的專題陸續(xù)刊載。 (一) 嵌入式程序開發(fā)基本概念 (二) 系統(tǒng)的初始化過(guò)程 (三) 如何滿足嵌入式系統(tǒng)的靈活需求 (四) 異常處理機(jī)制的設(shè)計(jì) (五) ARM/Thumb的交互工作 (六) 開發(fā)高效程序的技巧 1 嵌入式程序開發(fā)過(guò)程 不同于通用計(jì)算機(jī)和工作站上的軟件開發(fā)工程,一個(gè)嵌入式程序的開發(fā)過(guò)程具有很多特點(diǎn)和不確定性。其中最重要的一點(diǎn)是軟件跟硬件的緊密耦合特性。 圖1是兩類簡(jiǎn)化的嵌入式系統(tǒng)層次結(jié)構(gòu)圖。由于嵌入式系統(tǒng)的靈活性和多樣性,圖1中各個(gè)層次之間缺乏統(tǒng)一的標(biāo)準(zhǔn),幾乎每一個(gè)獨(dú)立的系統(tǒng)都不一樣。這樣就給上層的軟件設(shè)計(jì)人員帶來(lái)了極大地困難。第一,在軟件設(shè)計(jì)過(guò)程中過(guò)多地考慮硬件,給開發(fā)和調(diào)試都帶來(lái)了很多不便;第二,如果所有的軟件工作都需要在硬件平臺(tái)就緒之后進(jìn)行,自然就延長(zhǎng)了整個(gè)的系統(tǒng)開發(fā)周期。這些都是應(yīng)該從方法上加以改進(jìn)和避免的問(wèn)題。 ![]() 圖1 兩類不同的嵌入式系統(tǒng)結(jié)構(gòu)模型 為了解決這個(gè)問(wèn)題,工程和設(shè)計(jì)人員提出了許多對(duì)策。首先在應(yīng)用與驅(qū)動(dòng)(或API)這一層接口,可以設(shè)計(jì)成相對(duì)統(tǒng)一的一些接口函數(shù),這對(duì)于具體的某一個(gè)開發(fā)平臺(tái)或在某個(gè)公司內(nèi)部,是完全做得到的。這樣一來(lái),就大大提高了應(yīng)用層軟件設(shè)計(jì)的標(biāo)準(zhǔn)化程度,方便了應(yīng)用程序在跨平臺(tái)之間的復(fù)用和移植。 對(duì)于驅(qū)動(dòng)/硬件抽象這一層,因?yàn)橹苯域?qū)動(dòng)硬件,其標(biāo)準(zhǔn)化變得非常困難甚至不太可能。但是為了簡(jiǎn)化程序的調(diào)試和縮短開發(fā)周期,我們可以在特定的EDA工具環(huán)境下面進(jìn)行開發(fā),通過(guò)后再移植到硬件平臺(tái)上。這樣既可以保證程序邏輯設(shè)計(jì)的正確性,同時(shí)使得軟件開發(fā)可行甚至超前于硬件開發(fā)進(jìn)程。 我們把脫離于硬件的嵌入式軟件開發(fā)階段稱之為“PC軟件”的開發(fā),可以用圖2來(lái)示意一個(gè)嵌入式系統(tǒng)程序的開發(fā)過(guò)程。 ![]() 圖2 嵌入式系統(tǒng)程序的開發(fā)過(guò)程 在“PC軟件”開發(fā)階段,可以用軟件仿真,即指令集模擬的方法,來(lái)對(duì)用戶程序進(jìn)行驗(yàn)證。在ARM公司的開發(fā)工具中,ADS內(nèi)嵌的ARMulator和 RealView開發(fā)工具中的ISS,都提供了這項(xiàng)功能。在模擬環(huán)境下,用戶可以設(shè)置ARM處理器的型號(hào)、時(shí)鐘頻率等,同時(shí)還可以配置存儲(chǔ)器訪問(wèn)接口的時(shí)序參數(shù)。程序在模擬環(huán)境下運(yùn)行,不但能夠進(jìn)行程序的運(yùn)行流程和邏輯測(cè)試,還能夠統(tǒng)計(jì)系統(tǒng)運(yùn)行的時(shí)鐘周期數(shù)、存儲(chǔ)器訪問(wèn)周期數(shù)、處理器運(yùn)行時(shí)的流水線狀態(tài)(有效周期、等待周期、連續(xù)和非連續(xù)訪問(wèn)周期)等信息。這些寶貴的信息是在硬件調(diào)試階段都無(wú)法取得的,對(duì)于程序的性能評(píng)估非常有價(jià)值。 為了更加完整和真實(shí)地模擬一個(gè)目標(biāo)系統(tǒng),ARMulator和ISS還提供了一個(gè)開放的API編程環(huán)境。用戶可以用標(biāo)準(zhǔn)C來(lái)描述各種各樣的硬件模塊,連同工具提供的內(nèi)核模塊一起,組成一個(gè)完整的“軟”硬件環(huán)境。在這個(gè)環(huán)境下面開發(fā)的軟件,可以更大程度地接近最終的目標(biāo)。 利用這種先進(jìn)的EDA工具環(huán)境,極大地方便了程序開發(fā)人員進(jìn)行嵌入式開發(fā)的工作。當(dāng)完成一個(gè)“PC軟件”的開發(fā)之后,只要進(jìn)行正確的移植,一個(gè)真正的嵌入式軟件就開發(fā)成功了。而移植過(guò)程是相對(duì)比較容易形成一套規(guī)范的流程的,其中三個(gè)最重要的方面是: ◆ 考慮硬件對(duì)庫(kù)函數(shù)的支持 ◆ 符合目標(biāo)系統(tǒng)上的存儲(chǔ)器資源分布 ◆ 應(yīng)用程序運(yùn)行環(huán)境的初始化 2 開發(fā)工具環(huán)境里面的庫(kù)函數(shù) 如果用戶程序里調(diào)用了跟目標(biāo)相關(guān)的一些庫(kù)函數(shù),則在應(yīng)用前需要裁減這些函數(shù)以適合在目標(biāo)上允許的要求。主要需要考慮以下三類函數(shù): ◆ 訪問(wèn)靜態(tài)數(shù)據(jù)的函數(shù) ◆ 訪問(wèn)目標(biāo)存儲(chǔ)器的函數(shù) ◆ 使用semihosting(半主機(jī))機(jī)制實(shí)現(xiàn)的函數(shù) 這里所指的C庫(kù)函數(shù),除了ISO C標(biāo)準(zhǔn)里面定義的函數(shù)以外,還包括由編譯工具提供的另外一些擴(kuò)展函數(shù)和編譯輔助函數(shù)。 2.1 裁減訪問(wèn)靜態(tài)數(shù)據(jù)的函數(shù) 庫(kù)函數(shù)里面的靜態(tài)數(shù)據(jù),基本上都是在頭文件里面加以定義的。比如CTYPE類庫(kù)函數(shù),其返回值都是通過(guò)預(yù)定義好的CTYPE屬性表來(lái)獲得的。比如,想要改變isalpha() 函數(shù)的缺省判斷,則需要修改對(duì)應(yīng)CTYPE屬性表里對(duì)字符屬性的定義。 2.2 裁減訪問(wèn)目標(biāo)存儲(chǔ)器的函數(shù) 有一類動(dòng)態(tài)內(nèi)存管理函數(shù),如malloc() 等,其本身是獨(dú)立于目標(biāo)系統(tǒng)而運(yùn)行的;但是它所使用的存儲(chǔ)器空間需要根據(jù)目標(biāo)來(lái)確定。所以malloc() 函數(shù)本身并不需要裁減或移植,但那些設(shè)置動(dòng)態(tài)內(nèi)存區(qū)(地址和空間)的函數(shù)則是跟目標(biāo)系統(tǒng)的存儲(chǔ)器分布直接相關(guān)的,需要進(jìn)行移植。例如堆棧的初始化函數(shù) __user_initial_stackheap(),是用來(lái)設(shè)置堆(heap)和棧(stack)地址的函數(shù)。顯然,針對(duì)每一個(gè)具體的目標(biāo)平臺(tái),該函數(shù)都需要根據(jù)具體的目標(biāo)存儲(chǔ)器資源進(jìn)行正確移植。 下面是對(duì)示例函數(shù)__user_initial_stackheap() 進(jìn)行移植的一個(gè)例子: __value_in_regs struct __initial_stackheap __user_initial_stackheap( unsigned R0, unsigned SP, unsigned R2, unsigned SL) { struct __initial_stackheap config; config.heap_base = (unsigned int) 0x11110000; // config.stack_base = SP; // optional return config; } 請(qǐng)注意上面的函數(shù)體并不完全遵循標(biāo)準(zhǔn)C的關(guān)鍵字和語(yǔ)法規(guī)范,使用了ARM公司編譯器(ADS或RealView Compilation tool) 里的C語(yǔ)言擴(kuò)展特性。關(guān)于編譯器特定的C語(yǔ)言擴(kuò)展,請(qǐng)參考相關(guān)的編譯器說(shuō)明,這里簡(jiǎn)單介紹函數(shù)__user_initial_stackheap() 的功能,它主要是返回堆和棧的基地址。上面的程序中只對(duì)堆(heap) 的基地址進(jìn)行了設(shè)置(設(shè)成了0x11110000),也就是說(shuō)用戶把0x11110000開始的存儲(chǔ)器地址用作了動(dòng)態(tài)內(nèi)存分配區(qū)(heap區(qū))。具體地址的確定是要由用戶根據(jù)自己的目標(biāo)系統(tǒng)和應(yīng)用情況來(lái)確定的,至少要滿足以下條件: ◆ 0x11110000開始的地址空間有效且可寫(是RAM) ◆ 該存儲(chǔ)器空間不與其它功能區(qū)沖突(比如代碼區(qū)、數(shù)據(jù)區(qū)、stack區(qū)等) 因?yàn)開_user_initial_stackheap() 函數(shù)的全部執(zhí)行效果就是返回一些數(shù)值,所以只要符合接口的調(diào)用標(biāo)準(zhǔn),直接用匯編來(lái)實(shí)現(xiàn)看起來(lái)更加直觀一些: EXPORT __user_initial_stackheap __user_initial_stackheap LDR r0,0x11110000 MOV pc,lr 如果不對(duì)這個(gè)函數(shù)進(jìn)行移植,編譯過(guò)程中將使用缺省的設(shè)置,這個(gè)設(shè)置適用于ARM公司的Integrator系列平臺(tái)。 2.3 裁減使用半主機(jī)機(jī)制實(shí)現(xiàn)的函數(shù) 庫(kù)函數(shù)里有一大部分函數(shù)是涉及到輸入/輸出流設(shè)備的,比如文件操作函數(shù)需要訪問(wèn)磁盤I/O,打印函數(shù)需要訪問(wèn)字符輸出設(shè)備等。在嵌入式調(diào)試環(huán)境下,所有的標(biāo)準(zhǔn)C庫(kù)函數(shù)都是有效且有其缺省行為的,很多目標(biāo)系統(tǒng)硬件不能支持的操作,都通過(guò)調(diào)試工具來(lái)完成了。比如printf() 函數(shù),缺省的輸出設(shè)備是調(diào)試器里面的信息輸出窗口。 但是一個(gè)真實(shí)的系統(tǒng)是需要脫離調(diào)試工具而獨(dú)立運(yùn)行的,所以在程序的移植過(guò)程當(dāng)中,需先對(duì)這些庫(kù)函數(shù)的運(yùn)行機(jī)制作一了解。 圖3說(shuō)明了在ADS下面這類C庫(kù)函數(shù)的結(jié)構(gòu)。 ![]() 圖3 C庫(kù)函數(shù)實(shí)現(xiàn)過(guò)程中的層次調(diào)用 如圖4中例子所示,函數(shù)printf() 最終是調(diào)用了底層的輸入/輸出函數(shù)_sys_write() 來(lái)實(shí)現(xiàn)輸出操作的,而_sys_write() 使用了調(diào)試工具的內(nèi)部機(jī)制來(lái)把信息輸出到調(diào)試器。 ![]() 圖4 printf() 的調(diào)試過(guò)程 顯然這樣的函數(shù)調(diào)用過(guò)程在一個(gè)真實(shí)的嵌入式系統(tǒng)里是無(wú)法實(shí)現(xiàn)的,因?yàn)楠?dú)立運(yùn)行的嵌入式系統(tǒng)將不會(huì)有調(diào)試器的參與。如果在最終系統(tǒng)中仍然要保留 printf() 函數(shù),而且在系統(tǒng)硬件中具備正確的輸出設(shè)備(如LCD等),則在移植過(guò)程中,需要把printf() 調(diào)用的輸出設(shè)備進(jìn)行重新定向。 單純考慮printf() 的輸出重新定向,可以有三種途徑實(shí)現(xiàn): ◆ 改寫printf() 本身 ◆ 改寫 fput() ◆ 改寫 _sys_write() 需要注意的是,越底層的函數(shù),被其他上層函數(shù)調(diào)用的可能性越大,改變了一個(gè)底層函數(shù)的實(shí)現(xiàn),則所有調(diào)用該函數(shù)的上層函數(shù)的行為都被改變了。 以fputc() 的重新實(shí)現(xiàn)為例,下面是改變fputc() 輸出設(shè)備到系統(tǒng)串行通信端口的實(shí)例: int fputc(int ch, FILE *f) { /* e.g. write a character to an UART */ char tempch = ch; sendchar(&tempch); // UART driver return ch; } 代碼中的函數(shù)sendchar() 假定是系統(tǒng)的串口設(shè)備驅(qū)動(dòng)函數(shù)。只要新建函數(shù)fput() 的接口符合標(biāo)準(zhǔn),經(jīng)過(guò)編譯鏈接后,該函數(shù)實(shí)現(xiàn)就覆蓋了原來(lái)缺省的函數(shù)體,所有對(duì)該函數(shù)的調(diào)用,其行為都被新實(shí)現(xiàn)的函數(shù)所重新定向了。 3 Semihosting 機(jī)制 上面提到許多庫(kù)函數(shù)在調(diào)試環(huán)境下的實(shí)現(xiàn)都調(diào)用了一種叫作semihosting(半主機(jī))的機(jī)制。Semihosting具體來(lái)講是指一種讓代碼在 ARM 目標(biāo)上運(yùn)行,但使用運(yùn)行了ARM 調(diào)試器的主機(jī)上I/O 設(shè)備;也就是讓ARM 目標(biāo)將輸入/ 輸出請(qǐng)求從應(yīng)用程序代碼傳遞到運(yùn)行調(diào)試器的主機(jī)的一種機(jī)制。通常這些輸入/輸出設(shè)備包括鍵盤、屏幕和磁盤I/O。 半主機(jī)由一組已定義的SWI 操作來(lái)實(shí)現(xiàn),如圖5所示。庫(kù)函數(shù)調(diào)用相應(yīng)的SWI(軟件中斷),然后調(diào)試代理程序處理SWI 異常,并提供所需的與主機(jī)之間的通訊。多數(shù)情況下,半主機(jī)SWI 是由庫(kù)函數(shù)內(nèi)的代碼調(diào)用的。但是應(yīng)用程序也可以直接調(diào)用半主機(jī)SWI。半主機(jī)SWI 的接口函數(shù)是通用的。當(dāng)半主機(jī)操作在硬件仿真器、指令集仿真器、RealMonitor或Angel下執(zhí)行時(shí),不需要進(jìn)行移植處理。 ![]() 圖5 semihosting的實(shí)現(xiàn)過(guò)程 使用單個(gè)SWI 編號(hào)請(qǐng)求半主機(jī)操作。其它的SWI 編號(hào)可供應(yīng)用程序或操作系統(tǒng)使用。用于半主機(jī)的SWI號(hào)是: 在ARM 狀態(tài)下:0x123456 在Thumb 狀態(tài)下:0xAB SWI 編號(hào)向調(diào)試代理程序指示該SWI 請(qǐng)求是半主機(jī)請(qǐng)求。要辨別具體的操作類型,用寄存器r0 作為參數(shù)傳遞。r0 傳遞的可用半主機(jī)操作編號(hào)分配如下: ◆ 0x00~0x31:這些編號(hào)由ARM 公司使用,分別對(duì)應(yīng)32個(gè)具體的執(zhí)行函數(shù)。 ◆ 0x32~0xFF:這些編號(hào)由ARM 公司保留,以備將來(lái)用作函數(shù)擴(kuò)展。 ◆ 0x100~0x1FF:這些編號(hào)保留給用戶應(yīng)用程序。但是,如果編寫自己的SWI 操作,建議直接使用SWI指令和SWI編號(hào),而不要使用半主 機(jī)SWI 編號(hào)加這些操作類型編號(hào)的方法。 ◆ 0x200~0xFFFFFFFF:這些編號(hào)未定義。當(dāng)前未使用并且不推薦使用這些編號(hào)。半主機(jī)SWI使用的軟件中斷編號(hào)也可以由用戶自定 義,但若是改變了缺省的軟中斷編號(hào),需要: ◆ 更改系統(tǒng)中所有代碼(包括庫(kù)代碼)的半主機(jī)SWI 調(diào)用 ◆ 重新配置調(diào)試器對(duì)半主機(jī)請(qǐng)求的捕捉與響應(yīng)這樣才能使用新的SWI 編號(hào)。 4 應(yīng)用環(huán)境的初始化和根據(jù)目標(biāo)系統(tǒng)資源進(jìn)行的移植 在下一期中介紹應(yīng)用環(huán)境和目標(biāo)系統(tǒng)的初始化。(更多關(guān)于ARM的詳細(xì)技術(shù)資料,請(qǐng)?jiān)L問(wèn):http://www.arm.com/arm/documentation?OpenDocument) 引證文獻(xiàn) 1. 譚云福.程志剛.孫會(huì)珺 基于ARM的嵌入式系統(tǒng)的初始化引導(dǎo)程序 [期刊論文] -河北省科學(xué)院學(xué)報(bào)2006(04) 2. 孟凌凌 基于嵌入式系統(tǒng)技術(shù)的樁基礎(chǔ)檢測(cè)儀的研制 [學(xué)位論文] 碩士2006 3. 連迅 基于ARM微處理器的玻璃缺陷在線檢測(cè)系統(tǒng) [學(xué)位論文] 碩士2006 4. 楊志強(qiáng) 嵌入式系統(tǒng)設(shè)計(jì)與發(fā)展 [期刊論文] -青海師范大學(xué)學(xué)報(bào)(自然科學(xué)版)2005(03) 5. 劉志勇 基于ARM的無(wú)線視頻傳輸硬件系統(tǒng)的初步研究與開發(fā) [學(xué)位論文] 碩士2005 6. 田勁華 基于USB Host和ARM技術(shù)的大容量數(shù)據(jù)采集系統(tǒng)的研究 [學(xué)位論文] 碩士2005 7. 金志強(qiáng) 基于ARM的嵌入式控制系統(tǒng)硬件平臺(tái)設(shè)計(jì) [學(xué)位論文] 碩士2005 8. 王雷 基于ARM微處理器的應(yīng)用研究 [學(xué)位論文] 碩士2005 9. 信俊昌 基于ARM的具有網(wǎng)絡(luò)功能的嵌入式平臺(tái)的設(shè)計(jì)與實(shí)現(xiàn) [學(xué)位論文] 碩士2004 作 者:ARM中國(guó) 費(fèi)浙平 刊 名:?jiǎn)纹瑱C(jī)與嵌入式系統(tǒng)應(yīng)用 2003(8) |