zoukankan      html  css  js  c++  java
  • u-boot分析(二)----工作流程分析

    u-boot分析(二)

    由于这两天家里有点事,所以耽误了点时间,没有按时更新,今天我首先要跟大家说说我对于u-boot分析的整体的思路,然后呢我以后的博客会按照这个内容更新,希望大家关注。

    言归正传,我首先说一说我以后的思路,对于u-boot呢,我会结合24406410210这三款主流的学习芯片进行分析,首先会结合u-boot的源码以及我以前的arm启动流程一文http://www.cnblogs.com/wrjvszq/p/4204703.html 总结出u-boot的工作流程,然后以后的博文会结合u-boot源码、芯片手册等内容去分析,u-boot为什么去这么做。

    Ok我们下面进入我们今天的内容,今天我会以210为例来分析其u-boot的工作流程,因为2440,6410的启动流程大家跟着u-boot源码中的注释就能解决。

    我们根据上篇博文(http://www.cnblogs.com/wrjvszq/p/4206975.html)中提到的方法,可以得到其的入口为archarmcpuarmv7start.S中的_start我们可以轻松的找到其对应代码,下面我们对其工作流程进行分析:

    1.     设置中断向量表。

     1 .globl _start
     2 
     3 _start: b    reset
     4 
     5       ldr  pc, _undefined_instruction
     6 
     7       ldr  pc, _software_interrupt
     8 
     9       ldr  pc, _prefetch_abort
    10 
    11       ldr  pc, _data_abort
    12 
    13       ldr  pc, _not_used
    14 
    15       ldr  pc, _irq
    16 
    17       ldr  pc, _fiq

    2.     设置处理器到svc的模式(因为我们的上电将会触发reset

     

    1 reset:
    2     bl    save_boot_params//空函数
    3     /*
    4      * set the cpu to SVC32 mode
    5      */
    6     mrs    r0, cpsr
    7     bic    r0, r0, #0x1f
    8     orr    r0, r0, #0xd3
    9     msr    cpsr,r0

     

     

    继续跟随代码往下走我们会找到

    1 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
    2 
    3       bl   cpu_init_crit//跳转至cpu_init_crit
    4 
    5 #endif
    6 
    7 cpu_init_crit:

    3.     L1I/D caches失效

     

     1 cpu_init_crit:
     2     /*
     3      * Invalidate L1 I/D
     4      */
     5     mov    r0, #0            @ set up for MCR
     6     mcr    p15, 0, r0, c8, c7, 0    @ invalidate TLBs
     7     mcr    p15, 0, r0, c7, c5, 0    @ invalidate icache
     8     mcr    p15, 0, r0, c7, c5, 6    @ invalidate BP array
     9     mcr     p15, 0, r0, c7, c10, 4    @ DSB
    10     mcr     p15, 0, r0, c7, c5, 4    @ ISB

     

     

    4.     关闭MMUcaches      

     

     1     /*
     2      * disable MMU stuff and caches
     3      */
     4     mrc    p15, 0, r0, c1, c0, 0
     5     bic    r0, r0, #0x00002000    @ clear bits 13 (--V-)
     6     bic    r0, r0, #0x00000007    @ clear bits 2:0 (-CAM)
     7     orr    r0, r0, #0x00000002    @ set bit 1 (--A-) Align
     8     orr    r0, r0, #0x00000800    @ set bit 11 (Z---) BTB
     9 #ifdef CONFIG_SYS_ICACHE_OFF
    10     bic    r0, r0, #0x00001000    @ clear bit 12 (I) I-cache
    11 #else
    12     orr    r0, r0, #0x00001000    @ set bit 12 (I) I-cache
    13 #endif
    14     mcr    p15, 0, r0, c1, c0, 0

    下面代码我们将跳转至lowlevel_init(我们在此以三星的smart210为例)我们可以在oardsamsungsmart210lowlevel_init.S中找到

     

     1     /*
     2      * Jump to board specific initialization...
     3      * The Mask ROM will have already initialized
     4      * basic memory. Go here to bump up clock rate and handle
     5      * wake up conditions.
     6      */
     7     mov    ip, lr            @ persevere link reg across call
     8     bl    lowlevel_init        @ go setup pll,mux,memory
     9     mov    lr, ip            @ restore link
    10     mov    pc, lr            @ back to my caller
    11 #endif
    12 
    13 #ifndef CONFIG_SPL_BUILD

    5.     检查reset状态(reset分为两种1、掉电的reset   2、从睡眠唤醒的reset)所以要对其进行区分,以便跳过部分代码。

     

     1     push    {lr}//保存lr的值以便待会返回
     2 
     3     /* check reset status  */
     4 
     5     ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
     6     ldr    r1, [r0]
     7     bic    r1, r1, #0xfff6ffff
     8     cmp    r1, #0x10000
     9     beq    wakeup_reset_pre
    10     cmp    r1, #0x80000
    11     beq    wakeup_reset_from_didle

     

     

    6.     恢复IO引脚为默认值

     

    1     /* IO Retention release */
    2     ldr    r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
    3     ldr    r1, [r0]
    4     ldr    r2, =IO_RET_REL
    5     orr    r1, r1, r2
    6     str    r1, [r0]

     

    7.     关闭看门狗

     

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

     

    8.     SRAMSROM初始化

     

     1 /* SRAM(2MB) init for SMDKC110 */
     2     /* GPJ1 SROM_ADDR_16to21 */
     3     ldr    r0, =ELFIN_GPIO_BASE
     4 
     5     ldr    r1, [r0, #GPJ1CON_OFFSET]
     6     bic    r1, r1, #0xFFFFFF
     7     ldr    r2, =0x444444
     8     orr    r1, r1, r2
     9     str    r1, [r0, #GPJ1CON_OFFSET]
    10 
    11     ldr    r1, [r0, #GPJ1PUD_OFFSET]
    12     ldr    r2, =0x3ff
    13     bic    r1, r1, r2
    14     str    r1, [r0, #GPJ1PUD_OFFSET]
    15 
    16     /* GPJ4 SROM_ADDR_16to21 */
    17     ldr    r1, [r0, #GPJ4CON_OFFSET]
    18     bic    r1, r1, #(0xf<<16)
    19     ldr    r2, =(0x4<<16)
    20     orr    r1, r1, r2
    21     str    r1, [r0, #GPJ4CON_OFFSET]
    22 
    23     ldr    r1, [r0, #GPJ4PUD_OFFSET]
    24     ldr    r2, =(0x3<<8)
    25     bic    r1, r1, r2
    26     str    r1, [r0, #GPJ4PUD_OFFSET]
    27 
    28 
    29     /* CS0 - 16bit sram, enable nBE, Byte base address */
    30     ldr    r0, =ELFIN_SROM_BASE    /* 0xE8000000 */
    31     mov    r1, #0x1
    32     str    r1, [r0]
    33 
    34     /* PS_HOLD pin(GPH0_0) set to high */
    35     ldr    r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
    36     ldr    r1, [r0]
    37     orr    r1, r1, #0x300
    38     orr    r1, r1, #0x1
    39     str    r1, [r0]

     

    以下代码是在判断是不是在内存中运行,如果是在内存中运行则跳过部分代码,我们这是第一次运行BL,并且从nand启动的所以代码不在内存中不用关心。

     

     1 /* when we already run in ram, we don't need to relocate U-Boot.
     2      * and actually, memory controller must be configured before U-Boot
     3      * is running in ram.
     4      */
     5     ldr    r0, =0x00ffffff
     6     bic    r1, pc, r0        /* r0 <- current base addr of code */
     7     ldr    r2, _TEXT_BASE        /* r1 <- original base addr in ram */
     8     bic    r2, r2, r0        /* r0 <- current base addr of code */
     9     cmp     r1, r2                  /* compare r0, r1                  */
    10     beq     1f            /* r0 == r1 then skip sdram init   */

     

    9.     初始化时钟

    1 /* init system clock */
    2     bl system_clock_init

     

    10.  初始化内存

    1 /* Memory initialize */
    2     bl mem_ctrl_asm_init

     

    11.  串口简单初始化

    1 1:
    2     /* for UART */
    3     bl uart_asm_init

     

    12.  取消存储保护区

    1     bl tzpc_init
    2  //我们没有用onenand所以跳过
    3 
    4 #if defined(CONFIG_ONENAND)
    5     bl onenandcon_init
    6 #endif

     

    13.  简单初始化nand

    1 #if defined(CONFIG_NAND)
    2     /* simple init for NAND */
    3     bl nand_asm_init
    4 #endif

     

    14.  关闭ABB

     1     /* ABB disable */
     2     ldr    r0, =0xE010C300
     3     orr    r1, r1, #(0x1<<23)
     4     str    r1, [r0]
     5 
     6     /* Print 'K' */
     7     ldr    r0, =ELFIN_UART_CONSOLE_BASE
     8     ldr    r1, =0x4b4b4b4b
     9     str    r1, [r0, #UTXH_OFFSET]
    10 
    11     pop    {pc}//函数返回到调用者

     

    代码将会返回到这里

     1     /*
     2      * Jump to board specific initialization...
     3      * The Mask ROM will have already initialized
     4      * basic memory. Go here to bump up clock rate and handle
     5      * wake up conditions.
     6      */
     7     mov    ip, lr            @ persevere link reg across call
     8     bl    lowlevel_init        @ go setup pll,mux,memory
     9     mov    lr, ip            @ restore link
    10     mov    pc, lr            @ back to my caller

     

    代码继续返回到

     

    1     /* the mask ROM code should have PLL and others stable */
    2 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
    3     bl    cpu_init_crit
    4 #endif

     

    15.  设置堆栈

    1 /* Set stackpointer in internal RAM to call board_init_f */
    2 call_board_init_f:
    3     ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
    4     bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */
    5     ldr    r0,=0x00000000

     

    下面代码在判断我们现在是不是在内存中运行,如果在内存将跳转至board_init_in_ram

     

    1 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210)
    2     adr    r4, _start
    3     ldr    r5,_TEXT_BASE
    4     cmp     r5,r4
    5     beq    board_init_in_ram

          下面代码在判断从什么地方启动的我们是从nand启动的所以跳转至nand_boot_210

     

     1 ldr    r0, =PRO_ID_BASE
     2         ldr    r1, [r0,#OMR_OFFSET]
     3         bic    r2, r1, #0xffffffc1
     4 
     5     /* NAND BOOT */
     6     cmp    r2, #0x0        @ 512B 4-cycle
     7     moveq    r3, #BOOT_NAND
     8 
     9     cmp    r2, #0x2        @ 2KB 5-cycle
    10     moveq    r3, #BOOT_NAND
    11 
    12     cmp    r2, #0x4        @ 4KB 5-cycle    8-bit ECC
    13     moveq    r3, #BOOT_NAND
    14 
    15     cmp    r2, #0x6        @ 4KB 5-cycle    16-bit ECC
    16     moveq    r3, #BOOT_NAND
    17 
    18     cmp    r2, #0x8        @ OneNAND Mux
    19     moveq    r3, #BOOT_ONENAND
    20 
    21     /* SD/MMC BOOT */
    22     cmp     r2, #0xc
    23     moveq   r3, #BOOT_MMCSD    
    24 
    25     /* NOR BOOT */
    26     cmp     r2, #0x14
    27     moveq   r3, #BOOT_NOR    
    28 
    29     /* Uart BOOTONG failed */
    30     cmp     r2, #(0x1<<4)
    31     moveq   r3, #BOOT_SEC_DEV
    32     
    33     ldr    r0, =INF_REG_BASE
    34     str    r3, [r0, #INF_REG3_OFFSET]
    35 
    36     ldr    r1, [r0, #INF_REG3_OFFSET]
    37     cmp    r1, #BOOT_NAND        /* 0x0 => boot device is nand */
    38     beq    nand_boot_210
    39     cmp     r1, #BOOT_MMCSD
    40     beq     mmcsd_boot_210
    41     

    下面代码将会跳转至board_init_f_nand

    1 nand_boot_210:
    2     bl     board_init_f_nand
    3 
    4 mmcsd_boot_210:
    5     bl     board_init_f
    6 board_init_in_ram:
    7 #endif
    8     bl    board_init_f

     

     

           我们继续可以看看board_init_f_nand

     1 void board_init_f_nand(unsigned long bootflag)
     2 {
     3         __attribute__((noreturn)) void (*uboot)(void);
     4         copy_uboot_to_ram_nand();//复制nand中的bl2到内存
     5 
     6         /* Jump to U-Boot image */
     7         uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳转至内存执行uboot
     8     (*uboot)();
     9         /* Never returns Here */
    10 }

    16.  复制nand中的bl2到内存

    l  我们可以到copy_uboot_to_ram_nand函数中看到其将我们的BL2复制到CONFIG_SYS_TEXT_BASE地址处,用SI(Source Insight)我们可以看到这个地址为0x23E00000,我在arm启动流程一文中说到过210的地址布局可以知道其内存是从0x20000000开始的,所以上面这个地址在内存中

     

     1 int copy_uboot_to_ram_nand (void)
     2 {
     3     int large_block = 0;
     4     int i;
     5     vu_char id;
     6 
     7     NAND_CONTROL_ENABLE();
     8         NAND_ENABLE_CE();
     9         NFCMD_REG = NAND_CMD_READID;
    10         NFADDR_REG =  0x00;
    11 
    12     /* wait for a while */
    13         for (i=0; i<200; i++);
    14     id = NFDATA8_REG;
    15     id = NFDATA8_REG;
    16 
    17     if (id > 0x80)
    18         large_block = 1;
    19 
    20     /* read NAND Block.
    21      * 128KB ->240KB because of U-Boot size increase. by scsuh
    22      * So, read 0x3c000 bytes not 0x20000(128KB).
    23      */
    24     return nandll_read_blocks(CONFIG_SYS_TEXT_BASE, COPY_BL2_SIZE, large_block);
    25 }

     

    17.  跳转至内存执行uboot

    l  通运行代码段的方式完成向BL2的跳转 

    1         /* Jump to U-Boot image */
    2         uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳转至内存执行uboot
    3     (*uboot)();

     

    到此我们第一阶段的代码分析完成,接下来我们进行第二阶段的分析,由于我们的210第二阶段的代码入口和第一阶段相同,所以第一阶段执行的代码又会被执行一次,直到运行到下面代码的时候才会发生变化

     

    1 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210)
    2     adr    r4, _start
    3     ldr    r5,_TEXT_BASE
    4     cmp     r5,r4
    5     beq    board_init_in_ram

     

    我们代码将会跳到board_init_in_ram运行

    1 board_init_in_ram:
    2 #endif
    3     bl    board_init_f

     

    我们继续跳进board_init_f这个函数在archarmliboard.c中在这个函数中比较重要的一段代码,下面的代码是在遍历一个函数指针数组,并且一一执行

     

    1     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
    2         if ((*init_fnc_ptr)() != 0) {
    3             hang ();
    4         }
    5     }

     

    通过查找我们可以发现这个数组中的内容,是一大堆东西的初始化函数,也就是说上面的代码进行了一系列的初始化

     

     1 init_fnc_t *init_sequence[] = {
     2 #if defined(CONFIG_ARCH_CPU_INIT)
     3     arch_cpu_init,        /* basic arch cpu dependent setup */
     4 #endif
     5 #if defined(CONFIG_BOARD_EARLY_INIT_F)
     6     board_early_init_f,
     7 #endif
     8     timer_init,        /* initialize timer */
     9 #ifdef CONFIG_FSL_ESDHC
    10     get_clocks,
    11 #endif
    12     env_init,        /* initialize environment */
    13     init_baudrate,        /* initialze baudrate settings */
    14     serial_init,        /* serial communications setup */
    15     console_init_f,        /* stage 1 init of console */
    16     display_banner,        /* say that we are here */
    17 #if defined(CONFIG_DISPLAY_CPUINFO)
    18     print_cpuinfo,        /* display cpu info (and speed) */
    19 #endif
    20 #if defined(CONFIG_DISPLAY_BOARDINFO)
    21     checkboard,        /* display board info */
    22 #endif
    23 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    24     init_func_i2c,
    25 #endif
    26     dram_init,        /* configure available RAM banks */
    27     NULL,
    28 };

     

    经过上面的代码后其会返回到start.s中继续运行,中间又经过一系列东西,好多和第一阶段重复,所以这里就不一一分析了,最终通过下面代码进入board_init_r函数

     

     1     ldr    r0, _board_init_r_ofs
     2     adr    r1, _start
     3     add    lr, r0, r1
     4     add    lr, lr, r9
     5     /* setup parameters for board_init_r */
     6     mov    r0, r5        /* gd_t */
     7     mov    r1, r6        /* dest_addr */
     8     /* jump to it ... */
     9     mov    pc, lr
    10 
    11 _board_init_r_ofs:
    12     .word board_init_r - _start

     

    在这个函数中又进行了硬件和软件的初始化最后会落脚在

     

    1     /* main_loop() can return to retry autoboot, if so just run it again. */
    2     for (;;) {
    3         main_loop();
    4     }

     

    通过上面的代码对用户命令进行解析。至此210u-boot工作流程分析完毕,24406410比较简单,基本按照注释就可以总结出来。

    下面我将自己总结的流程跟大家分享一下

    l  2440

    第一阶段

    1.        设置中断向量表

    2.        设置处理器工作模式为svc

    3.        刷新I/Dcaches

    4.        关闭MMUcaches

    5.        关闭看门狗

    6.        关所有中断

    7.        设置系统时钟

    8.        初始化串口

    9.        初始化nand

    10.    初始化内存

    11.    复制BL到内存

    12.    设置堆栈

    13.    清楚BSS

    第二阶段

    1.        初始化串口

    2.        LCD初始化

    3.        网卡初始化

    4.        Led初始化

    5.        执行用户命令

    l  6410

    第一阶段

    1.        设置中断向量表

    2.        设置处理器工作模式为svc

    3.        刷新I/Dcaches

    4.        关闭MMUcaches

    5.        外设基地址初始化

    6.        点亮led

    7.        关看门狗

    8.        初始化时钟

    9.        初始化串口

    10.    初始化nand

    11.    初始化内存

    12.    复制BL到内存

    13.    设置堆栈

    14.    清楚BSS

    第二阶段

    1.        初始化串口

    2.        LCD初始化

    3.        网卡初始化

    4.        Led初始化

    5.        执行用户命令

    l  210

    第一阶段

    1.        设置中断向量表

    2.        设置处理器工作模式为svc

    3.        L1I/D caches失效

    4.        关闭MMUcaches

    5.        检查reset状态

    6.        回复io引脚为默认值

    7.        关看门狗

    8.        Sramsrom初始化

    9.        初始化时钟

    10.    初始化内存

    11.    初始化串口

    12.    取消存储保护区

    13.    初始化nand

    14.    关闭ABB

    15.    设置堆栈

    16.    复制BL2到内存

    17.    跳转到内存执行

    第二阶段

    1.        初始化串口

    2.        LCD初始化

    3.        网卡初始化

    4.        Led初始化

    5.        执行用户命令

     

  • 相关阅读:
    ISO/IEC 9899:2011 条款6.10.3——宏替换
    ISO/IEC 9899:2011 条款6.10.2——源文件包含
    关于Objective-C新增的__kindof关键字
    ISO/IEC 9899:2011 条款6.10.1——条件包含
    ISO/IEC 9899:2011 条款6.10——预处理指示符
    ISO/IEC 9899:2011 条款6.9.2——外部对象定义
    Objective-C中使用不定参数个数的方法调用
    php添加数据到xml文件的例子
    nginx rewrite重写与防盗链配置
    nginx url自动加斜杠的问题
  • 原文地址:https://www.cnblogs.com/wrjvszq/p/4215627.html
Copyright © 2011-2022 走看看