基于ar9331 mips架构AP121 uboot分析(3) 启动流程
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、進入命令行或者直接啟動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運行級別(核心級)。
配置gp寄存器,把GOT段的地址賦給gp寄存器。(gp寄存器的用處會在后面relocate code部分詳細解釋)
下面就是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進行初始化。
接著調(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
這時,配置棧的地址,進行調(diào)用函數(shù)bootstrap_board_init_f(/lib_bootstrap/bootstrap_board.c)進入函數(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)存進行劃分,在內(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)備進行relocate code了。
(relocate code含義:
通常u-boot的執(zhí)行代碼肯定是在flash上(調(diào)試可以在ram上).當(dāng)啟動起來之后,要把它從flash上搬移到ram里運行
)
但是,存在的問題是,flash地址和ram地址是不同的。當(dāng)我們把代碼從flash搬移到ram中后,當(dāng)執(zhí)行函數(shù)跳轉(zhuǎn)時,代碼里的函數(shù)地址還是flash的地址,一跳,又重新跳回去了(跳回了flash)。
IPC(position-independentcode) 由此引出了。
原理:
當(dāng)使用IPC方式時,在用gcc編譯時需要加上-fpic的選項。編譯器會為你的可執(zhí)行代碼建立一個GOT(global offset table)的段。一個地址在GOT表中有一項,里面存放地址的信息,在使用這個地址時,只要根據(jù)這個地址的編號(也可以叫做偏移量offset)找到表中相應(yīng)的項目,就可以取得那個地址了。
而如果位置發(fā)生變化,只要對GOT 表中的地址進行修改就可以了。
例:
Lw t9,1088(gp)
Jalr t9
這里,gp存放的就是GOT表的起始地址,而1088就是要調(diào)用函數(shù)offset,也就說GOT表的那個位置存放著它的地址。Lw t9,1088(gp)把函數(shù)地址放入t9寄存器,然后調(diào)用就可以了。
Relocate code說簡單一點就是:把u-boot的執(zhí)行代碼直接從flash里copy到ram的相應(yīng)區(qū)域。
然后,把GOT表中的地址都加上一個偏移量,這個偏移量就是flash里的地址與ram里的地址差。
這里完成的操作還有一些其他工作,比如:設(shè)置新的棧指針,從flash代碼里跳轉(zhuǎn)到ram代碼里等等.
之后,進入board.c的board_init_r函數(shù)。進入stage2。
Stage2:
在board_init_r函數(shù)中初始化malloc,flash,pci以及外設(shè)(如:網(wǎng)口),最后進入命令行或者直接啟動Linux Kernel.
這樣,u-boot的啟動工作完成。
from:?http://imgtec.eetrend.com/blog/4205
總結(jié)
以上是生活随笔為你收集整理的基于ar9331 mips架构AP121 uboot分析(3) 启动流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于centos7.3 3.10-514
- 下一篇: github+hexo+butterfl