u-boot 1.1.6版本学习笔记——Start.s分析

来源:互联网 发布:钉钉软件怎么样 编辑:程序博客网 时间:2024/06/07 07:37

首先直接看第一段代码:

.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

.globl 和.globol是一样的,它的作用是声明一个符号,而这个符号可以被其他文档引用,相当于声明了一个全局变量。
那么这里就是声名_start,_start可以被其他文档使用。
_start就是整个start.S的最开始,即整个uboot的代码的开始。后面的冒号,表示_start是一个标号,它的值,就是代码的位置,这里,是代码的最开始,所以其值=0。

b reset :表示跳转到 reset 执行,并且不返回

ldr: LDR伪指令—–大范围的地址读取
详情请看:1,http://blog.chinaunix.net/uid-28458801-id-3753775.html
代码是:

ldr pc, _undefined_instruction

我们先看大体一下_undefined_instruction是什么:

_undefined_instruction: .word undefined_instruction

.word: word是字的意思,.word就是分配一段字内存单元,这里相当于分配了一个4字节的内存单元,里面放的是undefined_instruction的值,这个undefined_instruction跟 _start一样,是标号,它的值就是代码所在的位置,所以ldr pc, _undefined_instruction
就是把,undefined_instruction代码的地址放到PC中,那样,发生未定义异常的时候,就可以通过PC进入undefined_instruction对应的代码段处理了。


接下去的代码:.balignl 16,0xdeadbeef
意思:接下的代码,也就是

_TEXT_BASE:    .word   TEXT_BASE

它是需要16字节对齐的,如果不满足16个字节,那么就用0xdeadbeef补充。

TEXT_BASE是一个word类型的变量,它代表的是整个u-boot的基地址,是在\board\xx\config.mk中定义的,makefile中会用到,后面再分析,我们假设 TEXT_BASE = 0x33D00000

.globl _bss_start_bss_start:    .word __bss_start.globl _bss_end_bss_end:    .word _end

_bss_start和_bss_end都叧是两个标号,对应着此处的地址。
而两个地址里面分别存放的值是__bss_start和_end,这两个的值,定义在 \board\xx\u-boot.lds 中这是一个连接脚本。
而关于_bss_start和_bss_end定义为.glogl即全局变量,是因为uboot的其他源码中要用到这两个变量。


#ifdef CONFIG_USE_IRQ/* IRQ stack memory (calculated at run-time) */.globl IRQ_STACK_STARTIRQ_STACK_START:    .word   0x0badc0de/* IRQ stack memory (calculated at run-time) */.globl FIQ_STACK_STARTFIQ_STACK_START:    .word 0x0badc0de#endif

IRQ_STACK_START跟FIQ_STACK_START两个全局变量,当前的值是没有意义的,因为一开始初始化,并不知道堆栈的准确地址,这是留给以后再修改的。

/* IRQ stack memory (calculated at run-time) */

/* * the actual reset code *//*对 mrs 和msr 有疑问的请看:http://blog.chinaunix.net/uid-28458801-id-4083956.html*/reset:    /*     * set the cpu to SVC32 mode.设置cpu为 SVC模式。     * 如果对arm的个模式有疑问的请看:1,http://blog.chinaunix.net/uid-28458801-id-3788554.html     *     2,http://blog.chinaunix.net/uid-28458801-id-3494646.html     */    mrs    r0,cpsr                    /* 传送CPSR的内容到R0 */    bic    r0,r0,#0x1f                /* 清除r0的bit[4:0]位 */    orr    r0,r0,#0xd3                /* 把r0的bit[7:6]和bit[4]和bit[2:0]置为1 */    msr    cpsr,r0                    /* 将r0的值赋给CPSR */

接下来是关看门狗的操作:

* turn off the watchdog */#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

首先,定义一些看门狗相关的寄存器地址。
关看门狗操作很简单,就是往pWTCON寄存器里写0

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)    ldr     r0, =pWTCON    mov     r1, #0x0    str     r1, [r0]

