分享

基于ar9331 mips架构AP121 uboot分析(3) 启动流程 | Imagination中文技术社区

 云将东游 2016-02-24

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的专栏

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多