《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 嵌入式技術(shù) > 設(shè)計(jì)應(yīng)用 > Uboot在S3C2440上的移植
Uboot在S3C2440上的移植
來(lái)源:微型機(jī)與應(yīng)用2010年第24期
盧 偉, 潘 煉
(武漢科技大學(xué) 信息科學(xué)與工程學(xué)院自動(dòng)化系,湖北 武漢434200)
摘要: 通過(guò)分析Uboot的文件結(jié)構(gòu)及其啟動(dòng)流程,詳細(xì)給出了Uboot在基于ARM920T開發(fā)板上的移植方案,包括編譯、調(diào)試全過(guò)程,最終能夠在Uboot命令方式下加載映像文件,完成Linux內(nèi)核與yaffs映像文件的調(diào)試,具有Bootloader移植的通用性。
Abstract:
Key words :

摘   要: 通過(guò)分析Uboot的文件結(jié)構(gòu)及其啟動(dòng)流程,詳細(xì)給出了Uboot在基于ARM920T開發(fā)板上的移植方案,包括編譯、調(diào)試全過(guò)程,最終能夠在Uboot命令方式下加載映像文件,完成Linux內(nèi)核與yaffs映像文件的調(diào)試,具有Bootloader移植的通用性。
關(guān)鍵詞: Uboot; S3C2440; ARM920T; 引導(dǎo)過(guò)程; 啟動(dòng)代碼

1 Uboot移植環(huán)境準(zhǔn)備
1.1 移植平臺(tái)的硬件組成

 硬件平臺(tái)是ARM9的體系結(jié)構(gòu),ARM920T的CPU, SOC芯片是三星的S3C2440,支持Nand Flash與Nor Flash的可選啟動(dòng)方式,其主要硬件資源如表1所示[1]。

 支持Nand Flash與Nor Flash啟動(dòng),可以通過(guò)跳線來(lái)選擇啟動(dòng)方式。Nand Flash啟動(dòng)時(shí),最開始4 KB數(shù)據(jù)被硬拷貝到內(nèi)部Boot Internal SRAM,且被映射到nGCS0的片選空間0x0000,0000—0x0800,0000;Nor Flash方式啟動(dòng)時(shí),它直接被映射到nGCS0的片選空間。所以,在Uboot移植時(shí),要考慮將Uboot燒寫到Nor flash上還是Nand Flash上。
1.2 Uboot工作原理
    Uboot的整體結(jié)構(gòu)如圖1所示。

  從圖1可以看出,這種分層結(jié)構(gòu)的Uboot分模塊化了,給移植帶來(lái)了很大的方便。由于協(xié)議層與應(yīng)用層是與目標(biāo)硬件無(wú)關(guān)的,因此移植工作主要集中在物理層和驅(qū)動(dòng)層上面的修改。而Uboot支持串口下載、網(wǎng)絡(luò)下載,并提供了很多交互式命令。整個(gè)Uboot編譯、連接過(guò)程如下:
  (1)創(chuàng)建編譯環(huán)境
    在MAKEFILE中會(huì)調(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í)行二進(jìn)制映像elf文件U-boot[2],可以直接將生成的U-boot下載到SDRAM來(lái)單步調(diào)試。
