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