uboot源码分析-第一阶段(汇编部分)

42 u-boot源码分析 --- 启动第一阶段

分析代码当然要从上电后执行的第一条指令开始看起咯, 那第一条指令在哪呢? 还是以smdk2410为例,我们看它的链接脚本:

board/smsk2410/u-boot.lds:

……

ENTRY(_start)   //入口地址

SECTIONS

{

    . = 0x00000000;

 

    . = ALIGN(4);

    .text      :

    {

  cpu/arm920t/start.o  (.text)   //呵呵, 这个就是启动后执行的第一个文件

  *(.text)

}

 

    . = ALIGN(4);

    .rodata : { *(.rodata) }

……

}

由这个文件可知第一个执行的文件是cpu/arm920t/start.S,那第一条指令就在这里了。我们看这个文件:

cpu/arm920t/start.S:

.globl _start

_start: b       reset       //复位向量

        ldr pc, _undefined_instruction

        ldr pc, _software_interrupt   

        ldr pc, _prefetch_abort

        ldr pc, _data_abort

        ldr pc, _not_used

        ldr pc, _irq   //中断向量

        ldr pc, _fiq   //快速中断向量

 

_undefined_instruction: .word undefined_instruction

_software_interrupt:   .word software_interrupt

_prefetch_abort:   .word prefetch_abort

_data_abort:       .word data_abort

_not_used:      .word not_used

_irq:           .word irq

_fiq:           .word fiq

 

2410CPU规定开机后的PC寄存器地址为0,即从0地址开始执行指令,因此我们必须把我们的程序放在正确的地址处才能正常开机。 ARM核也规定启动地址处的32个字节必须存放异常向量跳转表,里面保存有中断,异常等的处理函数地址。当系统产生中断时,必定会跳到这里来开始处理中断。具体可参考ARM方面的书籍。

u-boot.lds可知入口地址为_start, 即开机后从_start处开始执行指令。所以第一条指令就是:

b reset       //跳转到reset处进行复位处理

 

cpu/arm920t/start.S:

/*

 * the actual reset code

 */

 

reset:

    /*

     * 让处理器进入SVC32模式,这样可以拥有特权操作,参考ARM书籍

     */

    mrs r0,cpsr

    bic r0,r0,#0x1f

    orr r0,r0,#0xd3

    msr cpsr,r0

 

/* turn off the watchdog */

//CPU上操作watchdog相关的寄存器地址,可参考CPUdatasheet,这里用到的地址都是实地址,

//因为还没为MMU等部件进行初始化,也没切换操作模式呢。

#if defined(CONFIG_S3C2400)

# define pWTCON    0x15300000

# define INTMSK    0x14400008  /* Interupt-Controller base addresses */

# define CLKDIVN   0x14800014  /* clock divisor register */

#elif defined(CONFIG_S3C2410)

# define pWTCON    0x53000000

# define INTMSK    0x4A000008  /* Interupt-Controller base addresses */

# define INTSUBMSK 0x4A00001C

# define CLKDIVN   0x4C000014  /* clock divisor register */

#endif

 

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

    ldr     r0, =pWTCON

    mov     r1, #0x0

    str     r1, [r0] //关闭watchdog,具体寄存器含义可参考CPU手册

 

    /*

     * mask all IRQs by setting all bits in the INTMR - default

     */

    mov r1, #0xffffffff

    ldr r0, =INTMSK

    str r1, [r0]  //关闭所有的中断

# if defined(CONFIG_S3C2410)

    ldr r1, =0x3ff

    ldr r0, =INTSUBMSK

    str r1, [r0]  //关闭所有的中断

# endif

 

    /* FCLK:HCLK:PCLK = 1:2:4 */

    /* default FCLK is 120 MHz ! */

//设置HCLKFCLK/2, PCLKFCLK/4, FCLKCPU产生clock,HCLKAHB总线上的设备产生//clock, PCLKAPB总线上的设备产生clock,具体参考s3c2410datasheet

    ldr r0, =CLKDIVN

    mov r1, #3

    str r1, [r0] 

#endif  /* CONFIG_S3C2400 || CONFIG_S3C2410 */

 

    /*

     * we do sys-critical inits only at reboot,

     * not when booting from ram!

     */

//做系统相关的重要初始化,这些初始化代码只在系统重起的时候执行,

// CONFIG_SKIP_LOWLEVEL_INIT 可以看README.

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl  cpu_init_crit  //可以先看这段代码在转回来接着看后面的复位过程。

#endif

 

   //内存配置完后,可以进行重定位操作了

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:              /* 重定位u-bootRAM*/

    adr r0, _start      /* r0 flash中的代码的起始地址*/

    ldr r1, _TEXT_BASE  /* r1= 代码在RAM中的起始地址 */

    cmp     r0, r1      /* 看是否u-boot就在RAM中运行*/

    beq     stack_setup  /*如果在RAM中则无需重定位*/

   

    /*开始重定位,即把u-bootflash中搬到RAM中去运行*/

    ldr r2, _armboot_start  /*r2 = flash中代码的起始地址,看_armboot_start的定义*/

    ldr r3, _bss_start  /*r3 = bss段的起始地址,_bss_start可在u-boot.lds中查看。*/

    sub r2, r3, r2      /* r2 = 需要重定位的字节数*/

    add r2, r0, r2      /* r2 flashRO,RW内容的结束地址 */

 

//开始把代码从flash中搬运到RAM

copy_loop:

    ldmia   r0!, {r3-r10}      /*获取从r0开始的代码,存入r3—r10*/

    stmia   r1!, {r3-r10}      /*r3—r10的内容存入r1所在位置,即RAM*/

    cmp r0, r2                  /*copy所有代码 */

    ble copy_loop