然后就是关闭所有中断,方法:设置INTMSK寄存器

    mov r1, #0xffffffff    ldr r0, =INTMSK    str r1, [r0]    # if defined(CONFIG_S3C2410)    ldr r1, =0x3ff    ldr r0, =INTSUBMSK    str r1, [r0]# endif

#if 0        /* 不编译*/    /* FCLK:HCLK:PCLK = 1:2:4 */    /* default FCLK is 120 MHz ! */    ldr    r0, =CLKDIVN    mov    r1, #3    str    r1, [r0]#endif

接下来:

    /*     * we do sys-critical inits only at reboot,     * not when booting from ram!     */#ifndef CONFIG_SKIP_LOWLEVEL_INIT    bl  cpu_init_crit#endif

// B 转移指令,跳转到指令中指定的目的地址,即不返回
// BL 带链接的转移指令,像B 相同跳转并把转移后面紧接的一条指令地址保存到链接寄存器LR(R14)中,以此来完成子程式的调用,即能返回。
// 该语句首先调用cpu_init_crit 进行CPU 的初始化

那我们在看一下,#ifndef CONFIG_SKIP_LOWLEVEL_INIT这句话的作用是什么,其实,注释写了,因为bl cpu_init_crit是底层操作用这句话,来使得bl这句代码,只在u-boot在ram中运行的时候,才能执行,在外部的sdram就不执行了。


接下来分析cpu_init_crit 这部分代码:

#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit:    /*     * flush v4 I/D caches     */    mov r0, #0    mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */    mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */    /*     * disable MMU stuff and caches     */    mrc p15, 0, r0, c1, c0, 0    bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)    bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)    orr r0, r0, #0x00000002 @ set bit 2 (A) Align    orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache    mcr p15, 0, r0, c1, c0, 0    /*     * 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.     */    mov ip, lr    bl  lowlevel_init    mov lr, ip     mov pc, lr#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

第一个CONFIG_SKIP_LOWLEVEL_INIT 宏定义,在2440上是没用定义的,因此会执行这部分代码。
前面两部分很简单,就是操作cp15协处理器,invalidate icache, dcache,这里有个我的理解,看第二部份的代码注释

 @set bit 12 (I) I-Cache

即,使能了icache,我一开始觉得跟之前一段代码invalidate cache是冲突的,但后来想想可能是这样:invalidate跟使能是可以同时的,使能是让cache可以用,以后需要使用cache时,不需要再在使能。invalidate只是让cache里的数据无效,好像是让cache每一段的最后一位都为0。

然后看第三部分:
这一部分,一开始mov ip, lr这一句,是将lr保存起来,ip好像就是r11。为什么需要保存呢?因为在下面调用的lowlevel_init的子程序里,还需要使用lr,即还需要再调用子程序,所以将lr先保存,防止后面被修改。那这个lowlevel_init是干什么用的呢?
我们知道,u-boot很大,而2440内部只用4k的sram,所以,在这4k的代码中,你就要为后面的u-boot的代码存放的位置做好准备,这里,我们是将后面的代码放到外部的sdram中,那就需要把sdram初始化,初始化的意思就是让我2440的芯片可以管理外部的sdram。在直白点,就是配置2440的存储管理寄存器。
好,那我们就来看看这部分的代码:
首先第一部分是一些宏定义(看2440手册的第5章):

#define BWSCON  0x48000000

上面BWSCON 寄存器的的地址

#define DW8         (0x0)#define DW16            (0x1)#define DW32            (0x2)#define WAIT            (0x1<<2)#define UBLB            (0x1<<3)#define B1_BWSCON       (DW32)#define B2_BWSCON       (DW16)#define B3_BWSCON       (DW16 + WAIT + UBLB)#define B4_BWSCON       (DW16)#define B5_BWSCON       (DW16)#define B6_BWSCON       (DW32)#define B7_BWSCON       (DW32)