2 Uboot的移植操作
2.1存儲(chǔ)器映射與存儲(chǔ)器重映射

    存儲(chǔ)器映射,實(shí)現(xiàn)了統(tǒng)一編址,方便了程序在32 bit尋址(4 GB尋址空間)的范圍內(nèi)能夠?qū)ぶ返饺我獾奈锢泶鎯?chǔ)區(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是上電后就運(yùn)行,因此需要將代碼定位在Flash從0x0000_0000的上電入口處。為了提高系統(tǒng)加載速度并且實(shí)現(xiàn)在線編程功能,需要將整個(gè)Uboot從Flash中搬到RAM運(yùn)行,即代碼從定位,將整個(gè)代碼定位到SDRAM的0x3300_0000之后,來(lái)作為其實(shí)際的運(yùn)行地址,具體如圖4所示。

2.2 配置主機(jī)運(yùn)行環(huán)境
    Uboot與Linux系統(tǒng)密切相關(guān),筆者在RH Linux的虛擬機(jī)中搭建了整個(gè)運(yùn)行環(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,則代表路徑設(shè)置正確,交叉編譯工具鏈已經(jīng)成功安裝。
2.3 修改CPU相關(guān)代碼
    在調(diào)試Uboot時(shí),如果每次都將二進(jìn)制映像燒錄到Flash中,不僅需要等待,而且操作麻煩,本文是在調(diào)試階段將二進(jìn)制映像直接燒錄到外部存儲(chǔ)器SDRAM中,然后直接從該處運(yùn)行,這樣直接在內(nèi)存中運(yùn)行,可以很方便地完成Uboot調(diào)試。
    Uboot啟動(dòng)的第一階段,從.\cpu\arm920t\start.s開始執(zhí)行,依次完成關(guān)閉看門狗、關(guān)閉中斷、設(shè)置CPU分頻比、初始化SDRAM、代碼重定位、設(shè)置堆棧,最后跳轉(zhuǎn)到C函數(shù)的入口點(diǎn)。當(dāng)在SDRAM中調(diào)試時(shí),內(nèi)存的初始化已經(jīng)預(yù)先完成了,因此不需要初始化SDRAM和代碼重定位的功能。
    在.\include\configs\qq2430.h添加宏定義define CONFIG_SKIP_LOWLEVEL_INIT,就會(huì)跳過(guò)cpu_init_crit處的初始化SDRAM函數(shù),代碼如下所示:
    #ifndef CONFIG_SKIP_LOWLEVEL_INIT
    cpu_init_crit:
    …
    當(dāng)Uboot在SDRAM中運(yùn)行時(shí),代碼的入口地址_start與代碼在SDRAM中重定位的地址_TEXT_BASE相同,直接跳轉(zhuǎn)到堆棧初始化處stack_setup,跳過(guò)了代碼的Flash到RAM的搬運(yùn)。代碼如下所示:
      adr    r0, _start        
         ldr    r1, _TEXT_BASE    
         cmp r0, r1                 
         beq  stack_setup
2.4 添加平臺(tái)相關(guān)代碼
    從匯編跳轉(zhuǎn)到C程序的入口點(diǎn)start_armboot時(shí),即位于./Lib_arm/Board.c中的start_armboot函數(shù),通過(guò)函數(shù)初始化數(shù)組來(lái)依次完成CPU(cpu_init)、板級(jí)(board_init)、中斷(interrupt_init)、環(huán)境變量(env_init)、串口(serial_init)、波特率(init_buardrate)、顯示(disp_banner)、Flash初始化(nand_init),每個(gè)初始化后返回不為0的值,否則永遠(yuǎn)在死循環(huán)中掛起,需要重新啟動(dòng)開發(fā)板。
2.4.1 鎖相環(huán)時(shí)鐘的配置
    在smdk2410開發(fā)板的基礎(chǔ)上修改。在./board/qq2440v3下新建qq2440v3.c,并且將Uboot中的./board/smdk2410/smdk2410.c內(nèi)容全部復(fù)制給qq2440v3.c。在include/configs下新建qq2440v3.h配置文件,同時(shí)將include/configs/smdk2410.h全部復(fù)制給qq2440v3.h。因?yàn)镾3C2440與S3C2410的最高運(yùn)行速率不一樣,系統(tǒng)時(shí)鐘設(shè)置也不一樣,即鎖相環(huán)配置有差異,因此內(nèi)部總線的分頻比系數(shù)是不同的。S3C2440可運(yùn)行于400 MHz,而S3C2410則是200 MHz,需要更改系統(tǒng)時(shí)鐘部分,使其增加對(duì)S3C2440的支持。
    鎖相環(huán)輸出的MPLL(fclk時(shí)鐘頻率寄存器)與UPLL(USB控制時(shí)鐘頻率寄存器)計(jì)算式如下:
    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)的預(yù)置值,控制輸出頻率, Fin為晶振頻率12 MHz。通過(guò)下面的宏定義分別給M、P、S賦值0x5c、0x01、0x01就能輕松完成時(shí)鐘配置。
    #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個(gè)UART,用異步通用串口uart0與上位機(jī)通信。串口時(shí)鐘用pclk時(shí)鐘,即1/2 hclk時(shí)鐘,1/8 fclk時(shí)鐘(mpll時(shí)鐘),get_PCLK()函數(shù)返回pclk時(shí)鐘頻率。串口0工作于中斷模式,通過(guò)FIFO緩存收發(fā),沒(méi)有校驗(yàn)位,1位停止位,通過(guò)設(shè)置ULCON0寄存器與UFCON0來(lái)配置。為了提高數(shù)據(jù)傳送的可靠性,使用了錯(cuò)誤接收中斷機(jī)制,能夠檢測(cè)溢出錯(cuò)誤、奇偶校驗(yàn)錯(cuò)誤和幀錯(cuò)誤,相關(guān)錯(cuò)誤狀態(tài)保存在錯(cuò)誤狀態(tài)寄存器UTRSTAT0的對(duì)應(yīng)位中。
    串口的波特率用115 200 b/s,通過(guò)給波特率除數(shù)寄存器UBRDIV0賦值,能夠確定串行發(fā)送接收波特率。計(jì)算公式如下:
    UBRDIVn=(int) (UART clock/(buad rate*16))-1;
    其中UART clock為pclk時(shí)鐘,buard rate為115 200。
