S3C2416裸機(jī)開(kāi)發(fā)系列九_(tái)GCC啟動(dòng)代碼工程應(yīng)用實(shí)例
掃描二維碼
隨時(shí)隨地手機(jī)看文章
GNU是一個(gè)自由軟件工程項(xiàng)目,目標(biāo)在于創(chuàng)建一個(gè)完全兼容于UNIX的自由軟件環(huán)境。GNU已經(jīng)開(kāi)發(fā)出了大部分UNIX系統(tǒng)的程序庫(kù)和工具,如功能強(qiáng)大的文字編輯器Emacs,本章節(jié)涉及的GUN開(kāi)發(fā)編譯器GCC等。尤其是Linux與其它的GNU軟件結(jié)合,誕生了GNU下完全自由免費(fèi)的操作系統(tǒng)。GNU軟件功能完善而強(qiáng)大,絲毫不輸商業(yè)軟件,其開(kāi)源免費(fèi)的特性也得到了世界各地程序員的積極響應(yīng),讓GNU軟件尤其是Linux得到了相當(dāng)廣泛的應(yīng)用。s3c2416由于性能優(yōu)越,用來(lái)運(yùn)行Linux等GNU軟件是完全沒(méi)問(wèn)題的,筆者此處應(yīng)讀者的要求,把s3c2416的啟動(dòng)代碼工程移植到GCC交叉編譯環(huán)境上,以方便讀者在Linux下作進(jìn)一步的開(kāi)發(fā)。
1. 啟動(dòng)代碼工程架構(gòu)make工具是一個(gè)非常重要的編譯工具,利用make工具,可以將大型的開(kāi)發(fā)項(xiàng)目分解成為多個(gè)更易于管理的模塊,對(duì)于一個(gè)包括幾百個(gè)源文件的應(yīng)用程序而言,使用make工具和makefile文件就可以清晰地理順各個(gè)源文件之間的關(guān)系。對(duì)于一些商業(yè)的集成開(kāi)發(fā)環(huán)境編譯器,如MDK、IAR等,并不需要編寫make腳本,因?yàn)榧砷_(kāi)發(fā)環(huán)境通過(guò)可視化的選項(xiàng)或默認(rèn)的參數(shù)自動(dòng)幫用戶生成相關(guān)的make腳本。Linux下GCC開(kāi)發(fā)是需要掌握GNU make來(lái)構(gòu)建和管理自己的代碼工程的,make工具最基本的功能是調(diào)用makefile文件,通過(guò)makefile文件來(lái)描述源程序之間的相互依賴關(guān)系并自動(dòng)維護(hù)編譯工作。當(dāng)然,makefile 文件需要按照某種語(yǔ)法進(jìn)行編寫,需要說(shuō)明如何編譯各個(gè)源文件并連接生成可執(zhí)行文件,以及定義源文件之間的依賴關(guān)系。
如果讀者學(xué)習(xí)過(guò)相關(guān)的uboot源碼,那么是很容易理解筆者這篇在arm交叉編譯GCC下, s3c2416啟動(dòng)代碼工程的makefile管理方式。
1.1. 頂層目錄頂層目錄包括config.mk、Makefile、rules.mk這三個(gè)文件,start_code啟動(dòng)代碼源文件目錄以及其它可能的源碼目錄。
config.mk為所有makefile的配置文件,如配置交叉編譯工具所在的路徑、代碼的鏈接位置、庫(kù)文件路徑、頭文件搜索路徑、編譯選項(xiàng)等。通常被所有的makefile包含,如設(shè)置編譯選項(xiàng)為調(diào)試模式-g以及二級(jí)優(yōu)化-O2等都在此處修改。需要注意修改任何編譯選擇,如果想讓源碼重新編譯,需先在頂層目錄下make clean后再make,不然選項(xiàng)不起作用,不會(huì)重新編譯。因?yàn)閙akefile一般是不常修改的,為加快編譯,沒(méi)有在源碼的依賴關(guān)系中關(guān)聯(lián)config.mk文件。
rules.mk為源碼依賴關(guān)系生成文件,make工具很重要的一個(gè)功能就是根據(jù)依賴關(guān)系決定命令是否執(zhí)行,如一個(gè)源碼及其頭文件等沒(méi)有作過(guò)修改,那么下次編譯是不需要再次編譯的。make工具會(huì)選擇新添或修改過(guò)的源碼進(jìn)行編譯,其它未改動(dòng)的部分直接用上次編譯的結(jié)果。這樣的好處就是不用每次編譯都全部編譯,大大節(jié)省編譯時(shí)間。rules.mk通過(guò)被各個(gè)目錄下的makefile包含,和uboot該部分一樣,會(huì)在包含的目錄下生成該目錄下所有源碼依賴關(guān)系信息的隱藏文件.depend。
頂層Makefile,頂層Makefile用來(lái)管理工程各個(gè)源碼的目錄,并用對(duì)各個(gè)目錄編譯輸出進(jìn)行鏈接,最終輸出可執(zhí)行文件。所有的Makefile支持make all和make clean這兩個(gè)命令,頂層的Makefile執(zhí)行make后,首先會(huì)進(jìn)入各個(gè)源碼子目錄去執(zhí)行make_depend更新各個(gè)子目錄源碼的依賴關(guān)系,然后再進(jìn)入各個(gè)源碼子目錄去執(zhí)行make。所有子目錄均執(zhí)行更新完,最后調(diào)用鏈接命令把各個(gè)子目錄生成的代碼進(jìn)行鏈接,生成最終目標(biāo)文件。Make clean刪除所有make過(guò)后的輸出文件,還原工程原來(lái)的文件關(guān)系。
1.2. start_code子目錄與啟動(dòng)代碼相關(guān)的代碼文件放在start_code目錄文件夾中。目錄架構(gòu)如下:
s3c2416.S,啟動(dòng)代碼文件,代碼執(zhí)行時(shí)的入口,用來(lái)初始化系統(tǒng)到一個(gè)必要的c環(huán)境中,最后進(jìn)入c函數(shù)入口main執(zhí)行c代碼。
LowLevelInit.S,板級(jí)初始化代碼,包括DDR2控制器的初始化,代碼搬移到RAM的實(shí)現(xiàn)(sd卡啟動(dòng),Nand啟動(dòng)),由s3c2416.S調(diào)用。
Nand.h/Nand.c,Nand flash驅(qū)動(dòng)實(shí)現(xiàn),包括Nand啟動(dòng)時(shí),代碼從Nand搬移到RAM的接口函數(shù)實(shí)現(xiàn)。
MMU.h/MMU.c,MMU映射,把異常向量表從0x0處映射到用戶代碼的首地址,其余地址空間1:1映射,內(nèi)存區(qū)開(kāi)啟I/D-Cache。
Exception.h/Exception.c,異常處理(包括中斷),各個(gè)異常的處理代碼,IRQ實(shí)現(xiàn)統(tǒng)一管理各個(gè)外設(shè)中斷,支持中斷嵌套。
s3c2416.h,三星給出的s3c2416寄存器頭文件。
s3c2416.lds,GCC鏈接文件,在config.mk中配置引入該鏈接腳本。
Makefile,start_code目錄下的Makefile源碼管理,指定該目錄下需編譯的源碼,執(zhí)行make后會(huì)生成該目錄源碼的依賴關(guān)系文件.depend,并編譯指定的啟動(dòng)代碼源碼,生成一個(gè)靜態(tài)庫(kù)文件libstart_code.a以供頂層目錄鏈接,子目錄的Makefile功能是與uboot一致的。
筆者所用的DDR2型號(hào)為K4T51163QJ-BCE79(DDR2@400M5-5-5),64MB,行地址線13,列地址線為10,16位線寬。不同的板載DDR2,可能要修改LowLevelInit.S中DDR2的線寬時(shí)序配置,BANKCFG & BANKCON1, 2。所用的Nand flash為K9F2G08U0B,一頁(yè)有(2048+64)Byte,一個(gè)block有64頁(yè),容量大小為(256M+8M)Byte,是一款8位寬的SLC flash。Nand flash不一致,需Nand啟動(dòng)的,則需要修改Nand.c驅(qū)動(dòng)中的這些信息。其余代碼文件對(duì)于s3c2416/50/51都是通用的。
2. c開(kāi)發(fā)工程搭建首先,啟動(dòng)代碼工程是針對(duì)arm-linux-gcc的,需要在linux環(huán)境下安裝交叉編譯工具arm-linux-gcc??梢栽趙indows下通過(guò)虛擬機(jī)的方式安裝linux操作系統(tǒng)或安裝Cygwin搭建一個(gè)Linux環(huán)境。
2.1. 筆者文章最后給出GCC啟動(dòng)代碼示例工程鏈接,下載放在任意目錄下,如果arm-linux-gcc工具路徑在linux環(huán)境變量中,在代碼目錄下執(zhí)行make即可生成相關(guān)的文件。
2.2. 工程中加入新的源碼,以在apps目錄中加入test.c為例,首先新建或拷貝源代碼文件到目錄下,修改該目錄下的Makefile,在OBJS變量中加入test.c的目標(biāo)文件,OBJS = main.otest.o保存Makefile,其它不用修改,再在頂層目錄下make,即可看到test.c加入了編譯。
2.3. 工程中加入新的目錄,以在apps目錄中加入games目錄為例,首先新建目錄或拷貝目錄到apps目錄下,修改頂層的Makefile,在SUBDIRS變量中加入新添的目錄路徑,SUBDIRS= $(TOPDIR)/start_code $(TOPDIR)/apps $(TOPDIR)/apps/games,保存頂層的Makefile即可。同時(shí)新添目錄中的Makefile也應(yīng)保證符合子目錄的要求規(guī)則,games目錄的Makefile文件名要求第一字母大寫,內(nèi)容可完全拷貝參考apps或start_code目錄下的Makefile編寫,按上一步驟添加該目錄下需編譯的源碼,保存即可。如果源碼中引用了其它路徑的頭文件或庫(kù),則需要在config.mk中添加頭文件或庫(kù)的路徑。如在源碼中使用了math庫(kù),則應(yīng)LIBS變量中加入math庫(kù)路徑,LIBS = -lc -lm。在CFLAGS變量中加入games目錄下頭文件搜索路徑,CFLAGS += -I $(TOPDIR)/start_code-I $(TOPDIR)/apps-I $(TOPDIR)/apps/games,保存即可在頂層目錄中make。
2.4. 鏈接文件s3c2416.lds一般是無(wú)需修改的,目錄層次也是與uboot一致的。在鏈接腳本中引出了__code_start以及__code_end用來(lái)定位實(shí)際生成可執(zhí)行代碼bin大小,在代碼中代碼段(.text)、只讀段(.rodata)、已初始化可讀寫段(.data)是需要實(shí)際的存儲(chǔ)空間保存信息的,其它的未初始化段(.bss)等都可以直接用0來(lái)填充,無(wú)需任何的固化存儲(chǔ)空間。__code_start確定了代碼的運(yùn)行地址(從sd、nand等存儲(chǔ)設(shè)備加載代碼到ram需確定),__code_end - __code_start即為生成的代碼大小,這樣啟動(dòng)代碼根據(jù)這兩個(gè)信息即可自動(dòng)地把代碼從存儲(chǔ)設(shè)備拷貝到運(yùn)行的ram位置。同時(shí)start_code目錄中Bootloader1代碼應(yīng)放在最前面的8k位置,順序不能隨意改變。
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
__code_start = .;
/*************Bootloader1********************/
./start_code/s3c2416.o(.text)
./start_code/LowLevelInit.o(.text)
./start_code/MMU.o(.text)
./start_code/NAND.o(.text)
./start_code/Exception.o(.text)
/*********************************************/
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
__code_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
. =ALIGN(1<<14);
.mmudata : { *(.mmudata) }
}
2.5. 所有代碼加入到工程后,即可在頂層目錄中make,編譯完成后即可在頂層目錄生成s3c2416.elf、s3c2416.map、s3c2416.srec、s3c2416.bin、s3c2416.dis這五個(gè)文件。其中,s3c2416.map為鏈接Mapping文件,這里可以看到各個(gè)全局符號(hào)、各個(gè)段內(nèi)存鏈接位置、大小等信息。s3c2416.dis為工程的匯編生成文件,這是編譯器經(jīng)過(guò)編譯所有的源碼并進(jìn)行鏈接最終給出的匯編文件,這是最權(quán)威的查錯(cuò)文件,編譯器的bug以及任何用戶的失誤造成編