Advertisement

uboot:源码分析-启动第一阶段-lowlevel_init

阅读量:

lowlevel_init

找到lowlevel_init函数真正的地方,是在uboot/board/samsumg/x210/lowlevel_init.S中。
在这里插入图片描述

检查复位状态

(1)复杂CPU允许多种复位情况。譬如直接冷上电、热启动、睡眠(低功耗)状态下的唤醒等,这些情况都属于复位。所以我们在复位代码中要去检测复位状态,来判断到底是哪种情况。
(2)判断哪种复位的意义在于:冷上电时DDR是需要初始化才能用的;而热启动或者低功耗状态下的复位则不需要再次初始化DDR。

关闭看门狗
IO状态恢复
一些SRAM SROM相关GPIO设置
(1)与主线启动代码无关,暂时不用关注

供电锁存

(1)lowlevel_init.S的第100-104行,开发板供电锁存。
总结:在前100行,lowlevel_init.S中并没有做太多有意义的事情(除了关看门狗、供电锁存外),然后下面从110行才开始进行有意义的操作。

判断当前代码执行位置

复制代码
    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     1f            /* r0 == r1 then skip sdram init   */
    
    
    c
    
    

(1)lowlevel_init.S的110-115行。
(2)这几行代码的作用就是判定当前代码执行的位置在SRAM中还是在DDR中。为什么要做这个判定?原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中的BL1,如果是低功耗状态的复位这时候应该就是在DDR中的。原因2:我们判定当前代码的地址是有用的,可以指导后面代码的。譬如在lowlevel_init.S中判定当前代码的地址,就是为了确定要不要执行时钟初始化和初始化DDR的代码。如果当前代码是在SRAM中,说明冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。
(2)bic r1, pc, r0 这句代码的意义是:将pc的值中的某些bit位清0,剩下一些特殊的bit位赋值给r1(r0中为1的那些位清零)相等于:r1 = pc & ~(ff000fff)
ldr r2, _TEXT_BASE 加载链接地址到r2,然后将r2的相应位清0剩下特定位。
(3)最后比较r1和r2.
总结:这一段代码是通过读取当前地址和链接地址,然后处理两个地址后对比是否相等,来判定当前是在SRAM中(不相等)还是DDR中(相等)。从而决定是否跳过下面的时钟和DDR初始化。

system_clock_init

在这里插入图片描述
(1)system_clock_init就在当前文件的205行,一直到第385行。这个初始化时钟的过程和裸机中初始化的过程一样的,只是更加完整而且是用汇编代码写的。
(2)在x210_sd.h中300行到428行,都是和时钟相关的配置值。这些宏定义就决定了210的时钟配置是多少。也就是说代码在lowlevel_init.S中都写好了,但是代码的设置值都被宏定义在x210_sd.h中了。因此,如果移植时需要更改CPU的时钟设置,根本不需要动代码,只需要在x210_sd.h中更改配置值即可。
在这里插入图片描述

mem_ctrl_asm_init

(1)该函数用来初始化DDR
(2)函数位置在uboot/cpu/s5pc11x/s5pc110/cpu_init.S文件中。
在这里插入图片描述
DMC0_MEMCONFIG_0,在裸机中配置值为0x20E01323;在uboot中配置为0x30F01313.这个配置不同就导致结果不同。
在 裸机中DMC0的256MB内存地址范围是0x20000000-0x2FFFFFFF;
在uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF。

之前在裸机中时配置为2开头的地址,当时并没有说可以配置为3开头。从分析九鼎移植的uboot可以看出:DMC0上允许的地址范围是20000000-3FFFFFFF(一共是512MB),而我们实际只接了256MB物理内存,SoC允许我们给这256MB挑选地址范围。
总结一下:在uboot中,可用的物理地址范围为:0x30000000-0x4FFFFFFF。一共512MB,其中30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1。

需要的内存配置值在x210_sd.h的438行到468行之间。分析的时候要注意条件编译的条件,配置头文件中考虑了不同时钟配置下的内存配置值,这个的主要目的是让不同时钟需求的客户都能找到合适自己的内存配置值。
在这里插入图片描述
在uboot中DMC0和DMC1都工作了,所以在裸机中只要把uboot中的配置值和配置代码全部移植过去,应该是能够让DMC0和DMC1都工作的。

uart_asm_init

(1)这个函数用来初始化串口
在这里插入图片描述
(2)初始化完了后通过串口发送了一个’O’

pop {pc}以返回
(1)返回前通过串口打印’K’

分析;lowlevel_init.S执行完如果没错那么就会串口打印出"OK"字样。这应该是我们uboot中看到的最早的输出信

总结回顾:lowlevel_init.S中总共做了哪些事情:

检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印’O’、tzpc初始化、打印’K’。
其中值得关注的:关看门狗、开发板供电锁存、时钟初始化、DDR初始化、打印"OK"

再次设置栈(DDR中的栈)

之前在调用lowlevel_init程序前设置过1次栈(start.S 284-287行),那时候因为DDR尚未初始化,因此程序执行都是在SRAM中,所以在SRAM中分配了一部分内存作为栈。本次因为DDR已经被初始化了,因此要把栈挪移到DDR中,所以要重新设置栈,这是第二次(start.S 297-299行);这里实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。
这里都是满减栈,所以不要担心代码将uboot冲掉

复制代码
    /* get ready to call C functions */
    ldr    sp, _TEXT_PHY_BASE    /* setup temp stack pointer */
    sub    sp, sp, #12
    mov    fp, #0            /* no previous frame, so fp=0 */
    
    
    c
    
    

为什么要再次设置栈?DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在SRAM中可怜兮兮的了;原来SRAM中内存大小空间有限,栈放在那里要注意不能使用过多的栈否则栈会溢出,我们及时将栈迁移到DDR中也是为了尽可能避免栈使用时候的小心翼翼。

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

复制代码
    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   */
    
    
    c
    
    

(1)再次用相同的代码判断地址是在SRAM中还是DDR中,不过本次判断的目的不同(上次判断是为了决定是否要执行初始化时钟和DDR的代码)这次判断是为了决定是否进行uboot的relocate。
(2)冷启动时当前情况是uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到SRAM中正在,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(33e00000),这个加载过程就叫重定位。

uboot重定位详解

《S5PV210_iROM_ApplicationNote_Preliminary_20091126》
在这里插入图片描述

复制代码
    #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
    
    
    c
    
    

(1)D0037488这个内存地址在SRAM中,这个地址中的值是被硬件自动设置的。硬件根据我们实际电路中SD卡在哪个通道中,会将这个地址中的值设置为相应的数字。譬如我们从SD0通道启动时,这个值为EB000000;从SD2通道启动时,这个值为EB200000
(2)我们在start.S的260行确定了从MMCSD启动,然后又在278行将#BOOT_MMCSD写入了INF_REG3寄存器中存储着。然后又在322行读出来,再和#BOOT_MMCSD去比较,确定是从MMCSD启动。最终跳转到mmcsd_boot函数中去执行重定位动作。

复制代码
    /* SD/MMC BOOT */
    cmp     r2, #0xc
    moveq   r3, #BOOT_MMCSD    
    
    
    c
    
    

真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。是一个C语言的函数
在这里插入图片描述

复制代码
    copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
    CFG_PHY_UBOOT_BASE, 0);
    
    
    c
    
    

分析参数:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000).

学习记录,侵权联系删除。
来源:朱老师物联网大课堂

全部评论 (0)

还没有任何评论哟~