2.5 編譯并調(diào)試
    為了在內(nèi)存中運(yùn)行,先要把鏈接腳本文件./Board/qq2440v3/U-boot.lds中的程序初始化運(yùn)行地址.=0x0000,0000改為.=0x3300,0000,以方便直接在RAM中運(yùn)行。在終端控制臺(tái)中先進(jìn)入U(xiǎn)boot的根目錄,然后執(zhí)行命令make qq2440v3_config,通過(guò)make all來(lái)編譯和連接程序。編譯沒(méi)有錯(cuò)誤的情況下,會(huì)在Uboot的根目錄下生成u-boot、u-boot.srec和u-boot.bin三個(gè)文件,分別對(duì)應(yīng)于ELF格式、S-Record格式和二進(jìn)制格式。
    直接使用JTAG燒寫二進(jìn)制u-boot.bin到SDRAM的0x3300,0000處,燒寫完成之后,將pc的指針指向該處運(yùn)行,會(huì)在串口終端上顯示板子的自檢信息,并給出提示符等待用戶輸入命令,如圖5所示。

3 Uboot從Flash中啟動(dòng)
  S3C2440既支持從Nor Flash中啟動(dòng),也支持從Nand Flash中啟動(dòng)。Nor Flash的地址線與數(shù)據(jù)線分開,方便代碼存取且速度很快,上電復(fù)位時(shí)直接把Nor Flash映射到了0x0000,0000地址處。但是Nand Flash數(shù)據(jù)線與地址線復(fù)用,運(yùn)行速度慢,為了提高Nand Flash啟動(dòng)效率,S3C2440芯片加入了一個(gè)特殊機(jī)制,會(huì)在上電復(fù)位時(shí),把Nand Flash前4 KB代碼硬拷貝到內(nèi)部SRAM的4 KB空間,然后將SRAM的4 KB空間映射到0x0000,0000處,這樣直接在SRAM中啟動(dòng)Uboot,節(jié)省了啟動(dòng)時(shí)間。
3.1 從Nor Flash中運(yùn)行
3.1.1添加Nor Flash驅(qū)動(dòng)

 board/qq2440v3/Flash.c中的驅(qū)動(dòng)只支持Nor Flash的AMDLV400和AMDLV800兩種芯片,不支持本文板子上的AM29LV160,更不支持Nand Flash,只能用CFI標(biāo)準(zhǔn)接口連接,在/drivers/cfiflash.c中定義了該接口標(biāo)準(zhǔn)下的讀寫函數(shù)的具體實(shí)現(xiàn)。要調(diào)用該驅(qū)動(dòng),應(yīng)在配置頭文件/include/configs/qq2440v3.h中添加CFI的宏定義:
    #define CFG_FLASH_CFI_DRIVER 1
    并在board/qq2440v3/Makefile中去掉原來(lái)的Nor flash驅(qū)動(dòng)的編譯,即:
    COBJS:= qq2440v3.o flash.o 變量中去掉flash.o的連接。
3.1.2 SDRam初始化并實(shí)現(xiàn)代碼從定位
    Uboot在Nor Flash中啟動(dòng)后,在start.s階段除了要完成必要的寄存器設(shè)置外,還要完成SDRAM的初始化以及代碼從定位,即把Flash空間的Uboot映像搬運(yùn)到SDRAM高地址空間中,然后在SDRAM中運(yùn)行Uboot??梢灾苯訌腘or Flash啟動(dòng)Uboot,但從Nand Flash啟動(dòng)要實(shí)現(xiàn)重定位,在這里就一起實(shí)現(xiàn)了。
    首先在.\include\configs\qq2430.h中去掉剛才添加宏定義define CONFIG_SKIP_LOWLEVEL_INIT,則會(huì)在start.s階段進(jìn)入cpu_init_crit函數(shù)以完成I/D caches設(shè)置以及禁止MMU,隨后進(jìn)入lowlevel_init完成內(nèi)存寄存器組的設(shè)置,如SDRAM位寬、刷新率等的初始化工作。
    在.\include\configs\qq2430.h中去掉CONFIG_SKIP_
