新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > u-boot 啟動過程 —— 基于S3C2410

u-boot 啟動過程 —— 基于S3C2410

作者: 時間:2016-11-10 來源:網(wǎng)絡(luò) 收藏
本文以流行的Samsung公司的S3C2410,openmoko平臺和u-boot-1.3.2(2008.5 發(fā)布)為例,介紹如何在ZIX嵌入式開發(fā)環(huán)境下探索u-boot啟動過程。

雖然u-boot已經(jīng)廣泛應(yīng)用,由于其相對的復(fù)雜性使用戶在了解其內(nèi)部機(jī)理和進(jìn)行u-boot的移植工作時還是會碰到困難。u-boot已有一些分析文檔,但多數(shù)和真正的代碼不能同步或者版本老舊,難以將概念和現(xiàn)實的代碼匹配——即硬件板上跑的代碼在文檔資料中卻看不到,更無法緊密的跟蹤。本文涉及的代碼基于在s3c2410硬件運行的成熟u-boot-1.3.2代碼,版本較新,提供的特性非常豐富,而且在forum.linuxbj.com可以自由瀏覽和下載。此u-boot代表了業(yè)界的較高水平,可以直接構(gòu)建新版的嵌入式產(chǎn)品設(shè)計,有較高的應(yīng)用價值。

本文引用地址:http://butianyuan.cn/article/201611/317161.htm

u-boot總的啟動流程如下
->reset
-> 設(shè)置CPU模式
-> 關(guān)閉看門狗/中斷
-> 設(shè)置處理器時鐘/片上總線
-> 初始化調(diào)試串口
-> MMU/外部總線/SDRAM等初始化
-> rom代碼/數(shù)據(jù)搬移到ram
-> 初始化函數(shù)調(diào)用棧
-> 初始化外圍設(shè)備/參數(shù)
->啟動完畢,進(jìn)入main_loop循環(huán)

嵌入式系統(tǒng)離不開bootloader初始化硬件以及引導(dǎo)操作系統(tǒng)。
現(xiàn)在,專用的嵌入式板子運行嵌入式Linux系統(tǒng)已經(jīng)變得非常流行,u-boot是一種非常適合此類系統(tǒng)的bootloader。

u-boot主要提供以下功能:

  • 設(shè)置目標(biāo)板硬件參數(shù)并初始化;
  • 為操作系統(tǒng)傳遞必要信息;
  • 執(zhí)行交互式的底層操作;
  • 智能化裝載操作系統(tǒng);
  • 引導(dǎo)和運行的固件程序;
  • 支持大容量存儲和USB接口

利用ZIX開發(fā)環(huán)境,能夠通過比較直觀的方式觀察u-boot內(nèi)部,而且可以將代碼調(diào)試和分析同時進(jìn)行,是一種了解、移植u-boot的強大工具。

使用arm工具鏈編譯u-boot源代碼,得到可以燒錄的u-boot.bin文件。
在ZIX開發(fā)環(huán)境里,可以將u-boot.bin載入s3c2410板運行,并利用gdb調(diào)試。

gdb能通過JTAG接口訪問硬件,也可以通過TCP/IP訪問虛擬硬件。 建立好調(diào)試連接,即可通過gdb操縱u-boot啟動過程,下面可以跟隨代碼的執(zhí)行順序,了解從上點開始,究竟哪些操作被執(zhí)行。

s3c2410復(fù)位之后,pc指針會指向0x0地址。在u-boot代碼中,該0x0地址是一個向量表,
第一條指令跳轉(zhuǎn)branch到復(fù)位代碼start_code。 位于cpu/arm920t/start.S匯編語言文件第53行:

DE>.globl _start  _start: DE> DE>b start_code DE> DE>    DE>ldr pc, _undefined_instruction DE>    DE>ldr pc, _software_interrupt DE>    DE>ldr pc, _prefetch_abort DE>    DE>ldr pc, _data_abort DE>    DE>ldr pc, _not_used DE>    DE>ldr pc, _irq DE>    DE>ldr pc, _fiq

復(fù)位指令跳轉(zhuǎn)之后來到第154行,開始執(zhí)行arm920t處理器的基本初始化。
首先修改當(dāng)前程序狀態(tài)寄存器CPSR,使處理器進(jìn)入Supervisor|32 bit ARM模式,
并關(guān)閉ARM9TDMI中斷和快速中斷,這是通過設(shè)置CPSR相應(yīng)掩碼實現(xiàn)的:

DE>start_code: DE>DE>    DE>DE>/* DE>DE>    DE>DE> * set the cpu to SVC32 mode DE>DE>    DE>DE> */  DE>DE>    DE>DE>mrs r0,cpsr DE>DE>    DE>DE>bic r0,r0,#0x1f DE>DE>    DE>DE>orr r0,r0,#0xd3 DE>DE>    DE>DE>msr cpsr,r0 DE> 

緊接著,將S3C2410特有的WTCON寄存器清零,此舉僅為關(guān)閉看門狗,代碼位置是234行:

DE>    DE>DE>ldr r0, =pWTCON DE>DE>    DE>DE>mov r1, #0x0 DE>DE>    DE>DE>str r1, [r0] DE>

然后在241行,將S3C2410中斷控制器INTMSK寄存器置為全1,
INTSUBMSK置為0x7ff,禁止全部中斷源。S3C2410手冊358頁起對此有詳細(xì)描述:

DE>    DE>DE>mov r1, #0xffffffff  DE>DE>    DE>DE>ldr r0, =INTMSK DE>DE>    DE>DE>str r1, [r0]  # if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442) ||   DE>DE>  DE>DE>defined(CONFIG_S3C2443)  DE>DE>    DE>DE>ldr r1, =INTSUBMSK_val  DE>DE>    DE>DE>ldr r0, =INTSUBMSK  DE>DE>    DE>DE>str r1, [r0]  # endif DE> 

接下來在259行,訪問arm920t控制寄存器CP15,并置位最高兩位〔31,30〕。
此兩位置為0b11后,處理器時鐘被設(shè)置為異步模式,允許處理器異步訪問總線:

DE>    DE>DE>mrc p15, 0, r1, c1, c0, 0 DE>DE>    DE>DE>orr r1, r1, #0xc0000000 DE>DE>    DE>DE>mcr p15, 0, r1, c1, c0, 0 DE>

至此arm920t相關(guān)的配置完成,后面開始設(shè)定S3C2410時鐘合成參數(shù)。
通過設(shè)置UPLL,MPLL和CLKDIVN三個寄存器(在S3C2410手冊237頁起講述),
得到需要的處理器工作頻率,分別在308行:

DE>    DE>DE>ldr r0, =UPLLCON  DE>DE>    DE>DE>ldr r1, =UPLLCON_val DE>DE>    DE>DE>str r1, [r0] DE>

321行:

DE>    DE>DE>ldr r1, =MPLLCON_val  DE>DE>    DE>DE>str r1, [r0, #-4]DE>DE>    DE>DE>/* MPLLCON */DE>DE>DE>DE>    DE>DE>/* FCLK:HCLK:PCLK = 1:2:4 */ DE>DE>    DE>DE>ldr r0, =CLKDIVN DE>DE>    DE>DE>mov r1, #CLKDIVN_val DE>DE>    DE>DE>str r1, [r0] DE>

S3C2410的UART0得到初始化,以便于盡早通過UART0打印信息。
此段代碼從332行開始,其中涉及到的寄存器讀者可參考S3C2410手冊293頁起:

DE>    DE>DE>/* enable uart */DE>DE>    DE>DE>ldr r0, =0x4c00000c /* clkcon */ DE>DE>    DE>DE>ldr r1, =0x7fff0 /* all clocks on */ DE>DE>    DE>DE>str r1, [r0] DE>DE>DE>DE>    DE>DE>/* gpio UART0 init */ DE>DE>    DE>DE>ldr r0, =0x56000070 DE>DE>    DE>DE>mov r1, #0xaa DE>DE>    DE>DE>str r1, [r0] DE>DE>DE>DE>    DE>DE>/* init uart */ DE>DE>    DE>DE>ldr r0, =0x50000000 DE>DE>    DE>DE>mov r1, #0x03 DE>DE>    DE>DE>str r1, [r0] DE>DE>    DE>DE>ldr r1, =0x245 DE>DE>    DE>DE>str r1, [r0, #0x04] DE>DE>    DE>DE>mov r1, #0x01 DE>DE>    DE>DE>str r1, [r0, #0x08] DE>DE>    DE>DE>mov r1, #0x00 DE>DE>    DE>DE>str r1, [r0, #0x0c] DE>DE>    DE>DE>mov r1, #0x1a DE>DE>    DE>DE>str r1, [r0, #0x28] DE>

完成UART0設(shè)置之后,根據(jù)不同的編譯時選項和運行時參數(shù),代碼會在360行進(jìn)入相應(yīng)的分支,分別是

  1. 從nand啟動,代碼執(zhí)行l(wèi)owlevel_init,主要是清除cpu cache,以及關(guān)閉mmu和i-cache,
    并且根據(jù)板極硬件配置初始化外部存儲器總線和GPIO,最后把代碼從nandflash中拷貝到ram中并繼續(xù)執(zhí)行。
  2. 從nor啟動,與第1種情況相比,僅僅把代碼拷貝部分簡化,將DATA段從flash中拷貝到ram中,其余相同
  3. 從ram啟動,因為u-boot已經(jīng)處于配置好的ram中,
    所以會跳過所有cache,mmu,sdram,nand和nor相關(guān)代碼,跳轉(zhuǎn)到done_relocate執(zhí)行

下面以最復(fù)雜的nand啟動情況為例分析。首先會跳轉(zhuǎn)到572行執(zhí)行cpu_init_crit,
通過操作CP15完成flush處理器arm920t的cache和tlb,并關(guān)閉mmu和i-cache:

DE>cpu_init_crit:DE>DE>    DE>DE>/*DE>DE>    DE>DE> * flush v4 I/D caches DE>DE>     DE>DE>*/  DE>DE>    DE>DE>mov r0, #0 DE>DE>    DE>DE>mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ DE>DE>    DE>DE>mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ DE>DE>  DE>DE>    DE>DE>/*DE>DE>    DE>DE> * disable MMU stuff and cachesDE>DE>    DE>DE>*/ DE>DE>    DE>DE>mrc p15, 0, r0, c1, c0, 0 DE>DE>    DE>DE>bic r0, r0, #0x00002300DE>DE>     DE>DE>@ clear bits 13, 9:8 (--V- --RS) DE>DE>    DE>DE>bic r0, r0, #0x00000087 DE>DE>    DE>DE>@ clear bits 7, 2:0 (B--- -CAM) DE>DE>    DE>DE>orr r0, r0, #0x00000002 DE>DE>    DE>DE>@ set bit 2 (A) Align DE>DE>    DE>DE>orr r0, r0, #0x00001000 DE>DE>    DE>DE>@ set bit 12 (I) I-Cache DE>DE>    DE>DE>mcr p15, 0, r0, c1, c0, 0 DE>

然后跳轉(zhuǎn)到board/neo1973/common/lowlevel_init.S文件的139行執(zhí)行,
進(jìn)行總線數(shù)據(jù)寬度、時序、SDRAM控制、GPIO等配置,配置完畢后會返回start.S繼續(xù)執(zhí)行。
因為該代碼是與板相關(guān),故放在board目錄里面。由于代碼較多,只粘貼開始部分:

DE>    DE>DE>/* memory control configuration */  DE>DE>    DE>DE>/* make r0 relative the current location so that it */  DE>DE>    DE>DE>/* reads SMRDATA out of FLASH rather than memory ! */ DE>DE>    DE>DE>adr r0, SMRDATA  DE>DE>    DE>DE>ldr r1, =BWSCON /* Bus Width Status Controller */ DE>DE>    DE>DE>add r2, r0, #13*4 DE>

完成板級設(shè)置后,在cpu/arm920t/start.S的373行判斷代碼自身的執(zhí)行位置。如果從stepping stone內(nèi)執(zhí)行,
并且u-boot配置為nandboot模式,則跳轉(zhuǎn)到nand_load拷貝代碼:

DE>    DE>DE>ldr r1, =BWSCON /* Z = CPU booted from NAND */ DE>DE>    DE>DE>ldr r1, [r1] DE>DE>    DE>DE>tst r1, #6 /* BWSCON[2:1] = OM[1:0] */ DE>DE>    DE>DE>teqeq r0, #0 /* Z &= running at address 0 */ DE>DE>    DE>DE>beq nand_load DE>

在417行是nand_load代碼,首先會跳轉(zhuǎn)到614行執(zhí)行may_resume
以檢測系統(tǒng)是從待機(jī)模式喚醒還是上電啟動。如果喚醒,則會根據(jù)之前保存的現(xiàn)場進(jìn)行相應(yīng)處理,
本文不做更多敘述;如果是啟動,則會返回nand_load繼續(xù)執(zhí)行。在nand_load里初始化s3c2410的nandcontroller,
涉及存儲器映射和寄存器NFCONF等,參見S3C2410手冊215頁起。同樣,僅粘貼開始部分的代碼:

DE>    DE>DE>mov r1, #S3C2410_NAND_BASE DE>DE>    DE>DE>ldr r2, =0xf842 @ initial value enable tacls=3,rph0=6,rph1=0 DE>DE>    DE>DE>str r2, [r1, #oNFCONF] DE>DE>    DE>DE>ldr r2, [r1, #oNFCONF] DE>DE>    DE>DE>bic r2, r2, #0x800 @ enable chip DE>

在451行繼續(xù)根據(jù)配置設(shè)定棧指針,為后面調(diào)用C函數(shù)執(zhí)行拷貝作準(zhǔn)備:

DE>    DE>DE>ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ DE>DE>    DE>DE>sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ DE>DE>    DE>DE>sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ DE>DE>    DE>DE>sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif DE>DE>    DE>DE>sub sp, r0, #12 /* leave 3 words for abort-stack */ DE>

然后在460行,將SDRAM中的目標(biāo)地址存入r0,0x0地址存入r1,u-boot長度存入r2,
跳入cpu/arm920t/s3c24x0/nand_read.c文件第154行執(zhí)行nand_read_ll函數(shù),該函數(shù)接受前面3個寄存器中的值作為參數(shù),并將返回值放回r0:

DE>    DE>DE>ldr r0, _TEXT_BASE DE>DE>    DE>DE>mov r1, #0x0 DE>DE>    DE>DE>mov r2, #CFG_UBOOT_SIZE DE>DE>    DE>DE>bl nand_read_ll DE>

在nand_read_ll函數(shù)中實現(xiàn)了nandflash訪問代碼,并且支持自動跳過壞塊的特性,函數(shù)循環(huán)執(zhí)行nand頁面讀取并存入SDRAM,直到u-boot全部拷貝完,并返回0,該C代碼留給讀者自己閱讀。nand_read_ll返回0后,會跳轉(zhuǎn)到ok_nand_read,并482行對拷貝的頭4K字節(jié)進(jìn)行校驗:

DE>    @ verify mov r0, #0 @ldr r1, =0x33f00000 ldr r1, _TEXT_BASE mov r2, #0x400 DE>DE>    DE>DE>@ 4 bytes * 1024 = 4K-bytes go_next: DE>DE>    DE>DE>ldr r3, [r0], #4 DE>DE>    DE>DE>ldr r4, [r1], #4 DE>DE>    DE>DE>teq r3, r4 DE>DE>    DE>DE>bne notmatch DE>DE>    DE>DE>subs r2, r2, #4 DE>DE>    DE>DE>beq done_nand_read DE>DE>    DE>DE>bne go_next DE>

校驗通過后代碼506行,在地址為_booted_from_nand的SDRAM位置保存1,以便告訴上層軟件本次啟動是從nand引導(dǎo):

DE>done_nand_read: DE>DE>    DE>DE>ldr r0, _booted_from_nand DE>DE>    DE>DE>mov r1, #1 DE>DE>    DE>DE>strb r1, [r0] DE>

然后在518行,將中斷向量表拷貝到0x0:

DE>    DE>DE>mov r0, #0 DE>DE>    DE>DE>ldr r1, _TEXT_BASE DE>DE>    DE>DE>mov r2, #0x40 irqvec_cpy_next: DE>DE>    DE>DE>ldr r3, [r1], #4 DE>DE>    DE>DE>str r3, [r0], #4 DE>DE>    DE>DE>subs r2, r2, #4 DE>DE>    DE>DE>bne irqvec_cpy_next DE>

在532行,設(shè)置棧指針:

DE>    DE>DE>ldr r0, _TEXT_BASEDE>DE>    DE>DE>/* upper 128 KiB: relocated uboot */ DE>DE>    DE>DE>sub r0, r0, #CFG_MALLOC_LENDE>DE>    DE>DE>/* malloc area */ DE>DE>    DE>DE>sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ DE>DE>    DE>DE>sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif DE>DE>    DE>DE>sub sp, r0, #12DE>DE>    DE>DE>/* leave 3 words for abort-stack */ DE>

在541行,清除bss段并跳轉(zhuǎn)到真正的C函數(shù)start_armboot,進(jìn)入更高級的硬件初始化代碼,匯編初始化部分也全部完成使命:

DE>    DE>DE>ldr r0, _bss_start /* find start of bss segment */ DE>DE>    DE>DE>ldr r1, _bss_end /* stop here */ DE>DE>    DE>DE>mov r2, #0x00000000 /* clear */ DE>DE> clbss_l:DE>DE>    DE>DE>str r2, [r0] /* clear loop... */ DE>DE>    DE>DE>add r0, r0, #4 DE>DE>    DE>DE>cmp r0, r1 DE>DE>    DE>DE>ble clbss_l DE>DE> DE>DE>    DE>DE>ldr pc, _start_armboot DE>

start_armboot函數(shù)位于lib_arm/board.c文件第277行,首先初始化globel_data類型的變量gd。該變量是一個結(jié)構(gòu),其成員大多是板子的一些基本設(shè)置,如序列號、ip地址、mac地址等(欲知結(jié)構(gòu)的原型可參考include/asm-arm/globel_data.h和include/asm-arm/u-boot.h):

DE>         DE>DE>gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));DE>DE>         DE>DE>/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory");DE>DE>         DE>DE>memset ((void*)gd, 0, sizeof (gd_t));gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));DE>DE>         DE>DE>memset (gd->bd, 0, sizeof (bd_t)); DE>