这上面一开始是BWSCON 里面一些位设置的宏,接下来是对BANKn的数据总线宽度与功能的宏定义,对于BANK3因为要接网卡,网卡有wait信号和没有wait信号两种,所以这里有些特别。

/* BANK0CON */#define B0_Tacs         0x0 /*  0clk */#define B0_Tcos         0x0 /*  0clk */#define B0_Tacc         0x7 /* 14clk */#define B0_Tcoh         0x0 /*  0clk */#define B0_Tah          0x0 /*  0clk */#define B0_Tacp         0x0#define B0_PMC          0x0 /* normal *//* BANK1CON */#define B1_Tacs         0x0 /*  0clk */#define B1_Tcos         0x0 /*  0clk */#define B1_Tacc         0x7 /* 14clk */#define B1_Tcoh         0x0 /*  0clk */#define B1_Tah          0x0 /*  0clk */#define B1_Tacp         0x0#define B1_PMC          0x0#define B2_Tacs         0x0#define B2_Tcos         0x0#define B2_Tacc         0x7#define B2_Tcoh         0x0#define B2_Tah          0x0#define B2_Tacp         0x0#define B2_PMC          0x0#define B3_Tacs         0x0 /*  0clk */#define B3_Tcos         0x3 /*  4clk */#define B3_Tacc         0x7 /* 14clk */#define B3_Tcoh         0x1 /*  1clk */#define B3_Tah          0x0 /*  0clk */#define B3_Tacp         0x3     /*  6clk */#define B3_PMC          0x0 /* normal */#define B4_Tacs         0x0 /*  0clk */#define B4_Tcos         0x0 /*  0clk */#define B4_Tacc         0x7 /* 14clk */#define B4_Tcoh         0x0 /*  0clk */#define B4_Tah          0x0 /*  0clk */#define B4_Tacp         0x0#define B4_PMC          0x0 /* normal */#define B5_Tacs         0x0 /*  0clk */#define B5_Tcos         0x0 /*  0clk */#define B5_Tacc         0x7 /* 14clk */#define B5_Tcoh         0x0 /*  0clk */#define B5_Tah          0x0 /*  0clk */#define B5_Tacp         0x0#define B5_PMC          0x0 /* normal */

这上面的这段代码是对每一个BANK的BANKCON进行配置,这里要根据SRAM手册来配置对应的值,否则不能正常进行使用,在0~5中大多是对一些时序与Page模式的配置,只对应SRAM的要求就可以了。

#define B6_MT           0x3 /* SDRAM */#define B6_Trcd         0x1#define B6_SCAN         0x1 /* 9bit */#define B7_MT           0x3 /* SDRAM */#define B7_Trcd         0x1 /* 3clk */#define B7_SCAN         0x1 /* 9bit *//* REFRESH parameter */#define REFEN           0x1 /* Refresh enable */#define TREFMD          0x0 /* CBR(CAS before RAS)/Auto refresh */#define Trp         0x0 /* 2clk */#define Trc         0x3 /* 7clk */#define Tchr            0x2 /* 3clk */#define REFCNT          1113    /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */

然后,上面的代码,就是配置B6,B7,这两个块是可以作为ROM,SRAM,SDRAM等存储器的。
还有刷新控制寄存器的配置,要根据SDRAM的具体参数来配置,移植需要注意。

好了,就下来就是lowlevel_init了:

_TEXT_BASE:    .word   TEXT_BASE

这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中)

接下来是这段代码:

    ldr     r0, =SMRDATA    ldr r1, _TEXT_BASE    sub r0, r0, r1    ldr r1, =BWSCON /* Bus Width Status Controller */    add     r2, r0, #13*40:    ldr     r3, [r0], #4    str     r3, [r1], #4    cmp     r2, r0    bne     0b

