uboot移植之启动过程详解1

/*******************************************************************************
    uboot启动过程第一阶段的分析(start.s文件)

核心:start.s的分析,

其他:虚拟地址的简单介绍,lowlever_init函数的分析

    时间:2018年11月下旬
    作者:cryil_先森
    以朱有鹏课程中uboot-samsung-dev为分析对象


******************************************************************************/

/*******************************************************************************    
    
    u-boot.lds中找到start.s的入口
    整个程序的入口取决于链接脚本u-boot.lds中ENTRY声明的地方。
    start符号所在的文件是整个程序的起始文件,_start所在的代码是整个程序的起始代码。
    
    找到start.s文件,他是整个程序的起始文件。目录:u-boot-samsung-dev\cpu\s5pc11x    
    .globl相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问
    uboot启动的阶段的技巧是小范围内有限条件下的辗转腾挪(不断地在设置栈,变换储存的位置)
    
*******************************************************************************/

 

start.s的分析


***主要的工作:


    1.构建异常向量表;
    2.设置CPU为SVC模式;
    3.关看门狗,开发板的供电索存(lowlevel.inti.s中完成,在start.s中重复进行);
    4.时钟的初始化,DDR的初始化(lowlevel.inti.s中完成);
    5.调试的重要依据:串口初始化并打印“OK”;
    6.重定位,并且设置了uboot启动第二部分在DDR中的起始地址;
    7.建立映射表并开启MMU;
    8.设置三次不同用途的栈;
    9.跳转至uboot启动的第二阶段继续进行。
    
    

***头文件:


    #include <config.h>   config.h文件在include目录下,在配置时自动生成,详见mkconfig脚本
    其实config.h文件包含了一个头文件  include/configs/smdkv210onenand.h头文件。
     
    #include <version.h>
    include/version.h中包含了include/versionn_autogeneraled.h。
    这个文件在配置时自动生成,里面定义的宏  U-BOOT-VERSION是一个字符串,代表版本号。
    U-BOOT-VERSION来源于主Makefile开头位置的配置值,这个宏在程序之中被调用,在开机启动时会打印出我们的所配置的板子的信息。
    
    #include <asm/proc/domain.h>
    asm目录不是uboot的原生目录,是配置时创建的一个符号链接,指向asm-arm目录。
    asm/proc/domain.h实际的文件是 include/asm-arm/proc/domain.h
       
    #include <regs.h>
    regs.h文件在/include目录下,实际指向/include/s5pc110.h($6系统芯片.h)文件。
    