然后在一個for循環(huán)中從init_sequence地址開始,逐個調(diào)用初始化C函數(shù)至NULL為止。這些routine函數(shù)按調(diào)用順序分別是

  1. cpu_init() ——在common/main.c文件,執(zhí)行初始化中斷棧操作
  2. board_init() ——在board/neo1973/gta01/gta01.c文件中,執(zhí)行板級初始化,主要是更新GPIO和PLL設(shè)置
  3. interrupt_init() ——在/cpu/arm920t/s3c24x0/interrupts.c文件中,執(zhí)行時鐘中斷初始化
  4. env_init() ——在common/env_nand.c文件中,設(shè)置缺省環(huán)境變量
  5. init_baudrate() ——在lib_arm/board.c文件中,將環(huán)境變量中的baudrate存入bd_info結(jié)構(gòu)bd
  6. serial_init() ——在common/serial.c文件中,調(diào)用驅(qū)動中真正的init()初始化串口
  7. console_init_f() ——在common/console.c文件中,更新global_data結(jié)構(gòu)gd的have_console標(biāo)記為1
  8. display_banner() ——在lib_arm/board.c文件中,打印u-boot banner,輸出版本、運行地址等信息。比如在控制臺看到的
  9. init_func_i2c() ——在lib_arm/board.c文件中,初始化i2c總線
  10. dram_init() ——在board/neo1973/gta01/gta01.c文件中,填充bd->bi_dram[0]的start和size成員,用來描述u-boot可用的ram
  11. display_dram_config() ——在board/neo1973/gta01/gta01.c文件中,打印當(dāng)前ram配置。在控制臺能夠看到相應(yīng)的 DRAM: 128 MB

