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