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