利用gdb可以清晰的看到調(diào)用過程:

DE>         DE>DE>for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { DE>DE>         DE>DE>         DE>DE>if ((*init_fnc_ptr)() != 0) { DE>DE>         DE>DE>         DE>DE>         DE>DE>hang (); DE>DE>         DE>DE>         DE>DE>} DE>DE>         DE>DE>} DE>

接著是一些可選外設(shè)的初始化,如顯示屏、nor、nand、dataflash、網(wǎng)卡等,此過程執(zhí)行后全部初始化工作完成。下面僅粘貼nor代碼:

DE>#ifndef CFG_NO_FLASH DE>DE>         DE>DE>/* configure available FLASH banks */ DE>DE>         DE>DE>size = flash_init (); DE>DE>         DE>DE>display_flash_config (size); #endif /* CFG_NO_FLASH */ DE>

之后在457行進(jìn)入無限循環(huán),調(diào)用common/main.c文件的278行main_loop()函數(shù),u-boot完成啟動過程。main_loop提供一個交互式命令行,可通過串口或usb控制臺操作,也可以進(jìn)一步引導(dǎo)操作系統(tǒng):

DE>         DE>DE>for (;;) { DE>DE>         DE>DE>         DE>DE>main_loop (); DE>DE>         DE>DE>} DE>


關(guān)鍵詞: u-boot啟動過程S3C241

評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