新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > u-boot 啟動過程 —— 基于S3C2410

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

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

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

        本文引用地址:http://www.104case.com/article/201611/317161.htm

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

        嵌入式系統離不開bootloader初始化硬件以及引導操作系統。
        現在,專用的嵌入式板子運行嵌入式Linux系統已經變得非常流行,u-boot是一種非常適合此類系統的bootloader。

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

        • 設置目標板硬件參數并初始化;
        • 為操作系統傳遞必要信息;
        • 執行交互式的底層操作;
        • 智能化裝載操作系統;
        • 引導和運行的固件程序;
        • 支持大容量存儲和USB接口

        利用ZIX開發環境,能夠通過比較直觀的方式觀察u-boot內部,而且可以將代碼調試和分析同時進行,是一種了解、移植u-boot的強大工具。

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

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

        s3c2410復位之后,pc指針會指向0x0地址。在u-boot代碼中,該0x0地址是一個向量表,
        第一條指令跳轉branch到復位代碼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

        復位指令跳轉之后來到第154行,開始執行arm920t處理器的基本初始化。
        首先修改當前程序狀態寄存器CPSR,使處理器進入Supervisor|32 bit ARM模式,
        并關閉ARM9TDMI中斷和快速中斷,這是通過設置CPSR相應掩碼實現的:

        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寄存器清零,此舉僅為關閉看門狗,代碼位置是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頁起對此有詳細描述:

        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后,處理器時鐘被設置為異步模式,允許處理器異步訪問總線:

        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相關的配置完成,后面開始設定S3C2410時鐘合成參數。
        通過設置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設置之后,根據不同的編譯時選項和運行時參數,代碼會在360行進入相應的分支,分別是

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

        下面以最復雜的nand啟動情況為例分析。首先會跳轉到572行執行cpu_init_crit,
        通過操作CP15完成flush處理器arm920t的cache和tlb,并關閉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>

        然后跳轉到board/neo1973/common/lowlevel_init.S文件的139行執行,
        進行總線數據寬度、時序、SDRAM控制、GPIO等配置,配置完畢后會返回start.S繼續執行。
        因為該代碼是與板相關,故放在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>

        完成板級設置后,在cpu/arm920t/start.S的373行判斷代碼自身的執行位置。如果從stepping stone內執行,
        并且u-boot配置為nandboot模式,則跳轉到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代碼,首先會跳轉到614行執行may_resume
        以檢測系統是從待機模式喚醒還是上電啟動。如果喚醒,則會根據之前保存的現場進行相應處理,
        本文不做更多敘述;如果是啟動,則會返回nand_load繼續執行。在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行繼續根據配置設定棧指針,為后面調用C函數執行拷貝作準備:

        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中的目標地址存入r0,0x0地址存入r1,u-boot長度存入r2,
        跳入cpu/arm920t/s3c24x0/nand_read.c文件第154行執行nand_read_ll函數,該函數接受前面3個寄存器中的值作為參數,并將返回值放回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函數中實現了nandflash訪問代碼,并且支持自動跳過壞塊的特性,函數循環執行nand頁面讀取并存入SDRAM,直到u-boot全部拷貝完,并返回0,該C代碼留給讀者自己閱讀。nand_read_ll返回0后,會跳轉到ok_nand_read,并482行對拷貝的頭4K字節進行校驗:

        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引導:

        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行,設置棧指針:

        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段并跳轉到真正的C函數start_armboot,進入更高級的硬件初始化代碼,匯編初始化部分也全部完成使命:

        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函數位于lib_arm/board.c文件第277行,首先初始化globel_data類型的變量gd。該變量是一個結構,其成員大多是板子的一些基本設置,如序列號、ip地址、mac地址等(欲知結構的原型可參考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循環中從init_sequence地址開始,逐個調用初始化C函數至NULL為止。這些routine函數按調用順序分別是

        1. cpu_init() ——在common/main.c文件,執行初始化中斷棧操作
        2. board_init() ——在board/neo1973/gta01/gta01.c文件中,執行板級初始化,主要是更新GPIO和PLL設置
        3. interrupt_init() ——在/cpu/arm920t/s3c24x0/interrupts.c文件中,執行時鐘中斷初始化
        4. env_init() ——在common/env_nand.c文件中,設置缺省環境變量
        5. init_baudrate() ——在lib_arm/board.c文件中,將環境變量中的baudrate存入bd_info結構bd
        6. serial_init() ——在common/serial.c文件中,調用驅動中真正的init()初始化串口
        7. console_init_f() ——在common/console.c文件中,更新global_data結構gd的have_console標記為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文件中,打印當前ram配置。在控制臺能夠看到相應的 DRAM: 128 MB

        利用gdb可以清晰的看到調用過程:

        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>

        接著是一些可選外設的初始化,如顯示屏、nor、nand、dataflash、網卡等,此過程執行后全部初始化工作完成。下面僅粘貼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行進入無限循環,調用common/main.c文件的278行main_loop()函數,u-boot完成啟動過程。main_loop提供一個交互式命令行,可通過串口或usb控制臺操作,也可以進一步引導操作系統:

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


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 大荔县| 无为县| 梅州市| 河西区| 江孜县| 丹巴县| 乐业县| 嵊州市| 利辛县| 曲水县| 湘西| 临漳县| 东至县| 台前县| 会昌县| 长春市| 五大连池市| 抚宁县| 明水县| 舟曲县| 惠水县| 民权县| 滁州市| 曲麻莱县| 广州市| 浑源县| 衡山县| 邢台县| 广饶县| 大城县| 丰顺县| 泽普县| 辽源市| 全椒县| 兰考县| 沙坪坝区| 杨浦区| 浦东新区| 日照市| 莒南县| 安远县|