用GNU工具開(kāi)發(fā)基于ARM的嵌入式系統(tǒng)
當(dāng)前,ARM公司的32位RISC處理器,以其內(nèi)核耗電少、成本低、功能強(qiáng)、特有16/32位雙指令集,已成為移動(dòng)通信、手持計(jì)算、多媒體數(shù)字消費(fèi)等嵌入式解決方案的RISC標(biāo)準(zhǔn),市場(chǎng)占有率超過(guò)了75 %。多家公司都推出了自己的基于ARM內(nèi)核的處理器產(chǎn)品,越來(lái)越多的開(kāi)發(fā)人員開(kāi)始了針對(duì)ARM平臺(tái)的開(kāi)發(fā)。通常開(kāi)發(fā)人員需要購(gòu)買芯片廠商或第三方提供的開(kāi)發(fā)板,還需要購(gòu)買開(kāi)發(fā)軟件,如C編譯器或者集成了實(shí)時(shí)操作系統(tǒng)的開(kāi)發(fā)環(huán)境。開(kāi)發(fā)板的價(jià)格從數(shù)百到上千美元,而編譯器、實(shí)時(shí)操作系統(tǒng)價(jià)格更是動(dòng)輒數(shù)千到數(shù)萬(wàn)美元。這樣,在開(kāi)發(fā)初期,軟硬件上的投資就需要上萬(wàn)美元,對(duì)于國(guó)內(nèi)大多數(shù)開(kāi)發(fā)人員來(lái)說(shuō),無(wú)疑是太貴了。
慶幸的是,GNU所倡導(dǎo)的自由軟件給開(kāi)發(fā)者帶來(lái)了福音。1984 年,旨在開(kāi)發(fā)一個(gè)類似 Unix 的,并且是完全免費(fèi)的完整操作系統(tǒng)和配套工具:GNU 系統(tǒng)(發(fā)音為“guh-NEW”)。GNU的操作系統(tǒng)和開(kāi)發(fā)工具都是免費(fèi)的,遵循GNU 通用公共許可證 (GPL)協(xié)議,任何人都可以從網(wǎng)上獲取全部的源代碼。關(guān)于GNU和公共許可證協(xié)議的詳細(xì)資料,讀者可參看GNU網(wǎng)站的中文介紹:http://www.gnu.org/home.cn.html。
除了大家熟知的Linux操作系統(tǒng)外,GNU的軟件還包括編譯器(gcc,g++)、二進(jìn)制轉(zhuǎn)換工具(objdump,objcopy)、調(diào)試工具(gdb,gdbserver,kgdb)和基于不同硬件平臺(tái)的開(kāi)發(fā)庫(kù)。GNU開(kāi)發(fā)工具的主要缺點(diǎn)是采用命令行方式,用戶掌握和使用比較困難,不如基于 Windows系統(tǒng)的開(kāi)發(fā)工具好用。但是,GNU工具的復(fù)雜性是由于它更貼近編譯器和操作系統(tǒng)的底層,并提供了更大的靈活性。一旦學(xué)習(xí)和掌握了相關(guān)工具,也就了解了系統(tǒng)設(shè)計(jì)的基礎(chǔ)知識(shí),為今后的開(kāi)發(fā)工作打下基礎(chǔ)。GNU的開(kāi)發(fā)工具都是免費(fèi)的,遵循GPL協(xié)議,任何人都可以從網(wǎng)上獲取。筆者參與了一個(gè)基于 ARM平臺(tái)的嵌入式Linux系統(tǒng)開(kāi)發(fā),采用的是摩托羅拉龍珠系列的MC928MX1。從測(cè)試代碼、引導(dǎo)程序、嵌入式Linux移植、應(yīng)用程序、圖形界面都可以用GNU工具進(jìn)行開(kāi)發(fā),不需要在開(kāi)發(fā)工具上做額外的投入。本文所介紹的開(kāi)發(fā)方法同樣適用于其它公司的基于ARM的產(chǎn)品。
1 硬件平臺(tái)
MC928MX1(以下簡(jiǎn)稱MX1)是摩托羅拉公司基于ARM核心的第一款MCU,主要面向高端嵌入式應(yīng)用。內(nèi)部采用ARM920T內(nèi)核,并集成了 SDRAM/Flash、LCD、USB、藍(lán)牙(bluetooth)、多媒體閃存卡(MMC)、CMOS攝像頭等控制器。關(guān)于MX1的詳細(xì)資料,感興趣的讀者可以參考http://www.motorola.com.cn /semiconductors/。作為應(yīng)用開(kāi)發(fā)的最小系統(tǒng)必須包括RAM(程序運(yùn)行空間)、Flash(存放目標(biāo)代碼)和串行接口(用于調(diào)試和下載程序)。MX1提供了6個(gè)片選端(CS0“CS5),內(nèi)置了SDRAM控制器,數(shù)據(jù)寬度32位。在筆者的系統(tǒng)中采用了2片8M%26;#215;16位的 SDRAM和2片4M%26;#215;16位的同步Flash存儲(chǔ)器,分別接入數(shù)據(jù)線的低16位和高16位,如圖1所示。
圖1中SDRAM接片選端CS2,F(xiàn)lash接片選端CS3,其余為SDRAM/Flash的控制信號(hào)。最小系統(tǒng)還包括至少1個(gè)串行接口,可以采用 MX1內(nèi)置的UART控制器。
2 自舉模式
目前,許多嵌入式處理器都提供了自舉模式(Bootstrap),供用戶寫入引導(dǎo)代碼。自舉模式利用了固化在芯片內(nèi)部的一段引導(dǎo)程序,當(dāng)處理器復(fù)位時(shí),如果在特定引腳上加信號(hào),則處理器將在復(fù)位后執(zhí)行固化ROM中的程序。例如,MX1提供了4條復(fù)位引腳,復(fù)位時(shí)引腳不同的電平組合可以從不同的片選端啟動(dòng)系統(tǒng)。自舉ROM中的程序完成串口的初始化,然后等待用戶從串口寫入用戶代碼。自舉模式所能接受的是一種專門格式的文本文件,包括數(shù)據(jù)和要寫入/讀出的地址。關(guān)于自舉模式的代碼格式,可參考相關(guān)芯片的手冊(cè)。在摩托羅拉的網(wǎng)站還提供了許多小工具,幫助開(kāi)發(fā)者將其它格式的文件轉(zhuǎn)換成為自舉模式格式。通過(guò)自舉模式下載的通常是一段和上位機(jī)軟件(如超級(jí)終端)通信的程序,完成接收數(shù)據(jù)并寫入Flash的操作。寫入的數(shù)據(jù)可以是用戶自己的應(yīng)用程序、數(shù)據(jù)或者操作系統(tǒng)的內(nèi)核。通過(guò)自舉模式下載的引導(dǎo)程序同樣可以用GNU工具開(kāi)發(fā)。
3 GNU的編譯器和開(kāi)發(fā)工具
GNU提供的編譯工具包括匯編器as、C編譯器gcc、C++編譯器g++、連接器ld和二進(jìn)制轉(zhuǎn)換工具objcopy。基于ARM平臺(tái)的工具分別為 arm-linux-as、arm-linux-gcc、arm-linux-g++、arm -linux-ld 和arm-linux-objcopy。GNU的所有開(kāi)發(fā)工具都可以從www.gnu.org上下載,基于ARM的工具可以從 www.uclinux.org獲得。GNU的編譯器功能非常強(qiáng)大,共有上百個(gè)操作選項(xiàng),這也是這類工具讓初學(xué)者頭痛的原因。不過(guò),實(shí)際開(kāi)發(fā)中只需要用到有限的幾個(gè),大部分可以采用缺省選項(xiàng)。GNU工具的開(kāi)發(fā)流程如下:編寫C、C++語(yǔ)言或匯編源程序,用gcc或g++生成目標(biāo)文件,編寫連接腳本文件,用連接器生成最終目標(biāo)文件(elf格式),用二進(jìn)制轉(zhuǎn)換工具生成可下載的二進(jìn)制代碼。GNU工具都運(yùn)行在Linux下,開(kāi)發(fā)者需要1臺(tái)運(yùn)行Linux的PC 作為上位機(jī)。由于篇幅所限,不能完整地介紹整個(gè)嵌入式操作系統(tǒng)的開(kāi)發(fā)過(guò)程,將以第二節(jié)中提到的通過(guò)自舉模式下載的引導(dǎo)程序?yàn)槔f(shuō)明開(kāi)發(fā)的過(guò)程。對(duì)于像 Linux這樣的大系統(tǒng),基本的開(kāi)發(fā)流程是一樣的。
引導(dǎo)程序?qū)⑼ㄟ^(guò)自舉模式下載到MX1的片內(nèi)RAM,從地址0x00300000開(kāi)始并執(zhí)行。完成串口和SDRAM的初始化后,引導(dǎo)程序?qū)⒌却邮諔?yīng)用程序或操作系統(tǒng)內(nèi)核,將接收到的數(shù)據(jù)放在SDRAM中。數(shù)據(jù)接收完畢后,引導(dǎo)程序?qū)DRAM中的數(shù)據(jù)寫入Flash,下一次就可以從Flash中直接引導(dǎo)系統(tǒng)了。由于操作系統(tǒng)的內(nèi)核比較大,如Linux有1 MB以上,下載過(guò)程必須考慮糾錯(cuò)。因此,接收部分采用Xmode協(xié)議,可以用Windows下超級(jí)終端的Xmode發(fā)送方式發(fā)送文件。
(1)編寫C、C++語(yǔ)言或匯編源程序
通常匯編源程序用于系統(tǒng)最基本的初始化,如初始化堆棧指針、設(shè)置頁(yè)表、操作ARM的協(xié)處理器等。初始化完成后就可以跳轉(zhuǎn)到C代碼執(zhí)行。需要注意的是,GNU的匯編器遵循AT%26;amp;T的匯編語(yǔ)法,讀者可以從GNU的站點(diǎn)(www.gnu.org)上下載有關(guān)規(guī)范。匯編程序的缺省入口是 start標(biāo)號(hào),用戶也可以在連接腳本文件中用ENTRY標(biāo)志指明其它入口點(diǎn)(見(jiàn)下文關(guān)于連接腳本的說(shuō)明)。[!--empirenews.page--]
(2)用gcc或g++生成目標(biāo)文件
如果應(yīng)用程序包括多個(gè)文件,就需要進(jìn)行分別編譯,最后用連接器連接起來(lái)。如筆者的引導(dǎo)程序包括3個(gè)文件:init.s(匯編代碼、初始化硬件) xmrecever.c(通信模塊,采用Xmode協(xié)議)和flash.c(Flash擦寫模塊)。 分別用如下命令生成目標(biāo)文件: arm-linux-gcc-c-O2-o init.o init.s arm-linux-gcc-c-O2-o xmrecever.o xmrecever.c arm-linux-gcc-c-O2-o flash.o flash.c 其中-c命令表示只生成目標(biāo)代碼,不進(jìn)行連接;-o 命令指明目標(biāo)文件的名稱;-O2表示采用二級(jí)優(yōu)化,采用優(yōu)化后可使生成的代碼更短,運(yùn)行速度更快。如果項(xiàng)目包含很多文件,則需要編寫makefile文件。關(guān)于makefile的內(nèi)容,請(qǐng)感興趣的讀者參考相關(guān)資料。
(3)編寫連接腳本文件
gcc等編譯器內(nèi)置有缺省的連接腳本。如果采用缺省腳本,則生成的目標(biāo)代碼需要操作系統(tǒng)才能加載運(yùn)行。為了能在嵌入式系統(tǒng)上直接運(yùn)行,需要編寫自己的連接腳本文件。編寫連接腳本,首先要對(duì)目標(biāo)文件的格式有一定了解。GNU編譯器生成的目標(biāo)文件缺省為elf格式。elf文件由若干段(section)組成,如不特殊指明,由C源程序生成的目標(biāo)代碼中包含如下段:.text(正文段)包含程序的指令代碼;.data(數(shù)據(jù)段)包含固定的數(shù)據(jù),如常量、字符串;.bss(未初始化數(shù)據(jù)段)包含未初始化的變量、數(shù)組等。C++源程序生成的目標(biāo)代碼中還包括.fini(析構(gòu)函數(shù)代碼)和.init(構(gòu)造函數(shù)代碼)等。有關(guān)elf文件格式,讀者可自行參考相關(guān)資料。連接器的任務(wù)就是將多個(gè)目標(biāo)文件的.text、.data和.bss等段連接在一起,而連接腳本文件是告訴連接器從什么地址開(kāi)始放置這些段。例如筆者的引導(dǎo)程序連接文件link.lds為: ENTRY(begin) SECTION { 。=0x00300000; .text : { *(.text) } .data: { *(.data) } .bss: { *(.bss) } }
其中,ENTRY(begin)指明程序的入口點(diǎn)為begin標(biāo)號(hào);。=0x00300000指明目標(biāo)代碼的起始地址為0x00300000,這一段地址為MX1的片內(nèi)RAM;.text : { *(.text) }表示從0x00300000開(kāi)始放置所有目標(biāo)文件的代碼段,隨后的.data: { *(.data) }表示數(shù)據(jù)段從代碼段的末尾開(kāi)始,再后是.bss段。
(4)用連接器生成最終目標(biāo)文件
有了連接腳本文件,如下命令可生成最終的目標(biāo)文件: arm-linux-ld-nostadlib-o bootstrap.elf-T link.lds init.o xmrecever.o flash.o 其中,ostadlib表示不連接系統(tǒng)的運(yùn)行庫(kù),而是直接從begin入口;-o指明目標(biāo)文件的名稱;-T指明采用的連接腳本文件;最后是需要連接的目標(biāo)文件列表。
(5)生成二進(jìn)制代碼
連接生成的elf文件還不能直接下載執(zhí)行,通過(guò)objcopy工具可生成最終的二進(jìn)制文件: arm-linux-objcopy-O binary bootstrap.elf bootstrap.bin 其中-Obinary指定生成為二進(jìn)制格式文件。Objcopy還可以生成S格式的文件,只需將參數(shù)換成-O srec。如果想將生成的目標(biāo)代碼反匯編,還可以用objdump工具: arm-linux-objdump-D bootstrap.elf 至此,所生成的目標(biāo)文件就可以直接寫入Flash中運(yùn)行了。如果要通過(guò)自舉模式下載,還需要轉(zhuǎn)換為自舉模式的文件格式,相關(guān)轉(zhuǎn)換工具可以在摩托羅拉的網(wǎng)站上找到。
掌握了GNU工具后,開(kāi)發(fā)者就可以開(kāi)發(fā)或移植C或C++代碼的程序。用戶可以不需要操作系統(tǒng),直接開(kāi)發(fā)簡(jiǎn)單應(yīng)用程序。但對(duì)于更復(fù)雜的應(yīng)用來(lái)說(shuō),操作系統(tǒng)必不可少。目前流行的源代碼公開(kāi)的操作系統(tǒng)如Linux、μC/OS都可以用GNU工具編譯。ARM的Linux已有很多成熟的版本,可以支持 ARM720、ARM920、 ARM1020等多種處理器,讀者可從www.uclinux.org或www.armdevzone.com上獲取最新信息。Linux移植過(guò)程中和處理器相關(guān)的代碼都放在arch/arm目錄下。對(duì)于內(nèi)核,用戶需要做的是設(shè)定自己系統(tǒng)的內(nèi)存映像,RAM起始地址,I/O地址空間和虛擬I/O地址空間,參看arch/arm/mach-integrator/arch.c文件。除了內(nèi)核外,用戶還需要為自己的系統(tǒng)編制各種各樣的驅(qū)動(dòng)程序。
4 調(diào)試工具
Linux下的GNU調(diào)試工具主要是gdb、gdbserver和kgdb。其中g(shù)db和gdbserver可完成對(duì)目標(biāo)板上Linux下應(yīng)用程序的遠(yuǎn)程調(diào)試。gdbserver是一個(gè)很小的應(yīng)用程序,運(yùn)行于目標(biāo)板上,可監(jiān)控被調(diào)試進(jìn)程的運(yùn)行,并通過(guò)串口與上位機(jī)上的gdb通信。開(kāi)發(fā)者可以通過(guò)上位機(jī)的gdb輸入命令,控制目標(biāo)板上進(jìn)程的運(yùn)行,查看內(nèi)存和寄存器的內(nèi)容。gdb5.1.1以后的版本加入了對(duì)ARM處理器的支持,在初始化時(shí)加入 -target==arm參數(shù)可直接生成基于ARM平臺(tái)的gdbserver。gdb工具可以從ftp://ftp.gnu.org/pub/gnu /gdb/上下載。
對(duì)于Linux內(nèi)核的調(diào)試,可以采用kgdb工具,同樣需要通過(guò)串口與上位機(jī)上的gdb通信,對(duì)目標(biāo)板的Linux內(nèi)核進(jìn)行調(diào)試。由于篇幅所限,感興趣的讀者可以從http://oss.sgi.com/projects/kgdb/上了解具體的使用方法。
結(jié)束語(yǔ)
本文以一個(gè)具體的實(shí)例為例,對(duì)GNU工具中的常用功能作了介紹。其實(shí)GNU工具的功能還遠(yuǎn)不止這些,更進(jìn)一步的操作有:針對(duì)不同處理器,不同算法的軟件優(yōu)化、高效的內(nèi)嵌匯編、大型項(xiàng)目管理功能等。相信GNU能成為越來(lái)越多開(kāi)發(fā)人員的選擇。