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