mips架构u-boot启动流程 u-boot的启动过程大致做如下工作: 1、cpu初始化 2、时钟、串口、内存(ddr ram)初始化 3、内存划分、分配栈、数据、配置参数、以及u-boot代码在内存中的位置。 4、对u-boot代码作relocate 5、初始化malloc、flash、pci以及外设(比如,网口) 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:系统上电后执行汇编代码 Stage2:通过一些列设置搭建了C环境,通过汇编指令跳转到C语言执行. Stage1: 程序从cpu/mips/start_bootstrap.S的_start_bootstrap开始执行.(至于为什么,参考u-boot.lds分析.doc) 先查看start_bootstrap.S文件吧!~ 从_start_bootstrap标记开始会看到一长串莫名奇妙的代码: RVECENT(reset,0) /* U-boot entry point */ /*U-Boot开始执行的代码起始地址*/ RVECENT(reset,1) /* software reboot */ /*软重启时U-Boot开始执行的起始地址*/ RVECENT(romReserved,2) /*保留本代码所在的地址,重新映射调试异常向量时可以使用该空间*/ 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 原来这只是一个简单的跳转指令,f为一个标记,b为跳转指令。 然后看最后,发现: romReserved: b romReserved romExcHandle: b romExcHandle 这两个标记都构建了无意义的死循环。 通过_start标记处的语句RVECENT(reset,0) 代码跳转到标记reset的地方,该段代码的操作就是对寄存器的清零操作了。Mfc0和mtc0指令是对寄存器的一些读写. Mips 寄存器:http://blog.csdn.net/flyingqr/article/details/7073088 在接下来是对协处理器的操作了,其中包括: CP0_WATCHLO, CP0_WATCHHI, CP0_STATUS CP0_CAUSE, CP0_COUNT, CP0_COMPARE CP0_CONFIG 之后,配置寄存器CP0_STATUS,设置所使用的协处理器,中断以及cpu运行级别(核心级)。 配置gp寄存器,把GOT段的地址赋给gp寄存器。(gp寄存器的用处会在后面relocate code部分详细解释) 下面就是do_reset: // load reset register0x1806001c // bit24, fullchip reset 接下来执行/u-boot/board/ar7240/common/lowlevle_init.S的lowlevel_init(la t9, lowlevel_init)函数,主要目的是工作频率配置,比如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. * *****************************************************************************/ 转到了/board/ap7240/ap121/hornet_pll_init.S中执行: 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 接着设置0x180600AC的第2bit(spi启动),检查check_val,设置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设置了: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) 然后执行/u-boot/cpu/mips/cache.S中的simple_mips_cache_reset (la t9, simple_mips_cache_reset)对cache进行初始化。 接着调用mips_cache_lock(la t9, mips_cache_lock) (这个调用的目的:当代码执行到这个时候,ddr ram还没有配置好,而如果直接调用C语言的函数必须完成栈的设置,而栈必定要在ram中。所以,只有先把一部分cache拿来当做ram用。做法就是把一部分cache配置为栈的地址,锁定。这样,当读写栈的内存空间时,只会访问cache,而不会访问真的ram地址了。) 接着有:mips_cache_lock_24k 这时,配置栈的地址,进行调用函数bootstrap_board_init_f(/lib_bootstrap/bootstrap_board.c)进入函数board_init_f(la t9, board_init_f)后,首先做一些列的初始化: Timer_init 时钟初始化 Serial_init 串口初始化 Init_func_ram 初始化内存,配置ddr controller 这一系列工作完成后,串口和内存都已经可以用了。 然后,就要把内存进行划分,在内存的最后一部分,留出u-boot代码大小的空间,准备把u-boot代码从flash搬移到这里。 然后,是堆的空间,malloc的内存就来自于这里。 紧接着放两个全局数据结构bd_infoglobal_data和环境变量boot_params。 最后,是栈的空间。 当内存划分好后,就准备进行relocate code了。 (relocate code含义: 通常u-boot的执行代码肯定是在flash上(调试可以在ram上).当启动起来之后,要把它从flash上搬移到ram里运行 ) 但是,存在的问题是,flash地址和ram地址是不同的。当我们把代码从flash搬移到ram中后,当执行函数跳转时,代码里的函数地址还是flash的地址,一跳,又重新跳回去了(跳回了flash)。 IPC(position-independentcode) 由此引出了。 原理: 当使用IPC方式时,在用gcc编译时需要加上-fpic的选项。编译器会为你的可执行代码建立一个GOT(global offset table)的段。一个地址在GOT表中有一项,里面存放地址的信息,在使用这个地址时,只要根据这个地址的编号(也可以叫做偏移量offset)找到表中相应的项目,就可以取得那个地址了。 而如果位置发生变化,只要对GOT 表中的地址进行修改就可以了。 例: Lw t9,1088(gp) Jalr t9 这里,gp存放的就是GOT表的起始地址,而1088就是要调用函数offset,也就说GOT表的那个位置存放着它的地址。Lw t9,1088(gp)把函数地址放入t9寄存器,然后调用就可以了。 Relocate code说简单一点就是:把u-boot的执行代码直接从flash里copy到ram的相应区域。 然后,把GOT表中的地址都加上一个偏移量,这个偏移量就是flash里的地址与ram里的地址差。 这里完成的操作还有一些其他工作,比如:设置新的栈指针,从flash代码里跳转到ram代码里等等. 之后,进入board.c的board_init_r函数。进入stage2。 Stage2: 在board_init_r函数中初始化malloc,flash,pci以及外设(如:网口),最后进入命令行或者直接启动Linux Kernel. 这样,u-boot的启动工作完成。 本文转自:onejacky的专栏 |
|