摘 要: 通過分析Uboot的文件結構及其啟動流程,詳細給出了Uboot在基于ARM920T開發(fā)板上的移植方案,包括編譯、調(diào)試全過程,最終能夠在Uboot命令方式下加載映像文件,完成Linux內(nèi)核與yaffs映像文件的調(diào)試,具有Bootloader移植的通用性。
關鍵詞: Uboot; S3C2440; ARM920T; 引導過程; 啟動代碼
1 Uboot移植環(huán)境準備
1.1 移植平臺的硬件組成
硬件平臺是ARM9的體系結構,ARM920T的CPU, SOC芯片是三星的S3C2440,支持Nand Flash與Nor Flash的可選啟動方式,其主要硬件資源如表1所示[1]。
支持Nand Flash與Nor Flash啟動,可以通過跳線來選擇啟動方式。Nand Flash啟動時,最開始4 KB數(shù)據(jù)被硬拷貝到內(nèi)部Boot Internal SRAM,且被映射到nGCS0的片選空間0x0000,0000—0x0800,0000;Nor Flash方式啟動時,它直接被映射到nGCS0的片選空間。所以,在Uboot移植時,要考慮將Uboot燒寫到Nor flash上還是Nand Flash上。
1.2 Uboot工作原理
Uboot的整體結構如圖1所示。
從圖1可以看出,這種分層結構的Uboot分模塊化了,給移植帶來了很大的方便。由于協(xié)議層與應用層是與目標硬件無關的,因此移植工作主要集中在物理層和驅動層上面的修改。而Uboot支持串口下載、網(wǎng)絡下載,并提供了很多交互式命令。整個Uboot編譯、連接過程如下:
(1)創(chuàng)建編譯環(huán)境
在MAKEFILE中會調(diào)用根目錄下的mkconfig文件,如下:
MKCONFIG:= $(SRCTREE)/mkconfig
qq2440v3_config:unconfig
@$(MKCONFIG)$(@:_config=)arm ARM920T qq2440v3 NULL s3c24x0
Mkconfig文件引用傳入的參數(shù)$1=qq2440v3、$2=arm、$3=arm920t、$4=qq2440v3、$5=NULL、$6=s3c24x0,流程如圖2所示。
(2)編譯流程
編譯流程如圖3所示。
最終生成內(nèi)存映像圖文件U-boot.map和可執(zhí)行二進制映像elf文件U-boot[2],可以直接將生成的U-boot下載到SDRAM來單步調(diào)試。
2 Uboot的移植操作
2.1存儲器映射與存儲器重映射
存儲器映射,實現(xiàn)了統(tǒng)一編址,方便了程序在32 bit尋址(4 GB尋址空間)的范圍內(nèi)能夠尋址到任意的物理存儲區(qū)。
S3C2440芯片不帶片內(nèi)Flash,帶片內(nèi)4 KB的SRAM,被映射到了0x4000_0000~0x4000_1000的地址空間,外部的SDRAM被映射到bank6,網(wǎng)卡被映射到bank3,F(xiàn)lash被映射到bank0。
由于Uboot是上電后就運行,因此需要將代碼定位在Flash從0x0000_0000的上電入口處。為了提高系統(tǒng)加載速度并且實現(xiàn)在線編程功能,需要將整個Uboot從Flash中搬到RAM運行,即代碼從定位,將整個代碼定位到SDRAM的0x3300_0000之后,來作為其實際的運行地址,具體如圖4所示。
2.2 配置主機運行環(huán)境
Uboot與Linux系統(tǒng)密切相關,筆者在RH Linux的虛擬機中搭建了整個運行環(huán)境,采用的是2.2.4的Linux內(nèi)核,arm-linux-gcc-3.4.1的交叉編譯工具[3],需要在/root/.bashrc文件中做一下交叉編譯工具路徑的聲明,即加上如下一句:
export PATH=$PATH:/usr/local/arm/3.4.1/bin
保存并退出,在終端下輸入“arm-linux-gcc-version”并回車,如果能看到輸出版本信息為3.4.1,則代表路徑設置正確,交叉編譯工具鏈已經(jīng)成功安裝。
2.3 修改CPU相關代碼
在調(diào)試Uboot時,如果每次都將二進制映像燒錄到Flash中,不僅需要等待,而且操作麻煩,本文是在調(diào)試階段將二進制映像直接燒錄到外部存儲器SDRAM中,然后直接從該處運行,這樣直接在內(nèi)存中運行,可以很方便地完成Uboot調(diào)試。
Uboot啟動的第一階段,從.\cpu\arm920t\start.s開始執(zhí)行,依次完成關閉看門狗、關閉中斷、設置CPU分頻比、初始化SDRAM、代碼重定位、設置堆棧,最后跳轉到C函數(shù)的入口點。當在SDRAM中調(diào)試時,內(nèi)存的初始化已經(jīng)預先完成了,因此不需要初始化SDRAM和代碼重定位的功能。
在.\include\configs\qq2430.h添加宏定義define CONFIG_SKIP_LOWLEVEL_INIT,就會跳過cpu_init_crit處的初始化SDRAM函數(shù),代碼如下所示:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
…
當Uboot在SDRAM中運行時,代碼的入口地址_start與代碼在SDRAM中重定位的地址_TEXT_BASE相同,直接跳轉到堆棧初始化處stack_setup,跳過了代碼的Flash到RAM的搬運。代碼如下所示:
adr r0, _start
ldr r1, _TEXT_BASE
cmp r0, r1
beq stack_setup
2.4 添加平臺相關代碼
從匯編跳轉到C程序的入口點start_armboot時,即位于./Lib_arm/Board.c中的start_armboot函數(shù),通過函數(shù)初始化數(shù)組來依次完成CPU(cpu_init)、板級(board_init)、中斷(interrupt_init)、環(huán)境變量(env_init)、串口(serial_init)、波特率(init_buardrate)、顯示(disp_banner)、Flash初始化(nand_init),每個初始化后返回不為0的值,否則永遠在死循環(huán)中掛起,需要重新啟動開發(fā)板。
2.4.1 鎖相環(huán)時鐘的配置
在smdk2410開發(fā)板的基礎上修改。在./board/qq2440v3下新建qq2440v3.c,并且將Uboot中的./board/smdk2410/smdk2410.c內(nèi)容全部復制給qq2440v3.c。在include/configs下新建qq2440v3.h配置文件,同時將include/configs/smdk2410.h全部復制給qq2440v3.h。因為S3C2440與S3C2410的最高運行速率不一樣,系統(tǒng)時鐘設置也不一樣,即鎖相環(huán)配置有差異,因此內(nèi)部總線的分頻比系數(shù)是不同的。S3C2440可運行于400 MHz,而S3C2410則是200 MHz,需要更改系統(tǒng)時鐘部分,使其增加對S3C2440的支持。
鎖相環(huán)輸出的MPLL(fclk時鐘頻率寄存器)與UPLL(USB控制時鐘頻率寄存器)計算式如下:
MPLL=(2*m*Fin)/(p*2^s) /*鎖相環(huán)輸出fclk頻率
UPLL=(m*Fin)/(p*2^s) /*鎖相環(huán)輸出USB控制頻率
m=M(the value for divider M)+8,p=P(the value for divider P)+2,s=S
其中m、p、s為鎖相環(huán)的預置值,控制輸出頻率, Fin為晶振頻率12 MHz。通過下面的宏定義分別給M、P、S賦值0x5c、0x01、0x01就能輕松完成時鐘配置。
#define S3C2440_MPLL_400 MHz ((0x5c<<12)|(0x01<<4)|(0x01))
#define S3C2440_UPLL_48 MHz ((0x38<<12)|(0x02<<4)|(0x02))
#define S3C2440_CLKDIV 0x05 /* FCLK:HCLK:PCLK= 1:4:8, UCLK = UPLL
2.4.2 串口的配置
S3C2440帶有3個UART,用異步通用串口uart0與上位機通信。串口時鐘用pclk時鐘,即1/2 hclk時鐘,1/8 fclk時鐘(mpll時鐘),get_PCLK()函數(shù)返回pclk時鐘頻率。串口0工作于中斷模式,通過FIFO緩存收發(fā),沒有校驗位,1位停止位,通過設置ULCON0寄存器與UFCON0來配置。為了提高數(shù)據(jù)傳送的可靠性,使用了錯誤接收中斷機制,能夠檢測溢出錯誤、奇偶校驗錯誤和幀錯誤,相關錯誤狀態(tài)保存在錯誤狀態(tài)寄存器UTRSTAT0的對應位中。
串口的波特率用115 200 b/s,通過給波特率除數(shù)寄存器UBRDIV0賦值,能夠確定串行發(fā)送接收波特率。計算公式如下:
UBRDIVn=(int) (UART clock/(buad rate*16))-1;
其中UART clock為pclk時鐘,buard rate為115 200。
2.5 編譯并調(diào)試
為了在內(nèi)存中運行,先要把鏈接腳本文件./Board/qq2440v3/U-boot.lds中的程序初始化運行地址.=0x0000,0000改為.=0x3300,0000,以方便直接在RAM中運行。在終端控制臺中先進入Uboot的根目錄,然后執(zhí)行命令make qq2440v3_config,通過make all來編譯和連接程序。編譯沒有錯誤的情況下,會在Uboot的根目錄下生成u-boot、u-boot.srec和u-boot.bin三個文件,分別對應于ELF格式、S-Record格式和二進制格式。
直接使用JTAG燒寫二進制u-boot.bin到SDRAM的0x3300,0000處,燒寫完成之后,將pc的指針指向該處運行,會在串口終端上顯示板子的自檢信息,并給出提示符等待用戶輸入命令,如圖5所示。
3 Uboot從Flash中啟動
S3C2440既支持從Nor Flash中啟動,也支持從Nand Flash中啟動。Nor Flash的地址線與數(shù)據(jù)線分開,方便代碼存取且速度很快,上電復位時直接把Nor Flash映射到了0x0000,0000地址處。但是Nand Flash數(shù)據(jù)線與地址線復用,運行速度慢,為了提高Nand Flash啟動效率,S3C2440芯片加入了一個特殊機制,會在上電復位時,把Nand Flash前4 KB代碼硬拷貝到內(nèi)部SRAM的4 KB空間,然后將SRAM的4 KB空間映射到0x0000,0000處,這樣直接在SRAM中啟動Uboot,節(jié)省了啟動時間。
3.1 從Nor Flash中運行
3.1.1添加Nor Flash驅動
board/qq2440v3/Flash.c中的驅動只支持Nor Flash的AMDLV400和AMDLV800兩種芯片,不支持本文板子上的AM29LV160,更不支持Nand Flash,只能用CFI標準接口連接,在/drivers/cfiflash.c中定義了該接口標準下的讀寫函數(shù)的具體實現(xiàn)。要調(diào)用該驅動,應在配置頭文件/include/configs/qq2440v3.h中添加CFI的宏定義:
#define CFG_FLASH_CFI_DRIVER 1
并在board/qq2440v3/Makefile中去掉原來的Nor flash驅動的編譯,即:
COBJS:= qq2440v3.o flash.o 變量中去掉flash.o的連接。
3.1.2 SDRam初始化并實現(xiàn)代碼從定位
Uboot在Nor Flash中啟動后,在start.s階段除了要完成必要的寄存器設置外,還要完成SDRAM的初始化以及代碼從定位,即把Flash空間的Uboot映像搬運到SDRAM高地址空間中,然后在SDRAM中運行Uboot??梢灾苯訌腘or Flash啟動Uboot,但從Nand Flash啟動要實現(xiàn)重定位,在這里就一起實現(xiàn)了。
首先在.\include\configs\qq2430.h中去掉剛才添加宏定義define CONFIG_SKIP_LOWLEVEL_INIT,則會在start.s階段進入cpu_init_crit函數(shù)以完成I/D caches設置以及禁止MMU,隨后進入lowlevel_init完成內(nèi)存寄存器組的設置,如SDRAM位寬、刷新率等的初始化工作。
在.\include\configs\qq2430.h中去掉CONFIG_SKIP_
RELOCATE_UBOOT的宏定義,來完成整個代碼的重定位[5]。
Uboot代碼區(qū)的長度為_bss_start-_armboot_start,其中_bss_start與_armboot_start變量保存的都是代碼段的起始地址與終止地址。_start+_bss_start-_armboot_start為代碼區(qū)結束的絕對地址,通過地址絕對尋址來復制代碼區(qū)的數(shù)據(jù)到內(nèi)存中TEXT_BASE地址區(qū)域,其中TEXT_BASE在.\Board\QQ2440v3\Config.mk中被賦值,即TEXT_BASE=0x33000000,表示代碼重定位在SDRAM中的運行起始地址。
3.1.3 編譯并調(diào)試
Uboot已經(jīng)能夠成功在SDRAM中啟動運行了,為了能夠從Nor Flash中啟動,需要做如下工作。
先要把鏈接腳本文件./Board/qq2440v3/U-boot.lds中的程序初始化運行地址.=0x3300,0000改為.=0x0000,0000,通過硬件開關選擇開機啟動方式為Nor Flash,完成Nor Flash映射到0地址處。然后在終端控制臺編譯連接,直到?jīng)]有錯誤。通過HJTAG燒錄進Nor Flash里面,開機運行后串口終端輸出界面如圖6所示。
3.2 從Nand Flash中運行
3.2.1添加Nand Flash驅動
S3C2440支持從Nand Flash啟動,考慮到移植的通用性,對于沒有Nor Flash的板子,就需要從Nand Flash啟動。在.\drivers目錄下有兩種Nand的驅動,.\Nand和.\Nand_legacy兩種驅動可以選擇,其中.\Nand能夠自動識別很多型號的Nand Flash,并且是更新版本,因此選擇這種驅動。根據(jù)Nand.c中的
#if(CONFIG_COMMANDS&CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include <nand.h>
條件編譯選擇Nand驅動,首先在板級配置頭文件qq2440v3.h中的宏定義CONFIG_COMMAND中添加CFG_CMD_NAND,并且不定義CFG_NAND_LEGACY。在start_armboot()函數(shù)中會對外設逐一初始化,Nand初始化代碼如下:
#ifdefined(CFG_MAX_NAND_DEVICE) nand_init;
#endif
需要在板級配置頭文件qq2440v3.h中宏定義CFG_MAX_NAND_DEVICE,因為smdk2410開發(fā)板不支持Nand Flash,因此需要自己來編寫Nand Flash驅動函數(shù)board_nand_init來被nand_init以及nand_init_chip調(diào)用,以完成Nand Flash的硬件初始化,包括使能Nand Flash控制器、初始化ECC、使能片選信號、設置時序等。
3.2.2添加cmd命令
為了豐富Nand與網(wǎng)卡功能,還需要在配置文件中添加Nand與網(wǎng)卡相關命令來調(diào)用相關函數(shù)。在板級配置頭文件qq2440v3.h中的CONFIG_COMMANDS宏定義中以邏輯“或”的形式添加CFG_CMD_NAND與CFG_CMD_NET,這樣便可以通過命令方式實現(xiàn)Nand Flash的讀寫以及網(wǎng)絡下載功能。
Uboot的網(wǎng)絡功能很強大,可以方便地通過TFTP引導或者是NFS引導內(nèi)核映像或者文件系統(tǒng)到SDRAM,然后直接go到此處執(zhí)行,在SDRAM中調(diào)試完成后,再將映像文件燒錄到Flash中,不僅調(diào)試方便,而且還節(jié)省下載時間。
3.2.3 編譯并調(diào)試
編譯過程跟Nor Flash啟動一樣,最后串口輸出信息如圖7所示。
此時,整個Uboot的移植就算完成了,由于支持串口跟網(wǎng)卡驅動,可以很方便地用這個Uboot來通過網(wǎng)卡下載內(nèi)核映像與文件系統(tǒng)到Flash,通過串口輸出信息調(diào)試Uboot。
4 Uboot引導Linux內(nèi)核
4.1 內(nèi)核啟動參數(shù)的傳遞
Uboot在引導內(nèi)核啟動時,通過標記列表的方式向內(nèi)核傳遞啟動參數(shù)。這些啟動參數(shù)預先以環(huán)境變量的方式保存在Flash中,在./Lib_arm/Board.c中的初始化環(huán)境變量函數(shù)env_init()初始化,下面的函數(shù)來實現(xiàn)向kernel跳轉。
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); thekernel其實不是個函數(shù),而是指向內(nèi)核入口地址的指針,為0x30008000。這里把它強行轉化為帶三個參數(shù)的函數(shù)指針,會把三個參數(shù)保存到通用寄存器中,實現(xiàn)了向kernel傳遞信息的功能,R0賦值為0,R1賦值為機器號,R2賦值為啟動參數(shù)數(shù)據(jù)結構的首地址[6]。
標記列表實際上是內(nèi)存中的結構體組成的列表,在./Lib_arm/Armlinux.c中函數(shù)setup_start_tag()來創(chuàng)建標記列表。
4.2 tftp加載內(nèi)核映像
對于已經(jīng)編譯好了的內(nèi)核映像文件zImage,其格式是ELF的可執(zhí)行文件,首先要把它轉化成U-boot格式的文件uImage,實際是添加了一個header定義,直接用tools目錄下的工具mkimage就可用實現(xiàn),具體在終端中執(zhí)行如下操作:
①arm-linux-objcopy -O binary -R .note -R .comment -S vmlinux linux.bin
②gzip -9 linux.bin
③mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 -e 0x30008040 -n "Linux Kernel Image" -d linux.bin.gz uImage
先從內(nèi)核文件中提取二進制文件,然后壓縮,最后構造文件頭信息,包括名稱、大小、類型、CRC校驗碼等,添加的頭信息占用0x40大小空間。完成后下載內(nèi)核映像uImage,如下操作:
開啟主機tftp服務,將uImage放置tftp目錄下,然后啟動Uboot,運行tftp下載,镲除、燒寫Nand Flash,具體如圖8所示。
最后燒寫文件系統(tǒng)映像,與燒寫內(nèi)核映像一樣,先tftp下載到內(nèi)存,然后再燒寫,不同類型的文件系統(tǒng)nand燒寫命令不一樣,本文用到的是yaffs文件類型,則通過Nand write.yaffs 0x30000000 0x1d0000 $(filesize)命令來燒寫。
本文研究了Uboot在基于S3C2440系統(tǒng)上的移植,并且提出了可行性方案,通過邊搭建硬件環(huán)境邊調(diào)試Uboot,使Uboot在嵌入式系統(tǒng)板上正常運行,實現(xiàn)了串口通信、網(wǎng)口下載、Flash燒錄等功能,并且成功引導了Linux系統(tǒng),為后續(xù)的系統(tǒng)驅動程序開發(fā)奠定了基礎,使得Uboot的移植具有開發(fā)的通用性。
參考文獻
[1] 劉淼.嵌入式系統(tǒng)接口設計與Linux驅動程序開發(fā)[M]. 北京:北京航空航天大學出版社,2006.
[2] YAGHMOUR K. Building embedded Linux system[M]. [S.l.]:O’Reilly,2004.
[3] HENKEL J, XIAOBO SHARON HU, SHUVRA S. BHATTACHARYYA. Taking on the embedded system design challenge[J].IEEE Computer,2003,5(4):35-37.
[4] SD-Memory Card Specifications /Part1 Physical Layer Specification Version 1.01[R]. [S.l.]:SD Group, 2001.
[5] SUMSUANG ELECTRONICS. S3C2410X User’s Manual[Z].Republic of Korea: Sumsang,2003.
[6] Configurations.Denx software engineering[EB/OL]. (2006-7-23)http://www.denx.de/wiki/DULG/Manual.