RELOCATE_UBOOT的宏定義,來(lái)完成整個(gè)代碼的重定位[5]。
     Uboot代碼區(qū)的長(zhǎng)度為_bss_start-_armboot_start,其中_bss_start與_armboot_start變量保存的都是代碼段的起始地址與終止地址。_start+_bss_start-_armboot_start為代碼區(qū)結(jié)束的絕對(duì)地址,通過(guò)地址絕對(duì)尋址來(lái)復(fù)制代碼區(qū)的數(shù)據(jù)到內(nèi)存中TEXT_BASE地址區(qū)域,其中TEXT_BASE在.\Board\QQ2440v3\Config.mk中被賦值,即TEXT_BASE=0x33000000,表示代碼重定位在SDRAM中的運(yùn)行起始地址。
3.1.3 編譯并調(diào)試
    Uboot已經(jīng)能夠成功在SDRAM中啟動(dòng)運(yùn)行了,為了能夠從Nor Flash中啟動(dòng),需要做如下工作。
    先要把鏈接腳本文件./Board/qq2440v3/U-boot.lds中的程序初始化運(yùn)行地址.=0x3300,0000改為.=0x0000,0000,通過(guò)硬件開關(guān)選擇開機(jī)啟動(dòng)方式為Nor Flash,完成Nor Flash映射到0地址處。然后在終端控制臺(tái)編譯連接,直到?jīng)]有錯(cuò)誤。通過(guò)HJTAG燒錄進(jìn)Nor Flash里面,開機(jī)運(yùn)行后串口終端輸出界面如圖6所示。

3.2 從Nand Flash中運(yùn)行
3.2.1添加Nand Flash驅(qū)動(dòng)

    S3C2440支持從Nand Flash啟動(dòng),考慮到移植的通用性,對(duì)于沒(méi)有Nor Flash的板子,就需要從Nand Flash啟動(dòng)。在.\drivers目錄下有兩種Nand的驅(qū)動(dòng),.\Nand和.\Nand_legacy兩種驅(qū)動(dòng)可以選擇,其中.\Nand能夠自動(dòng)識(shí)別很多型號(hào)的Nand Flash,并且是更新版本,因此選擇這種驅(qū)動(dòng)。根據(jù)Nand.c中的
    #if(CONFIG_COMMANDS&CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
    #include <nand.h>
    條件編譯選擇Nand驅(qū)動(dòng),首先在板級(jí)配置頭文件qq2440v3.h中的宏定義CONFIG_COMMAND中添加CFG_CMD_NAND,并且不定義CFG_NAND_LEGACY。在start_armboot()函數(shù)中會(huì)對(duì)外設(shè)逐一初始化,Nand初始化代碼如下:
    #ifdefined(CFG_MAX_NAND_DEVICE) nand_init;
    #endif
    需要在板級(jí)配置頭文件qq2440v3.h中宏定義CFG_MAX_NAND_DEVICE,因?yàn)閟mdk2410開發(fā)板不支持Nand Flash,因此需要自己來(lái)編寫Nand Flash驅(qū)動(dòng)函數(shù)board_nand_init來(lái)被nand_init以及nand_init_chip調(diào)用,以完成Nand Flash的硬件初始化,包括使能Nand Flash控制器、初始化ECC、使能片選信號(hào)、設(shè)置時(shí)序等。
3.2.2添加cmd命令
    為了豐富Nand與網(wǎng)卡功能,還需要在配置文件中添加Nand與網(wǎng)卡相關(guān)命令來(lái)調(diào)用相關(guān)函數(shù)。在板級(jí)配置頭文件qq2440v3.h中的CONFIG_COMMANDS宏定義中以邏輯“或”的形式添加CFG_CMD_NAND與CFG_CMD_NET,這樣便可以通過(guò)命令方式實(shí)現(xiàn)Nand Flash的讀寫以及網(wǎng)絡(luò)下載功能。
    Uboot的網(wǎng)絡(luò)功能很強(qiáng)大,可以方便地通過(guò)TFTP引導(dǎo)或者是NFS引導(dǎo)內(nèi)核映像或者文件系統(tǒng)到SDRAM,然后直接go到此處執(zhí)行,在SDRAM中調(diào)試完成后,再將映像文件燒錄到Flash中,不僅調(diào)試方便,而且還節(jié)省下載時(shí)間。
