從ADS到RealView MDK(MDK ARM)
很多嵌入式系統(tǒng)開發(fā)工程師對ARM的老版本開發(fā)工具ADS非常熟悉,而RealView MDK與ADS相比較,從外觀、仿真流程以及內(nèi)部二進制編譯鏈接工具上都有了不少改進,用法稍有不同。本主的主旨是介紹通用的流程,以及一些注意事項,幫助ADS用戶將老的、遺留的ADS工程轉(zhuǎn)化成在RealView MDK中進行開發(fā)調(diào)試的工程。
工具結(jié)構(gòu)的改進
作為ARM的新一代微控制器開發(fā)工具,RealView MDK不但包含ARM的最新版本編譯鏈接工具,即RVDS3.0的編譯鏈接工具,而且根據(jù)微控制器調(diào)試開發(fā)的特點采用了與ADS、RVDS完全不同的調(diào)試、仿真環(huán)境,μVision debugger與simulator。因此,MDK與ADS在工具架構(gòu)組成上有一些不同,包括了不同的工程管理器,不同版本的ARM編譯器(compiler),不同的調(diào)試器(debugger),不同的仿真器(simulator),以及不同的硬件調(diào)試單元(見表1)。
1編譯工具例化形式
在ADS中,當用戶需要將高級語言代碼編譯成目標文件時,需要根據(jù)目標機器碼的不同(16位的Thumb代碼或者32位的ARM代碼),以及高級語言的不同(C代碼或者C++代碼)選擇不同的編譯器可執(zhí)行文件。RVCT3.0編譯器則將它們?nèi)拷y(tǒng)一為armcc,僅僅通過不同的編譯選項進行區(qū)分。表2較為詳細的列出了其中的差別(表2中“默認的編譯選項”是指在沒有其他編譯選項時編譯器的缺省選項)。
2 POSIX格式
MDK集成了RVDS的編譯工具RVCT,與ADS相比,除去編譯、鏈接工具的可執(zhí)行二進制文件不同之外,兩個不同版本編譯器的很多編譯鏈接選項也有所不同。有關(guān)編譯鏈接選項的變化用戶可以參考ARM工具文檔“RVCT Compiler and Libraries Guide中Table E-2 Mapping of compiler options。
RVCT采用了POSIX格式的編譯鏈接選項,所有的多字符選項前必須使用雙下劃線。例如:ADS的編譯選項-cpu,在MDK中需要改寫成--cpu,否則用戶在MDK中直接使用ADS的makefile時,工具會產(chǎn)生一個如下警告:
Warning: L3910W: Old syntax, please use ‘--cpu’
3 ARM ABI的變化
ARM ABI是Application Binary Interface for the ARM Architecture的簡稱,是一系列ARM體系架構(gòu)標準的集合,囊括了ARM二進制代碼交互、開發(fā)工具以及操作系統(tǒng)等方面。
對目標文件進行鏈接之前,MDK工具的鏈接器會嚴格檢查各個目標文件(objects),判斷它們是否復合ARM體系結(jié)構(gòu)的ABI標準。而 MDK與ADS編譯鏈接工具所遵循的ARM ABI是不同版本的,所以將ADS的遺留工程直接移植到MDK并進行鏈接時,用戶可能會遇到如下的錯誤或者警告:
Error: L6238E: foo.o(.text) contains invalid call from‘~PRES8’function to ‘REQ8’ function
Warning: L6306W: ‘~PRES8’section foo.o(.text) should not use the address of ‘REQ8’ function foobar
這是因為新工具的ABI要求在函數(shù)調(diào)用時,系統(tǒng)必須保證堆棧指針8byte對齊,即每次進棧或者出棧的寄存器數(shù)目必須為偶數(shù)。這是為了能夠更加高效的使用STM與LDR指令對“double”或者“long long”類型的數(shù)據(jù)進行訪問。而老的ARM開發(fā)工具ADS并沒有考慮到新的ARM內(nèi)核架構(gòu),其ABI對于堆棧的操作僅僅要求4byte對齊。所以當用戶將在ADS中編譯鏈接成功的工程代碼移植到MDK上,或者將老的、ADS遺留的目標文件、庫文件在新工具MDK中進行鏈接時,MDK的鏈接器就會報出以上的錯誤。
對于以上情況,用戶可以通過簡單修改代碼并重新編譯鏈接,或者使用特殊的編譯選項來解決。
● 重新編譯所有代碼
當用戶擁有該ADS遺留工程的所有源代碼時,使用MDK重新編譯鏈接全部代碼是最好的解決方法。MDK中的新版本編譯工具會重新生成滿足堆棧8byte對齊要求的目標文件,避免由于堆棧不對齊引起的鏈接錯誤。
當工程中包含匯編代碼時,用戶可能還需要做少量的代碼修改。這些修改包括:
① 檢查匯編源碼中的指令,確保堆棧操作指令是8byte對齊的。
如例1中,ADS的遺留代碼一次性將5個寄存器壓棧,由于ARM的指令寄存器寬度為32位,即4byte,顯然5個寄存器入棧之后,堆棧指針不能夠滿足64位,8byte對齊。為了解決這種情況,我們可以將另外一個并不需要壓棧的寄存器、R12,同時壓棧,這樣當6個32位寄存器進棧之后,堆棧就能滿足64位對齊了。
例1
STMFD sp!,{r0-r3, lr} ; 將R0,R1,R2,R3,LR(奇數(shù))寄存器入棧
……
STMFD sp!, {r0-r3, r12, lr}; 將偶數(shù)個寄存器入棧
② 在每個匯編文件的開頭,添加“PRESERVE8”指令(見Ex2)。
例2
AREA Init, CO
……
PRESERVE8
AREA Init, CO
● 使用--apcs /adsabi編譯選項
當用戶沒有該ADS遺留工程的全部源碼,只擁有庫文件或者目標文件時,可以通過--apcs/adsabi編譯選項強制MDK的編譯器產(chǎn)生復合ADS ABI要求的目標文件,以達到與遺留的ADS庫文件、目標文件兼容的目的(ARM新工具將不會繼續(xù)支持--apcs/adsabi選項。建議用戶及時更新工具到最新版本)。
4 分散加載注意事項
MDK同樣支持ADS的分散加載文件,但是當分散加載文件中涉及到必須被放置ROOT Region中的C庫函數(shù)時,有時用戶需要作少量修改。
ROOT Region的load address與execution address相同,所以這部分代碼在系統(tǒng)初始化時無須進行搬移操作,很多庫函數(shù),如__scatter*.o或者__dc*.o,必須被放置在Root Region中。
例3 分散加載文件的修改;ADS 中的分散加載文件
ROM_LOAD 0x0
{
ROM_EXEC 0x0
{ vectors.o (Vect, +First)
__main.o (+RO)
* (Region$$Table)
* (ZISection$$Table)
}
RAM_EXEC 0x100000
{ *.o (+RO,+RW,+ZI) }
}……;
MDK中的分散加載文件1; MDK中的分散加載文件2
ROM_LOAD 0x0 ROM_LOAD 0x0
{ {
ROM_EXEC 0x0 ROM_EXEC 0x0
{ {
vectors.o (Vect, +First) vectors.o (Vect, +First)
* (InRoot$$Sections) __main.o(*)
} * (Region$$Table)
RAM_EXEC 0x100000 __scatter*.o(*)
{ __dc*.o(*)
*.o (+RO,+RW,+ZI) }
} RAM_EXEC 0x100000
}
{ *.o (+RO,+RW,+ZI)}
}
在ADS中,用戶必須在分散加載文件中明確的將特定section代碼放置在Root Region中。而MDK為了支持新的RW壓縮機制,采用了新的region table格式,這種新的格式并不包含ZISection$$Table,而且新的scatter-loading (__scatter*.o) 與 decompressor (__dc*.o)必須被放置在root region中。所以EX3中ADS的分散加載文件應(yīng)該被修改成新的形式。例3中提供了兩種修改分散加載文件的方法,分散加載文件1通過 InRoot$$Sections自動將所有必須的庫目標放至在root region中,而分散加載文件2則詳細的注明了__scatter*.o與 __dc*.o的位置。
5 C庫函數(shù)的差異
為了與新的ABI一致,MDK中的庫函數(shù)名稱與ADS可能會有不同。ADS中的__rt_*庫函數(shù)被替換為__aeabi_*。如果用戶的 ADS工程中曾經(jīng)重定義(retarget)過這些庫函數(shù),那么在移植到MDK時,需要重新實現(xiàn)這些函數(shù),以滿足新ABI的要求。表3列出了部分函數(shù)的對應(yīng)關(guān)系。
移植實例
結(jié)合以上對MDK與ADS差異的描述,本節(jié)將以實例的形式敘述如何將ADS1.2上的遺留代碼移植到MDK上。
以Philip的LPC2294(ARM7TDMI)為處理器,將一個在ADS1.2上開發(fā)的由LPC2294控制LED閃爍的工程移植到 MDK上來。該工程(Legacy_ADS.mcp)共有4個源文件(Startμp.s、tartget.c、IRQ.s、main.c),以及一個分散加載文件(Scatterload)。
使用ADS1.2編譯器,編譯選項為:-O1 -g+;鏈接選項為:-info totals -entry 0x00000000 -scatter .srcScatterload.scf -info sizes,我們得到最終代碼尺寸信息如下:
Total RO Size(Co
Total RW Size(RW Da
Total ROM Size(Co
為了能夠使用ARM新工具MDK的一系列特性,我們需要把ADS中的遺留工程移植到MDK上來。其具體步驟如下。
1 在MDK中新建工程
打開MDK,在主菜單中選擇Project-->New…-->μVision Project,并給新工程命名為New_MDK.uv2并保存。
在MDK自動彈出的器件選擇窗口(Select Device for Target)中選擇該工程所對應(yīng)的處理器型號,“LPC2294”。當MDK提示用戶是否自動添加啟動代碼時,選擇“否”。
2 添加源文件,并設(shè)置工程屬性
將Legacy_ADS.mcp工程中所有的源文件都添加到新的New_MDK.uv2工程中來。單擊工程屬性快捷鍵,打開工程屬性設(shè)置窗口,并選擇C/C++標簽頁,設(shè)置編譯器屬性。用戶可以根據(jù)以前ADS工程的編譯屬性設(shè)置,也可以根據(jù)當前具體需求重新設(shè)置編譯屬性。在本例中,我們將 ADS遺留工程的編譯屬性,“-O1 -g+”修改為“-O1 -g -W”后,復制到“Misc Controls”欄中來。這是因為由于編譯器版本的變化,其對應(yīng)的編譯選項也有所變化的緣故。注意:-W選項可以抑止所有的warning。
對ADS工程中的鏈接選項作適當修改如下,使其復合POSIX格式。
--info totals --entry 0x00000000 --scatter .srcScatterload.scf --info sizes
選擇Linker標簽,將修改過的鏈接選項復制至MDK工程屬性的Linker屬性中,并單擊“確定”按鈕。
3 Build工程并適當修改代碼
當所有的工程屬性都設(shè)置好之后,單擊“Build all target file”快捷鍵,對整個工程進行編譯鏈接。在MDK窗口的build輸出一欄中,我們會發(fā)現(xiàn)系統(tǒng)出現(xiàn)了一個鏈接錯誤L6238E,這是由于MDK中新版本編譯鏈接工具與ADS的老版本build工具采用不同的ABI造成的。
4 重新編譯鏈接該工程
代碼修改完畢之后,單擊“Build all target file”快捷鍵,對該工程進行二次編譯鏈接。MDK將成功生成New_MDK.axf文件,并顯示其代碼尺寸信息為:
Program Size: Co
這些信息同樣可以從鏈接生成的New_MDK.map文件中得到。
5 代碼調(diào)試與固化
與其他ARM開發(fā)工具相比較,MDK擁有非常出色的仿真功能,可以幫助用戶在純軟件的平臺上進行較為精確的調(diào)試。用戶可以在工程屬性設(shè)置窗口選擇simulator調(diào)試或者通過硬件調(diào)試工具(uLink)進行調(diào)試。
當選擇simμlator調(diào)試時,單擊debμg快捷鍵,打開simulator調(diào)試窗口。為了驗證該程序在LPC2294硬件平臺上是否能夠正確執(zhí)行,通過GPIO口驅(qū)動LED進行循環(huán)閃爍,用戶可以單擊Peripherals->GPIO->Port2,將GPIO端口2的仿真界面打開。
單擊運行快捷鍵,可以看到在GPIO端口2的仿真調(diào)試窗口中,IO口的輸出在不停的循環(huán)變化。
當程序通過了仿真調(diào)試之后,用戶就可以通過MDK的硬件調(diào)試工具,uLink,將最終代碼固化在非易失性的存儲器中了。
評論