***程序部分分析:


    **1.启动代码的16字节头部:

	#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
	#endif


    分析:.word是GNU的伪指令,实际上定义了16字节的内容,这16字节是SD/NAND启动时整个镜像开头所需的16字节校验头。
    在uboot启动时:在开头位置放置了16字节的填充占位,这个占位的16字节只是保证image的头部确实有16字节,但是这16字节里面的内容是不正确的,需要后面去计算校验然后重新填充(类似及裸机之中的mkv210_image.c文件)
    

    
    **2.异常向量表:

	_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


    分析:异常向量表由硬件决定,软件进行参考设计实现。
          异常向量表中的每一个异常都应该被处理,否则遇到这种异常就会跑飞。
          
    .balignl 16,0xdeadbeef:
    让当前地址对其排布,如果当前地址不对齐则自动向后走直到地址对齐,并且后面的内存用0xdeadbeef填充     (deadbeef:坏牛肉,表示可以被丢弃的内容)
    
    


    **3. _TEXT_BASE:

    .word    TEXT_BASE


    分析:和主Makefile中定义的TEXT_BASE相同,是我们链接时指定的链接地址,也就是c3e00000
    
    


    **4. _TEXT_PHY_BASE:

    .word    CFG_PHY_UBOOT_BASE


    分析:CFG_PHY_UBOOT_BASE=0x20000000+3e00000=0x23e00000   也是DDR之中的物理地址
    
    


    ***5.

 #if defined(CONFIG_USE_IRQ)
    /* IRQ stack memory (calculated at run-time) */
    .globl IRQ_STACK_START
    IRQ_STACK_START:
        .word    0x0badc0de

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


    分析:uboot中与中断有关的设置,一般情况下会被删除。
    
    


    **6.进行复位模式的设置,整个程序真正入口的地方

    reset:
    /*
     * set the cpu to SVC32 mode and IRQ & FIQ disable
     */
    @;mrs    r0,cpsr
    @;bic    r0,r0,#0x1f
    @;orr    r0,r0,#0xd3
    @;msr    cpsr,r0
    msr    cpsr_c, #0xd3        @ I & F disable, Mode: 0x13 - SVC


     设置CPU当前工作的状态模式:禁止FIQ IRQ ARM状态,SVC模式

    分析:将CPU设置为禁止FIQ  IRQ  ARM状态,SVC模式。(uboot工作时CPU移植处在SVC)
    
    


    **7.CPU的一些初始化操作:


  

 cpu_init_crit:
    #ifndef CONFIG_EVT1   判断是否是定义了CONFIG_EVT1,后面的if~endif 可以删除
    #if 0    
        bl    v7_flush_dcache_all
    #else
        bl    disable_l2cache

        mov    r0, #0x0    @ 
        mov    r1, #0x0    @ i    
        mov    r3, #0x0
        mov    r4, #0x0
    lp1:
        mov    r2, #0x0    @ j
    lp2:    
        mov    r3, r1, LSL #29        @ r3 = r1(i) <<29
        mov    r4, r2, LSL #6        @ r4 = r2(j) <<6
        orr    r4, r4, #0x2        @ r3 = (i<<29)|(j<<6)|(1<<1)
        orr    r3, r3, r4
        mov    r0, r3            @ r0 = r3
        bl    CoInvalidateDCacheIndex
        add    r2, #0x1        @ r2(j)++
        cmp    r2, #1024        @ r2 < 1024
        bne    lp2            @ jump to lp2
        add    r1, #0x1        @ r1(i)++
        cmp    r1, #8            @ r1(i) < 8
        bne    lp1            @ jump to lp1

        bl    set_l2cache_auxctrl
        
        bl    enable_l2cache
    #endif
    #endif
    bl    disable_l2cache    禁止12cache

    bl    set_l2cache_auxctrl_cycle   12cash 相关初始化

    bl    enable_l2cache   使能 12 cache
    
       /*
        * Invalidate L1 I/D
        */
        mov    r0, #0                  @ set up for MCR
        mcr    p15, 0, r0, c8, c7, 0   @ invalidate TLBs
        mcr    p15, 0, r0, c7, c5, 0   @ invalidate icache

       /*
        * disable MMU stuff and caches
        */
        mrc    p15, 0, r0, c1, c0, 0
        bic    r0, r0, #0x00002000     @ clear bits 13 (--V-)
        bic    r0, r0, #0x00000007     @ clear bits 2:0 (-CAM)
        orr    r0, r0, #0x00000002     @ set bit 1 (--A-) Align
        orr    r0, r0, #0x00000800     @ set bit 12 (Z---) BTB
        mcr     p15, 0, r0, c1, c0, 0
        
        /* Read booting information */
        
        ldr    r0, =PRO_ID_BASE
        ldr    r1, [r0,#OMR_OFFSET]
        bic    r2, r1, #0xffffffc1    
        总结:以上都是关于和CPU相关的cach  mmu有关的,不用细看。
        
        
        识别并暂时储存启动介质的选择:   
        ldr    r0, =PRO_ID_BASE
        ldr    r1, [r0,#OMR_OFFSET]
        bic    r2, r1, #0xffffffc1


        分析:从哪里启动由SOC的OM5~OM0这6个引脚的高低电平决定。
        在210内有一个寄存器(地址是0xE0000004),这个寄存器是硬件根据OM引脚的设置而自动设置的,这个值(r2)反映了OM引脚的接法(电平的高低),从而确定了启动介质是NAND还是SD其他的。
    

    


    **9.进行板子的信息匹配

 #ifdef CONFIG_VOGUES   
    /* PS_HOLD(GPH0_0) set to output high   设置输出高*/
    ldr    r0, =ELFIN_GPIO_BASE
    ldr    r1, =0x00000001
    str    r1, [r0, #GPH0CON_OFFSET]

    ldr    r1, =0x5500
    str    r1, [r0, #GPH0PUD_OFFSET]

    ldr    r1, =0x01
    str    r1, [r0, #GPH0DAT_OFFSET]
    #endif


    分析:将板子与VOGUES进行比较 一致的话则进行下面的操作 (与我们的板子不同 删除)
    


    **10. 设置板子启动的方式:


 

  /* NAND BOOT */
    cmp    r2, #0x0        @ 512B 4-cycle
    moveq    r3, #BOOT_NAND

    cmp    r2, #0x2        @ 2KB 5-cycle
    moveq    r3, #BOOT_NAND

    cmp    r2, #0x4        @ 4KB 5-cycle    8-bit ECC
    moveq    r3, #BOOT_NAND

    cmp    r2, #0x6        @ 4KB 5-cycle    16-bit ECC
    moveq    r3, #BOOT_NAND

    cmp    r2, #0x8        @ OneNAND Mux
    moveq    r3, #BOOT_ONENAND

    /* SD/MMC BOOT */
    cmp     r2, #0xc
    moveq   r3, #BOOT_MMCSD

    /* NOR BOOT */
    cmp     r2, #0x14
    moveq   r3, #BOOT_NOR    
    
    /* Uart BOOTONG failed */
    cmp     r2, #(0x1<<4)
    moveq   r3, #BOOT_SEC_DEV
    
    ldr    r0, =INF_REG_BASE
    str    r3, [r0, #INF_REG3_OFFSET]     

    分析:通过给r2赋值,确定板子的启动方式:nand,onenand,sd,nor flash
          如果启动失败的话则对r2重新赋值,更换启动的方式。
    

    


    **11.初始化第一个栈(SRAM内)


    ldr    sp, =0xd0036000 /* end of sram dedicated to u-boot */  //设置寄存器的值 
    sub    sp, sp, #12    /* set stack */
    mov    fp, #0
    


    分析:第一次设置栈,这次设置是在SRAM中设置的,因为当前整个代码还是运行在SRAM中,DDR还未初始化。在调用函数之前初始化栈主要原因是在被调用的函数内部还会再次调用函数,而BL只会返回地址储存到LR之中,但是我们只有一个LR,所以在第二层调用函数之前将LR入栈,否则函数返回时第一层的返回地址就丢了。
    
    


    **12.一个重要的函数:lowlevel_init

   bl    lowlevel_init    /* go setup pll,mux,memory */


    在其他博客内会有具体的介绍。
    
    


    **13.开发板的供电索存


    ldr    r0, =0xE010E81C  /* PS_HOLD_CONTROL register   */
    ldr    r1, =0x00005301     /* PS_HOLD output high    */
    str    r1, [r0]


    在lowlevel_init中初始化过了(可删除)。
    
    


    **14.第二次设置栈(DDR内)


    ldr    sp, _TEXT_PHY_BASE    /* setup temp stack pointer */
    sub    sp, sp, #12
    mov    fp, #0            /* no previous frame, so fp=0 */


    分析:由于我们的DDR已经被初始化,所以此次设置栈为内存分配更大的空间,原来SRAM内存的空间受限,过多时易发生溢出现象,及时的将它迁移至DDR。
    
    


    **15.再次判断当前地址以决定是否重定位


    ldr    r0, =0xff000fff
    bic    r1, pc, r0        /* r0 <- current base addr of code */
    ldr    r2, _TEXT_BASE        /* r1 <- original base addr in ram */
    bic    r2, r2, r0        /* r0 <- current base addr of code */
    cmp     r1, r2                  /* compare r0, r1                  */
    beq     after_copy        /* r0 == r1 then skip flash copy   */


    分析:本次判断是为了决定是否执行uboot代码的重定位。
    (而在lowlevel_init也有判断,以决定是否执行初始化时钟和DDR的代码)
    
    uboot启动的阶段:
    1.冷启动部分:uboot的前一部分,开机时自动从SD卡加载至SRAM中去运行;
    2.第二部分(实际是整个部分):此时整个uboot还在SD卡的某个扇区开头的N个扇区中,在uboot第一阶段结束之前必须将它加载至DDR中链接地址0x23e00000处,这个过程就是重定位
    

    **16 再次对启动方式进行判断:


    
    #if defined(CONFIG_EVT1)
        /* If BL1 was copied from SD/MMC CH2 */
        ldr    r0, =0xD0037488
        ldr    r1, [r0]
        ldr    r2, =0xEB200000
        cmp    r1, r2
        beq     mmcsd_boot
    #endif

        ldr    r0, =INF_REG_BASE
        ldr    r1, [r0, #INF_REG3_OFFSET]
        cmp    r1, #BOOT_NAND        /* 0x0 => boot device is nand */
        beq    nand_boot
        cmp    r1, #BOOT_ONENAND    /* 0x1 => boot device is onenand */
        beq    onenand_boot
        cmp     r1, #BOOT_MMCSD
        beq     mmcsd_boot
        cmp     r1, #BOOT_NOR
        beq     nor_boot
        cmp     r1, #BOOT_SEC_DEV
        beq     mmcsd_boot
        


    最终跳转至mmcsd_boot函数之中进行执行。
    
    


    **17.重定位:


    mmcsd_boot:
    #if DELETE
        ldr     sp, _TEXT_PHY_BASE      
        sub     sp, sp, #12
        mov     fp, #0
    #endif
        bl      movi_bl2_copy
        b       after_copy

    nor_boot:
        bl      read_hword
        b       after_copy

    进行一个判断之后通过调用movi_bl2_copy函数程序来完成。执行完之后再跳转至after_copy函数进行执行。
    
    而movi_bl2_copy函数在uboot/cpu/s5pc11x/Movi.c文件之中。
    在这个函数值里面其实调用了另外一个函数:copy_bl2:
    copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0)
    分析参数:2:表示通道; MOVI_BL2_POS:是uboot的第二部分在SD卡的开始扇区,这个扇区必数字必须和烧录时的位置相同;
    MOVI_BL2_BLKCNT:是uboot的长度所占据的扇区数;
    CFG_PHY_UBOOT_BASE:重定位时将uboot的第二部分复制到DDR之中的起始地址。(0x23e00000)
    
    
    


    **18.设置虚拟地址映射:

  after_copy:

    #if defined(CONFIG_ENABLE_MMU)
    enable_mmu:
        /* enable domain access */
        ldr    r5, =0x0000ffff
        mcr    p15, 0, r5, c3, c0, 0        @load domain access register

        /* Set the TTB register */
        ldr    r0, _mmu_table_base
        ldr    r1, =CFG_PHY_UBOOT_BASE
        ldr    r2, =0xfff00000
        bic    r0, r0, r2
        orr    r1, r0, r1
        mcr    p15, 0, r1, c2, c0, 0

        /* Enable the MMU */
    mmu_on:
        mrc    p15, 0, r0, c1, c0, 0
        orr    r0, r0, #1
        mcr    p15, 0, r0, c1, c0, 0
        nop
        nop
        nop
        nop
    #endif


    
    分析:得到转换表的基地址并将其放置在c2之中。
        然后将cp15的c1寄存器赋值为1,开启MMU,
        注:上层软件的地址就必须通过转换表才能发给下层的物理层去执行。

    CONFIG_ENABLE_MMU在smdkv210onenand.h中有定义,这是一个和虚拟地址映射有关的关键字
    
    cp15协处理器内部有c0~c15共计6个寄存器,我们通过mrc和mcr指令来访问这些寄存器
    操作CPU协处理器其实就是操作cp15寄存器。c3在mmu之中作用就是控制域访问,控制域访问和mmu的访问控制有关。    
    


    
    **19 Set the TTB register:转换表基地址  (从此处开始,用的都是虚拟地址)


    转换表是建立虚拟地址映射的关键,转换表分为表索引和表项两部分,表索引对应虚拟地址映射,表项对应物理地址,一个表索引和表项构成一个转换单元,能够对一个内存块进行虚拟地址转换。(内存管理和映射以快为单位)转换表放置在内存之中,放置时要求起始地址在内存之中要xx位对齐,转换表不需要软件的支持,而是将基地址TTB设置到cp15的c2寄存器之中,然后MMU工作时会自动查询转换表。
    
    宏观上整个转换表可以看做是一个int型的数组,数组的元素值就是表项,元素的下标就是表索引。在uboot/board/samsung/smdkc110/lowlever_init.s中对TTB转换表基地址有着具体的定义。
    
     

   /* form a first-level section entry */
    .macro FL_SECTION_ENTRY base,ap,d,c,b
        .word (\base << 20) | (\ap << 10) | \
              (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
    .endm
    .section .mmudata, "a"
        .align 14
        // the following alignment creates the mmu table at address 0x4000.
        .globl mmu_table
    mmu_table:
        .set __base,0
        // Access for iRAM
        .rept 0x100
        FL_SECTION_ENTRY __base,3,0,0,0
        .set __base,__base+1
        .endr

        // Not Allowed
        .rept 0x200 - 0x100
        .word 0x00000000
        .endr

        .set __base,0x200
        // should be accessed
        .rept 0x600 - 0x200
        FL_SECTION_ENTRY __base,3,0,1,1
        .set __base,__base+1
        .endr
        .rept 0x800 - 0x600
        .word 0x00000000
        .endr

        .set __base,0x800
        // should be accessed
        .rept 0xb00 - 0x800
        FL_SECTION_ENTRY __base,3,0,0,0
        .set __base,__base+1
        .endr

    /*    .rept 0xc00 - 0xb00
        .word 0x00000000
        .endr */

        .set __base,0xB00
        .rept 0xc00 - 0xb00
        FL_SECTION_ENTRY __base,3,0,0,0
        .set __base,__base+1
        .endr

        .set __base,0x200
        // 256MB for SDRAM with cacheable
        .rept 0xD00 - 0xC00
        FL_SECTION_ENTRY __base,3,0,1,1
        .set __base,__base+1
        .endr

        // access is not allowed.
        @.rept 0xD00 - 0xC80
        @.word 0x00000000
        @.endr
        
        .set __base,0xD00
        // 1:1 mapping for debugging with non-cacheable
        .rept 0x1000 - 0xD00
        FL_SECTION_ENTRY __base,3,0,0,0
        .set __base,__base+1
        .endr    
        
        #else    // CONFIG_MCP_AC, CONFIG_MCP_H, CONFIG_MCP_B


    
    分析:设置了具体的分区方式和虚拟地址映射的关系。
    核心:虚拟地址映射只是将虚拟地址的c0000000开头的256MB映射到了DMC0的30000000开头的256MB物理内存之中去,其他的虚拟地址没有发生变化,映射的还是原来的物理地址。
    注:DRAM的有效范围:
        DMC0:0x30000000~0x3FFFFFFF
        DMC1:0X40000000~0X4FFFFFFF

     

   
        
    **20 第三次设置栈(DDR之中),清除bss段代码。
 

 skip_hw_init:
        /* Set up the stack                            */
    stack_setup:
    #if defined(CONFIG_MEMORY_UPPER_CODE)
        ldr    sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
    #else
        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                        */
    #if defined(CONFIG_USE_IRQ)
        sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
    #endif
        sub    sp, r0, #12        /* leave 3 words for abort-stack    */

    #endif
    
    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
    


    本次设置栈主要是为了设置一个更加安全的栈,设置了栈的起始位置,设置了栈的大小:
    栈的起始位置:CFG_UBOOT_BASE + CFG_UBOOT_SIZE:uboot起始地址上方2MB处
    栈的大小:CFG_UBOOT_BASE + CFG_UBOOT_SIZE-0x1000  约为1.8MB
    这次设置使得这个栈离我们uboot的源码储存的地方尽量的近,从而紧凑而不浪费的使用了内存空间。(uboot中的栈是满减栈,向下进行储存,所以设置的栈在减去uboot源码的空间之后有着巨大的空间可以供开发使用)
    
    而bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。
        

    


    **21 第一阶段的结束,与第二阶段进行衔接

    ldr    pc, _start_armboot
    _start_armboot:
        .word start_armboot


        
    分析:实际上start_armboot是一个指针,指向的是uboot启动源码第二部分在DDR之中存放的地址。它存放在uboot/lib_arm/board.c中。
    相当于进行一个远程的跳转指令,直接跳转至DDR中的第二阶段开始的地址处。这个远跳转是uboot启动过程第一阶段和第二阶段的分界线。
    

虚拟地址映射的简单介绍:

***1.物理地址:

物理设备设计生产时所被赋予的地址,是硬件编码的,在设计出厂之后就无法                    改(只能通过数据手册进行查询并应用)

***2.虚拟地址映射层:

在软件操作和硬件操作之间增加的一个便于编程人员进行开发的层次。
    映射层建立了一个从虚拟地址到物理地址的映射关系,当软件执行时可以通过访问虚拟地址来访问相应的物理地址,再控制硬件的操作。
    (软件程序之中使用的都是虚拟地址,而和硬件直接相关的是物理地址,虚拟地址到物理地址的映射是通过硬件实现的)
    


    **访问控制:


        在内存管理时同时对内存进行分块处理,然后对每一块进行单独的虚拟地址映射,并同时对每一块实现访问控制(只读,只写。。。。)
        例:在C语言之中出现的警告错误:segmentation fault (段错误)
        原因就是访问控制导致的,当前程序只能操作自己有权访问的地址范围(若干个内存块),如果当前程序指针访问了不属于自己的内存块,就会触发段错误。
        
   


    **cache:

快速缓存,速度比CPU慢,比DDR快。相当于两者之间的介质。
        CPU在访问DDR之前会先访问cache之中缓存的内容,然后访问DDDR之中的内容。
        CPU的速度远高于内存,当CPU直接从内存中存取数据时要等待一定时间周期,而Cache则可以保存CPU刚用过或循环使用的一部分数据,如果CPU需要再次使用该部分数据时可从Cache中直接调用,这样就避免了重复存取数据,减少了CPU的等待时间,因而提高了系统的效率。
 

    
***3.MMU单元:


    内存管理单元,实际上是SOC中的一个硬件单元,主要实现虚拟地址到物理地址的映射。
    MMU在cp15协处理器中被进行控制,所以对于虚拟地址映射的操控就是对cp15协处理器的寄存器进行编程。

lowlever_init函数的分析

/*******************************************************************************

    lowlever_init函数存在的地方:/board/samsunug/smdkc110(board)/lowlever_init.s
    具体的作用:检查复位状态,进行IO恢复,关闭看门狗、开发板的供电索存、判断代码的运行状态、时钟的初始化、DDR的初始化、后口初始化并打印“O”、tzpc初始化、打印“K”。
    
    最核心的是:1.判断当前代码的工作区域,即DDR or SRAM,从而设置不同的启动方式;
    2.通过串口的方式在uboot的启动最开始打印出“OK”可以作为很重要调试方式,初步排查问题的地方。
    
**********************************************************************************/
    


***具体的分析:

    A.与启动主线无多大影响的部分:


    

**1.压栈操作

    lowlevel_init:
    push    {lr}


    分析:进行压栈操作,以免因为后面ld调用其他的函数而导致数据的丢失
    
    


    **2.检查复位的状态:


    ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
    ldr    r1, [r0]
    bic    r1, r1, #0xfff6ffff
    cmp    r1, #0x10000
    beq    wakeup_reset_pre
    cmp    r1, #0x80000
    beq    wakeup_reset_from_didle


    
    复杂CPU的复位方式情况很复杂,所以在复位代码启动之前要去检测复位的状态。
    例如:冷上电时DDR需要初始化,热启动时(休眠、低功耗)不需要初始化DDR。
    
    
    


    3.对IO和看门狗进行设置


    /* IO Retention release **/
    ldr    r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
    ldr    r1, [r0]
    ldr    r2, =IO_RET_REL
    orr    r1, r1, r2
    str    r1, [r0]

    /* Disable Watchdog   /
    ldr    r0, =ELFIN_WATCHDOG_BASE    /* 0xE2700000 */
    mov    r1, #0
    str    r1, [r0]


    分析:对io相关的出处理,并关掉看门狗
    
    


    4.对一些SRAM  SROM  GPIO进行设置以及供电索存(与主线启动代码无关)
    


    /* SRAM(2MB) init for SMDKC110 */
    /* GPJ1 SROM_ADDR_16to21 */
    ldr    r0, =ELFIN_GPIO_BASE
    
    ldr    r1, [r0, #GPJ1CON_OFFSET]
    bic    r1, r1, #0xFFFFFF
    ldr    r2, =0x444444
    orr    r1, r1, r2
    str    r1, [r0, #GPJ1CON_OFFSET]

    ldr    r1, [r0, #GPJ1PUD_OFFSET]
    ldr    r2, =0x3ff
    bic    r1, r1, r2
    str    r1, [r0, #GPJ1PUD_OFFSET]

    /* GPJ4 SROM_ADDR_16to21 */
    ldr    r1, [r0, #GPJ4CON_OFFSET]
    bic    r1, r1, #(0xf<<16)
    ldr    r2, =(0x4<<16)
    orr    r1, r1, r2
    str    r1, [r0, #GPJ4CON_OFFSET]

    ldr    r1, [r0, #GPJ4PUD_OFFSET]
    ldr    r2, =(0x3<<8)
    bic    r1, r1, r2
    str    r1, [r0, #GPJ4PUD_OFFSET]
    
    /* PS_HOLD pin(GPH0_0) set to high */
    ldr    r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
    ldr    r1, [r0]
    orr    r1, r1, #0x300    
    orr    r1, r1, #0x1    
    str    r1, [r0]
    


    


    B.真正有意义的地方:


    
    **1.判定当前代码的执行位置(SRAM or    DDR)

ldr    r0, =0xff000fff
    bic    r1, pc, r0        /* r0 <- current base addr of code */


    分析:     r1 =pc  & ~(0xff000fff)清零pc的特定位
    注:pc加载的是运行地址的现在地址
    

  ldr    r2, _TEXT_BASE        /* r1 <- original base addr in ram */


    分析:_TEXT_BASE是我们的链接地址,是DRAM、DDR中的内容,
    

 bic    r2, r2, r0        /* r0 <- current base addr of code */


    分析:清零r2的特定位

    
    cmp     r1, r2                  /* compare r0, r1                  */


    分析:将r2与r1进行比较,以确定我们的启动方式是热启动还是冷启动。相同的话当前运行在SRAM之中,则依次执行下面的代码;如果不同的话当前运行在DDR之中,则向下跳转至“1:”位置运行其相应的代码。
    注:在裸机启动时,仅仅只是比较_start的运行地址和链接地址是否相同而确定我们当前代码的运行地址,从而确定启动的方式。而在uboot之中,pc获取的是运行地址的现在地址,显然和我们的链接地址的首地址(_TEXT_BASE)不相同,则将_TEXT_BASE和pc的地址均取相同的位进行比较。
   

  beq     1f            /* r0 == r1 then skip sdram init   */

        /* init PMIC chip */
        bl PMIC_InitIp

        /* init system clock */
        bl system_clock_init

        /* Memory initialize */
        bl mem_ctrl_asm_init


    分析:相同时直接先进行时钟和内存(DDR动态内存)的初始化(后面会有具体的函数),不相同时直接跳转“1:”执行。
    通过分析mem_ctrl_asm_init(DDR动态内存)可知在uboot中DMC0、DMC1都工作了 。 它可用的物理地址范围是0x30000000~0x4fffffff  一共是512MB,
    其中30000000~3fffffff是DMC0,40000000~4FFFFFFF是DMC1。
   (时钟和内存的初始化的一些地址在s5pc110.h文件里有定义,如果想更改时钟的初始化只需要更改.h文件里面的定义即可。)
        

    1:
        /* for UART */
        bl uart_asm_init

        bl tzpc_init    


    分析:初始化串口(函数在后面的程序之中有具体的定义),在初始化完毕后,发送一个“o”;接着初始化可信任区域。
  

 
    **2.接下来通过if判断执行一些初始化,最重要的是在最后程序正确执行完毕之后打印了“K”,这和之前打印的“O”组合一起,打印“OK”,是否打印出OK可以作为uboot启动过程的一个调试方法。
    


    #if defined(CONFIG_ONENAND)
        bl onenandcon_init
    #endif

    #if defined(CONFIG_NAND)
        /* simple init for NAND */
        bl nand_asm_init
    #endif

        /* check reset status  */
        
        ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
        ldr    r1, [r0]
        bic    r1, r1, #0xfffeffff
        cmp    r1, #0x10000
        beq    wakeup_reset_pre    
        
    /* ABB disable */
        ldr    r0, =0xE010C300
        orr    r1, r1, #(0x1<<23)
        str    r1, [r0]

        /* Print 'K' */
        ldr    r0, =ELFIN_UART_CONSOLE_BASE
        ldr    r1, =0x4b4b4b4b
        str    r1, [r0, #UTXH_OFFSET]

        pop    {pc}
        


        


    **3.一些基本的的初始化操作

 wakeup_reset_from_didle:
        /* Wait when APLL is locked */
        ldr    r0, =ELFIN_CLOCK_POWER_BASE
    lockloop:
        ldr    r1, [r0, #APLL_CON0_OFFSET]
        and    r1, r1, #(1<<29)
        cmp    r1, #(1<<29)
        bne     lockloop
        beq    exit_wakeup

    wakeup_reset_pre:
        mrc    p15, 0, r1, c1, c0, 1    @Read CP15 Auxiliary control register
        and    r1, r1, #0x80000000    @Check L2RD is disable or not
        cmp    r1, #0x80000000        
        bne    wakeup_reset        @if L2RD is not disable jump to wakeup_reset 
        
        bl    disable_l2cache
        bl    v7_flush_dcache_all
        /* L2 cache enable at sleep.S of kernel
         * bl    enable_l2cache 
         */

    wakeup_reset:
        /* init system clock */
        bl system_clock_init
        bl mem_ctrl_asm_init
        bl tzpc_init
    #if defined(CONFIG_ONENAND)
        bl onenandcon_init
    #endif
    #if defined(CONFIG_NAND)
        bl nand_asm_init
    #endif

    exit_wakeup:
        /*Load return address and jump to kernel*/
        ldr    r0, =(INF_REG_BASE+INF_REG0_OFFSET)
        ldr    r1, [r0]    /* r1 = physical address of s5pc110_cpu_resume function*/

        mov    pc, r1        /*Jump to kernel */
        nop
        nop
        
        


    **4.最后就是一系列的初始化源码:

 system_clock_init:  uart_asm_init:  nand_asm_init:  tzpc_init:   onenandcon_init:
    

完结。。。。。。。。。。。。。

    时间:2018年11月下旬
    作者:cryil_先森

     ID:qq_41464499

     转载需备注来源

猜你喜欢

转载自blog.csdn.net/qq_41464499/article/details/84716049