3.2.3 編譯并調(diào)試
    編譯過(guò)程跟Nor Flash啟動(dòng)一樣,最后串口輸出信息如圖7所示。

    此時(shí),整個(gè)Uboot的移植就算完成了,由于支持串口跟網(wǎng)卡驅(qū)動(dòng),可以很方便地用這個(gè)Uboot來(lái)通過(guò)網(wǎng)卡下載內(nèi)核映像與文件系統(tǒng)到Flash,通過(guò)串口輸出信息調(diào)試Uboot。
4 Uboot引導(dǎo)Linux內(nèi)核
4.1 內(nèi)核啟動(dòng)參數(shù)的傳遞

    Uboot在引導(dǎo)內(nèi)核啟動(dòng)時(shí),通過(guò)標(biāo)記列表的方式向內(nèi)核傳遞啟動(dòng)參數(shù)。這些啟動(dòng)參數(shù)預(yù)先以環(huán)境變量的方式保存在Flash中,在./Lib_arm/Board.c中的初始化環(huán)境變量函數(shù)env_init()初始化,下面的函數(shù)來(lái)實(shí)現(xiàn)向kernel跳轉(zhuǎn)。
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params); thekernel其實(shí)不是個(gè)函數(shù),而是指向內(nèi)核入口地址的指針,為0x30008000。這里把它強(qiáng)行轉(zhuǎn)化為帶三個(gè)參數(shù)的函數(shù)指針,會(huì)把三個(gè)參數(shù)保存到通用寄存器中,實(shí)現(xiàn)了向kernel傳遞信息的功能,R0賦值為0,R1賦值為機(jī)器號(hào),R2賦值為啟動(dòng)參數(shù)數(shù)據(jù)結(jié)構(gòu)的首地址[6]。
   標(biāo)記列表實(shí)際上是內(nèi)存中的結(jié)構(gòu)體組成的列表,在./Lib_arm/Armlinux.c中函數(shù)setup_start_tag()來(lái)創(chuàng)建標(biāo)記列表。
4.2 tftp加載內(nèi)核映像
 對(duì)于已經(jīng)編譯好了的內(nèi)核映像文件zImage,其格式是ELF的可執(zhí)行文件,首先要把它轉(zhuǎn)化成U-boot格式的文件uImage,實(shí)際是添加了一個(gè)header定義,直接用tools目錄下的工具mkimage就可用實(shí)現(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)核文件中提取二進(jìn)制文件,然后壓縮,最后構(gòu)造文件頭信息,包括名稱、大小、類型、CRC校驗(yàn)碼等,添加的頭信息占用0x40大小空間。完成后下載內(nèi)核映像uImage,如下操作:
    開啟主機(jī)tftp服務(wù),將uImage放置tftp目錄下,然后啟動(dòng)Uboot,運(yùn)行tftp下載,镲除、燒寫Nand Flash,具體如圖8所示。

    最后燒寫文件系統(tǒng)映像,與燒寫內(nèi)核映像一樣,先tftp下載到內(nèi)存,然后再燒寫,不同類型的文件系統(tǒng)nand燒寫命令不一樣,本文用到的是yaffs文件類型,則通過(guò)Nand write.yaffs 0x30000000 0x1d0000 $(filesize)命令來(lái)燒寫。
    本文研究了Uboot在基于S3C2440系統(tǒng)上的移植,并且提出了可行性方案,通過(guò)邊搭建硬件環(huán)境邊調(diào)試Uboot,使Uboot在嵌入式系統(tǒng)板上正常運(yùn)行,實(shí)現(xiàn)了串口通信、網(wǎng)口下載、Flash燒錄等功能,并且成功引導(dǎo)了Linux系統(tǒng),為后續(xù)的系統(tǒng)驅(qū)動(dòng)程序開發(fā)奠定了基礎(chǔ),使得Uboot的移植具有開發(fā)的通用性。

參考文獻(xiàn)
[1]  劉淼.嵌入式系統(tǒng)接口設(shè)計(jì)與Linux驅(qū)動(dòng)程序開發(fā)[M]. 北京:北京航空航天大學(xué)出版社,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.

此內(nèi)容為AET網(wǎng)站原創(chuàng),未經(jīng)授權(quán)禁止轉(zhuǎn)載。