首先看第一句:ldr r0, =SMRDATASMRDATA是在下面定义的:

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

SMRDATA表示的是这13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内存中,这一句就是将其值加载到r0中。
接下来的分析,我转的一个人的:

_TEXT_BASE: .word TEXT_BASE  //这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中                  //可到找到“TEXT_BASE=0x33F80000.globl lowlevel_init  //这里相当于定义一个全局的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  //SMDATA表示这13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内                       //存中,这一句的作用是把其值加载到r0中 ldr r1, _TEXT_BASE    //把代码的起始地址(0x33F80000)加载到r1sub r0, r0, r1        //r0减去r1其结果存入r0,也即SMDATA中的起始地址0x33F8xxxx减去                       //0x33F80000,其结果就是13个寄存器的值在NOR Flash存放的开始地址ldr r1, =BWSCON /* Bus Width Status Controller */ //存储控制器的基地址 add     r2, r0, #13*4     //在计算出来的存放地址加上#13*4,然后其结果保存在r2中                           //13个寄存器,每个寄存器占4个字节0: ldr     r3, [r0], #4      //内存中r0的值加载到r3中,然后r0加4,即下一个寄存器的 str     r3, [r1], #4      //读出寄存器的值保存到r1中,然后r1也偏移4 cmp     r2, r0            //比较r0r2的值,如果不等继续返回0:执行,也即13个寄存器的值                           //是否读完 bne     0b /* everything is fine now */ mov pc, lr               //程序跳转,返回到cpu_init_crit中

到这里,lowlevel_init就结束了。


上面结束后,程序就回到之前调用cpu_init_crit的地方,继续往下运行。
代码如下:

relocate:               /* relocate U-Boot to RAM       */    adr r0, _start      /* r0 <- current position of code   */    ldr r1, _TEXT_BASE      /* test if we run from flash or RAM */    cmp     r0, r1                  /* don't reloc during debug         */    beq     stack_setup

/* 这一段程序的目的是判断 _start 和 _TEXT_BASE 是否相同,如果程序开始执行的地方就位于程序的链接地址的话,就不用重定位了。如果是nor启动,_start即为代码的最开始,相对的0的位置;如果重新relocate代码之后,_start就是在/board/smdk2410/config.mk中定义的TEXT_BASE = 0x33F80000位置,即_start = TEXT_BASE = 0x33F80000,这样的话就不用重定位代码了。 */

ldr r2, _armboot_start    ldr r3, _bss_start    sub r2, r3, r2      /* r2 <- size of armboot            */    add r2, r0, r2      /* r2 <- source end address         */

能执行到这,就说明_start 和 _TEXT_BASE 是不相同的。重定位代码就是把代码从源地址拷贝到目的地址,拷贝多大的问题。源就是r0中的值,为_start,目的就是r1中的值,为_TEXT_BASE,程序拷贝多大呢?从链接脚本里面可以看出来,用(_bss_start - _armboot_start)即为程序的大小。 为什么这么大,待思考。

stack_setup:    ldr r0, _TEXT_BASE      /* upper 128 KiB: relocated uboot   */    sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */    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     /* leave 3 words for abort-stack    */

设置栈,在经过上面的重定位代码以后, r0 = TEXT_BASE = 0x33F80000,去除分给malloc堆的空间以后,再取出一部分空间给bdinfo结构体,再留出12字节给终止异常的空间,然后将这个值赋给sp,即设置好了栈。

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

清BSS段,BSS段的大小为(_bss_end - _bss_start),将这个空间都清为0。

    ldr pc, _start_armboot_start_armboot: .word start_armboot

将_start_armboot的值赋给pc,即调用start_armboot函数,即跳到uboot的第二阶段。

总结一下流程:
1,设置svc模式;
2,关看门狗;
3,屏蔽中断;
4,invalidate cache,关mmu
5,初始化存储管理器
6,代码重定位
7,设置栈
8,启动第二段代码

0 0