| mips架構(gòu)u-boot啟動流程 u-boot的啟動過程大致做如下工作: 1、cpu初始化 2、時鐘、串口、內(nèi)存(ddr ram)初始化 3、內(nèi)存劃分、分配棧、數(shù)據(jù)、配置參數(shù)、以及u-boot代碼在內(nèi)存中的位置。 4、對u-boot代碼作relocate 5、初始化malloc、flash、pci以及外設(shè)(比如,網(wǎng)口) 6、進(jìn)入命令行或者直接啟動Linux kernel 整個啟動中要涉及到四個文件: start_bootstrap.S à cpu/mips/start_bootstrap.S Lowlevel_init.S à board/ar7240/common/lowlevel_init.S Cache.S à cpu/mips/cache.S Board.c à lib_mips/board.c 整個啟動過程分為兩個階段來看: Stage1:系統(tǒng)上電后執(zhí)行匯編代碼 Stage2:通過一些列設(shè)置搭建了C環(huán)境,通過匯編指令跳轉(zhuǎn)到C語言執(zhí)行. Stage1: 程序從cpu/mips/start_bootstrap.S的_start_bootstrap開始執(zhí)行.(至于為什么,參考u-boot.lds分析.doc) 先查看start_bootstrap.S文件吧!~ 從_start_bootstrap標(biāo)記開始會看到一長串莫名奇妙的代碼: RVECENT(reset,0) /* U-boot entry point */ /*U-Boot開始執(zhí)行的代碼起始地址*/ RVECENT(reset,1) /* software reboot */ /*軟重啟時U-Boot開始執(zhí)行的起始地址*/ RVECENT(romReserved,2) /*保留本代碼所在的地址,重新映射調(diào)試異常向量時可以使用該空間*/ RVECENT(romReserved,3) RVECENT(romReserved,4) RVECENT(romReserved,5) RVECENT(romReserved,6) RVECENT(romReserved,7) RVECENT(romReserved,8) RVECENT(romReserved,9) … … 回過頭看剛開始的定義有這樣的代碼: 可以找到: #defineRVECENT(f,n) \ b f; nop 原來這只是一個簡單的跳轉(zhuǎn)指令,f為一個標(biāo)記,b為跳轉(zhuǎn)指令。 然后看最后,發(fā)現(xiàn): romReserved: b romReserved romExcHandle: b romExcHandle 這兩個標(biāo)記都構(gòu)建了無意義的死循環(huán)。 通過_start標(biāo)記處的語句RVECENT(reset,0) 代碼跳轉(zhuǎn)到標(biāo)記reset的地方,該段代碼的操作就是對寄存器的清零操作了。Mfc0和mtc0指令是對寄存器的一些讀寫. Mips 寄存器:http://blog.csdn.net/flyingqr/article/details/7073088 在接下來是對協(xié)處理器的操作了,其中包括: CP0_WATCHLO, CP0_WATCHHI, CP0_STATUS CP0_CAUSE, CP0_COUNT, CP0_COMPARE CP0_CONFIG 之后,配置寄存器CP0_STATUS,設(shè)置所使用的協(xié)處理器,中斷以及cpu運(yùn)行級別(核心級)。 配置gp寄存器,把GOT段的地址賦給gp寄存器。(gp寄存器的用處會在后面relocate code部分詳細(xì)解釋) 下面就是do_reset: // load reset register0x1806001c // bit24, fullchip reset 接下來執(zhí)行/u-boot/board/ar7240/common/lowlevle_init.S的lowlevel_init(la t9, lowlevel_init)函數(shù),主要目的是工作頻率配置,比如wlan-reset,HORNET_BOOTSTRAP_STATUS,RTCreset, AHB/APH reset ,MAC reset ,AR7240_CPU_CLOCK_CONTROL ,DDR工作頻率,等, /****************************************************************************** * first level initialization: * * 0) If clock cntrl reset switch is alreadyset, we're recovering from * "divider reset"; goto 3. * 1) Setup divide ratios. * 2) Reset. * 3) Setup pll's, wait for lock. * *****************************************************************************/ 轉(zhuǎn)到了/board/ap7240/ap121/hornet_pll_init.S中執(zhí)行: hornet_pll_init: 先wlan reset: set_reg(0xb806001c,0x00c06b30) //1100_0000_0110_1011_0011_0000 nop set_reg(0xb806001c,0x00c06330) //1100_0000_0110_0011_0011_0000 接著設(shè)置0x180600AC的第2bit(spi啟動),檢查check_val,設(shè)置0x180600AC的值為 set_reg(HORNET_BOOTSTRAP_STATUS,0x0002110e) //0010_0001_0001_0000_1110 RTCreset: set_reg(0x1810704c, 0x00000003) // 0x1810704c :RTC_FORCE_WAKE set_reg(0x18107040, 0x00000000) // 0x18107040: RTC_RESET set_reg(0x18107040, 0x00000001) wait_loop1: li t6, KSEG1ADDR(0x18107044) //0x18107044:RTC_STATUS lw t7, 0(t6) li t8, 0x2 // 看第1bit是否為1,RTC in on state and t7, t7, t8 bne t8, t7,wait_loop1 nop /*AHB/APH reset */ set_reg(0x18104000,0x00000003) TODO:寫3的意思 set_reg(0x18104000, 0x00000000) // 0x18104000:HOST_INTF_RESET_CONTROL /* MACreset */ set_reg(0x18107000,0x0000000F) TODO:RTC registers occupy the offset range 0x18107000–0x18107FFC in the AR9331 addressspace. set_reg(0x18107000, 0x00000000) 接下來有2個 otp_loop0: TODO otp_loop1: TODO fetch_otp: TODO 然后有: *Program PMU */ TODO /*Program ki, kd */ /*Program phase shift */ 先對PLL設(shè)置了:PLLControl Registers 0x18050000-0x18050044 ap121.h 中解釋比較明確 * PLL = (25 MHz * DIV_INT) / (2 ^ OUTDIV) (25 MHz * 32/2 = 400 MHz) // DIV_INT =32 (25 MHz * 32/2 = 400 MHz) // REFDIV =1 //RANGE = 0 //OUTDIV = 1 * CPU = PLL / CPU_POST_DIV * DDR = PLL / DDR_POST_DIV * AHB = PLL / AHB_POST_DIV 1./* setPLL bypass(Bit 2), CPU_POST_DIV, DDR_POST_DIV, AHB_POST_DIV inCPU clock control */ set_reg(AR7240_CPU_CLOCK_CONTROL,CPU_CLK_CONTROL_VAL1) #if(CFG_PLL_FREQ == CFG_PLL_400_400_200) #define CFG_HZ (400000000/2) // CPU_DIV = 1, RAM_DIV = 1, AHB_DIV = 2 #define CPU_CLK_CONTROL_VAL1 0x00018004 // 1_1000--_0000_0000_0100 #define CPU_CLK_CONTROL_VAL2 0x00008000// 1000_0000_0000_0000 2. /* setSETTLE_TIME in CPU PLL */ set_reg(AR7240_USB_PLL_CONFIG,CPU_PLL_SETTLE_TIME_VAL) #defineCPU_PLL_SETTLE_TIME_VAL 0x00000352 3. /* set nint, frac, refdiv, outdiv, rangein CPU PLL configuration resiter */ set_reg(AR7240_CPU_PLL_CONFIG,CPU_PLL_CONFIG_VAL1) #define CPU_PLL_CONFIG_VAL10x40818000//100_0000_1000_0001_1000_0000_0000_0000 #defineCPU_PLL_CONFIG_VAL2 0x00818000// 1000_0001_1000_0000_0000_0000 //DIV_INT = 32 //REFDIV = 1 // RANGE = 0 //OUTDIV = 1 4.讀取AR7240_CPU_PLL_CONFIG第31bit –》PLLupdate :1 ,pending 0,complete 5./* putfrac bit19:10 configuration */ set_reg(AR7240_PCIE_PLL_CONFIG,CPU_PLL_DITHER_FRAC_VAL) 6. /* clear PLL bypass(Bit 2), CPU_POST_DIV,DDR_POST_DIV, AHB_POST_DIV in CPU clock control */ set_reg(AR7240_CPU_CLOCK_CONTROL,CPU_CLK_CONTROL_VAL2) 7. /*Sync mode , Set Bit 8 of DDR Tap Conrtol 3 register */set_reg(AR7240_DDR_TAP_CONTROL3, 0x10105); TODO:沒有的 下面是/u-boot/cpu/mips/ar7240/hornet_ddr_init.S中的hornet_ddr_init,反正就是初始化ddr(ddr2) 然后執(zhí)行/u-boot/cpu/mips/cache.S中的simple_mips_cache_reset (la t9, simple_mips_cache_reset)對cache進(jìn)行初始化。 接著調(diào)用mips_cache_lock(la t9, mips_cache_lock) (這個調(diào)用的目的:當(dāng)代碼執(zhí)行到這個時候,ddr ram還沒有配置好,而如果直接調(diào)用C語言的函數(shù)必須完成棧的設(shè)置,而棧必定要在ram中。所以,只有先把一部分cache拿來當(dāng)做ram用。做法就是把一部分cache配置為棧的地址,鎖定。這樣,當(dāng)讀寫棧的內(nèi)存空間時,只會訪問cache,而不會訪問真的ram地址了。) 接著有:mips_cache_lock_24k 這時,配置棧的地址,進(jìn)行調(diào)用函數(shù)bootstrap_board_init_f(/lib_bootstrap/bootstrap_board.c)進(jìn)入函數(shù)board_init_f(la t9, board_init_f)后,首先做一些列的初始化: Timer_init 時鐘初始化 Serial_init 串口初始化 Init_func_ram 初始化內(nèi)存,配置ddr controller 這一系列工作完成后,串口和內(nèi)存都已經(jīng)可以用了。 然后,就要把內(nèi)存進(jìn)行劃分,在內(nèi)存的最后一部分,留出u-boot代碼大小的空間,準(zhǔn)備把u-boot代碼從flash搬移到這里。 然后,是堆的空間,malloc的內(nèi)存就來自于這里。 緊接著放兩個全局?jǐn)?shù)據(jù)結(jié)構(gòu)bd_infoglobal_data和環(huán)境變量boot_params。 最后,是棧的空間。 當(dāng)內(nèi)存劃分好后,就準(zhǔn)備進(jìn)行relocate code了。 (relocate code含義: 通常u-boot的執(zhí)行代碼肯定是在flash上(調(diào)試可以在ram上).當(dāng)啟動起來之后,要把它從flash上搬移到ram里運(yùn)行 ) 但是,存在的問題是,flash地址和ram地址是不同的。當(dāng)我們把代碼從flash搬移到ram中后,當(dāng)執(zhí)行函數(shù)跳轉(zhuǎn)時,代碼里的函數(shù)地址還是flash的地址,一跳,又重新跳回去了(跳回了flash)。 IPC(position-independentcode) 由此引出了。 原理: 當(dāng)使用IPC方式時,在用gcc編譯時需要加上-fpic的選項(xiàng)。編譯器會為你的可執(zhí)行代碼建立一個GOT(global offset table)的段。一個地址在GOT表中有一項(xiàng),里面存放地址的信息,在使用這個地址時,只要根據(jù)這個地址的編號(也可以叫做偏移量offset)找到表中相應(yīng)的項(xiàng)目,就可以取得那個地址了。 而如果位置發(fā)生變化,只要對GOT 表中的地址進(jìn)行修改就可以了。 例: Lw t9,1088(gp) Jalr t9 這里,gp存放的就是GOT表的起始地址,而1088就是要調(diào)用函數(shù)offset,也就說GOT表的那個位置存放著它的地址。Lw t9,1088(gp)把函數(shù)地址放入t9寄存器,然后調(diào)用就可以了。 Relocate code說簡單一點(diǎn)就是:把u-boot的執(zhí)行代碼直接從flash里copy到ram的相應(yīng)區(qū)域。 然后,把GOT表中的地址都加上一個偏移量,這個偏移量就是flash里的地址與ram里的地址差。 這里完成的操作還有一些其他工作,比如:設(shè)置新的棧指針,從flash代碼里跳轉(zhuǎn)到ram代碼里等等. 之后,進(jìn)入board.c的board_init_r函數(shù)。進(jìn)入stage2。 Stage2: 在board_init_r函數(shù)中初始化malloc,flash,pci以及外設(shè)(如:網(wǎng)口),最后進(jìn)入命令行或者直接啟動Linux Kernel. 這樣,u-boot的啟動工作完成。 本文轉(zhuǎn)自:onejacky的專欄 | 
|  |