#endif  /* CONFIG_SKIP_RELOCATE_UBOOT */

 

    /*设置栈地址*/

stack_setup:

    ldr r0, _TEXT_BASE     /*upper 128 KiB: relocated uboot*/

    sub r0, r0, #CFG_MALLOC_LEN /*malloc分配内存的区域,大小以板子的配置而定,smdk2410

的在include/configs/smdk2410.h中定义*/

    sub r0, r0, #CFG_GBL_DATA_SIZE /* 存放bdinfo的区域,定义同上*/

#ifdef CONFIG_USE_IRQ

    sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)  //保留中断所需的区域

#endif

    sub sp, r0, #12     /* 保留 12字节给 abort-stack, 并设好堆栈*/

 

//bss段内容清0

clear_bss:

    ldr r0, _bss_start     /* find start of bss segment        */

    ldr r1, _bss_end       /* stop here                        */

    mov     r2, #0x00000000    /* clear                            */

 

clbss_l:str r2, [r0]        /* clear loop...                    */

    add r0, r0, #4

    cmp r0, r1

    ble clbss_l

 

#if 0

    /* try doing this stuff after the relocation */

    ldr     r0, =pWTCON

    mov     r1, #0x0

    str     r1, [r0]

 

    /*

     * mask all IRQs by setting all bits in the INTMR - default

     */

    mov r1, #0xffffffff

    ldr r0, =INTMR

    str r1, [r0]

 

    /* FCLK:HCLK:PCLK = 1:2:4 */

    /* default FCLK is 120 MHz ! */

    ldr r0, =CLKDIVN

    mov r1, #3

    str r1, [r0]

    /* END stuff after relocation */

#endif

 

    ldr pc, _start_armboot   //跳转到_start_armboot处执行。

_start_armboot: .word start_armboot

 

总结reset这块代码,主要完成了一下几个部分:

1.    重要部分的初始化工作,如禁止中断,关闭watchdog,初始化memory控制器等

2.    重定位boot loaderram

3.    设置好堆栈

4.    跳转到第2阶段执行

完成这些后,此时内存的分布情况如下:


这个图代表的是u-boot自己在内存的情况,和上面的图不一样,这里的_TEXT_BASE就是0x33F8’0000

 

   接着看CPU_init_critical

 

cpu/arm920t/start.S:

/*

 *************************************************************************

 *

 * CPU_init_critical registers

* 设置cache,TLB,MMU等寄存器

 * 设置内存操作的时序

 *

 *************************************************************************

 */

cpu_init_crit:

    /*

     * flush v4 I/D caches

     */

    /*使cacheTLB无效, 可以参考data sheet*/

    mov r0, #0

    mcr p15, 0, r0, c7, c7, 0  /* 使指令cache和数据cache无效 */

    mcr p15, 0, r0, c8, c7, 0  /* 使TLB无效 */

 

    /*

     * disable MMU stuff and caches

     */

    mrc p15, 0, r0, c1, c0, 0  /*读出c1控制寄存器的值*/

    bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)

    bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM),小端对齐,关闭数据cache,关

//闭错误检测,关闭MMU

    orr r0, r0, #0x00000002 @ set bit 2 (A) Align, 使能错误检测

    orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache, 使能指令cache

    mcr p15, 0, r0, c1, c0, 0  /*设置c1控制寄存器*/

    /*可以参考data sheet*/

 

    /*

     * before relocating, we have to setup RAM timing

     * because memory timing is board-dependend, you will

     * find a lowlevel_init.S in your board directory.

     */

    //在把u-boot重定位到RAM前,我们必须先把RAM的时序设置好,内存时序是依板子而定的,//所以这里的初始化应该由我们提供,一般在我们的板子所在目录下有个lowlevel_init.S来负//责这件事情。特定板子的目录还记得吗, 呵呵回到上面在看看。

    mov ip, lr

    bl  lowlevel_init

    mov lr, ip

 

    mov pc, lr //类似于函数返回

 

cpu_init_crit主要是使能了instruction cache,关闭了MMU等部件,但是好像在u-boot后面的代码里没有看见打开MMU的操作,我猜测可能是留到了OS启动的时候再打开了吧,data cache在第二阶段的board_init下被使能。

 

接着看lowlevel_init。以smdk2410位例

 

board/smdk2410/lowlevel_init.S

_TEXT_BASE:

    .word   TEXT_BASE

 

.globl lowlevel_init

lowlevel_init:

    /* memory control configuration */

    /* make r0 relative the current location so that it */

    /* reads SMRDATA out of FLASH rather than memory ! */

   // 内存控制器的配置, 配置完后就可以使用内存了

    ldr     r0, =SMRDATA   //在下面定义

    ldr     r1, _TEXT_BASE

    sub     r0, r0, r1   //???

    ldr     r1, =BWSCON /* Bus Width Status Controller */

    add     r2, r0, #13*4

0:

    ldr     r3, [r0], #4

    str     r3, [r1], #4  //设置内存配置寄存器,可以对着datasheet来看这里的设置,包括时

//序位宽等等, 使用一个循环来配置所有的寄存器

    cmp     r2, r0

    bne     0b

 

    /* everything is fine now */

    mov pc, lr

 

    .ltorg

/* the literal pools origin */

//这些就是要被设置进内存配置寄存器的值,

SMRDATA:

    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))

    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))

    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))

    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))

    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))

    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))

    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))

    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

    .word 0x32

    .word 0x30

    .word 0x30

这部分代码主要是设置memory的时序,位宽等参数

 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值