TQ2440之U-Boot-1.1.6之第一阶段代码详解


 

根据Board/EmbedSky/u-boot.lds这个链接脚本文件,可知:

———————————————————————————————————

SECTIONS
{
     . = 0x00000000;

     . = ALIGN(4);
     .text      :
      {
            
cpu/arm920t/start.o (.text)
                    board/EmbedSky/boot_init.o (.text)
                    board/EmbedSky/lowlevel_init.o (.text)
                    *(.text)

      }

      . = ALIGN(4);
      .rodata : { *(.rodata) }

      . = ALIGN(4);
      .data : { *(.data) }

      . = ALIGN(4);
      .got : { *(.got) }

      . = .;
      __u_boot_cmd_start = .;
      .u_boot_cmd : { *(.u_boot_cmd) }
      __u_boot_cmd_end = .;

      . = ALIGN(4);
      __bss_start = .;
      .bss : { *(.bss) }
      _end = .;
}

————————————————————————————————————

可知U-Boot首先编译cpu/arm920t/start.S这个汇编源文件,被放在程序的最前面。

这个汇编源文件依次完成了如下硬件初始化设置:(下面开始分析start.S源代码)

————————————————————————————————————

.globl _start_start: b       reset

1、将CPU的工作模式设置为管理模式(SVC);

reset:
 /*
  * set the cpu to SVC32 mode
  */
      mrs r0,cpsr
      bic r0,r0,#0x1f
      orr r0,r0,#0xd3
      msr cpsr,r0

 

2、关闭看门狗(WATCHDG);

/* 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) || defined(CONFIG_S3C2440)
# define pWTCON  0x53000000
# define INTMOD  0X4A000004
# define INTMSK  0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif

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

3、屏蔽所有中断

 /*
  * 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]
# elif defined(CONFIG_S3C2440)
     ldr r1, =0x7fff
     ldr r0, =INTSUBMSK
     str r1, [r0]
# endif

4、设置FCLK、HCLK、PCLK的比例(这里实际上是没有设置的,后面会有C函数来完成);

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

接着一个宏定义判断

 /*
  * we do sys-critical inits only at reboot,
  * not when booting from ram!
  */


#ifndef CONFIG_SKIP_LOWLEVEL_INIT
     bl cpu_init_crit
#endif

在根目录下的 include/configs/EmbedSky.h文件里确实没有宏定义CONFIG_SKIP_LOWLEVEL_INIT,那么就跳入到cpu_init_crit这个函数里面,这个函数在start.S的第265行开始到292行结束。

5、cpu_init_crit这个函数关闭CACHES、MMU

     MCR  MRC  C7 C8这些属于ARM架构的协指令,在《ARM体系架构与编程》,杜春雷著,这本书写的很详细,具体语法我就不多说了,有兴趣的去看可看。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_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 */

我们注意到,bl   lowlevel_init这句,这个函数在board/EmbedSky/lowlevel_init.S这个汇编源代码里面

6、为加载bootloader的第二阶段代码准备RAM空间, 下面这段代码就是初始化内存芯片,使SDRAM可以使用。。

_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  //TEXT_BASE = 0x33D00000
 sub r0, r0, r1

 

/*这三句,完成了地址变换,因为这个时候内存中还没有数据,不能使用.lds文件里面确定的地址来读取数据。*/

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

 /* everything is fine now */
     mov pc, lr

 .ltorg
/* the literal pools origin */

/*  SMRDATA表示这13个寄存器的值存放的开始地址(连续地址),处于内存中。 */

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 .........

————————————————————————————————————

 7、设置栈,为第二阶段的C函数做好准备。

 /* Set up the stack          */
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    */


8、时钟初始化

    bl  clock_init

      其实上面的时钟初始化,在汇编源文件里是无效的,因为使用了#if 0 ,此时这段区域里的代码都无效,所以这里要用C函数初始化时钟,正好响应了第七步初始化栈。clock_init这个函数究竟在哪里呢,在start.S里面是找不到它的函数的,我们回头看看u-boot.lds看到, board/EmbedSky/boot_init.o (.text),正好,就是在boot_init.c 文件里面。

void clock_init(void)
{
      S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000;

      /* FCLK:HCLK:PCLK = ?:?:? */
#if CONFIG_133MHZ_SDRAM
      clk_power->CLKDIVN = S3C2440_CLKDIV136;   //HJ 1:3:6
#else
      clk_power->CLKDIVN = S3C2440_CLKDIV;    //HJ 1:4:8
#endif
 /* change to asynchronous bus mod */
 __asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */ 
                    "orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */ 
                    "mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */ 
                    :::"r1"
                    );

 /* to reduce PLL lock time, adjust the LOCKTIME register */
        clk_power->LOCKTIME = 0xFFFFFF;

 /* configure UPLL */
       clk_power->UPLLCON = S3C2440_UPLL_48MHZ;  //fin=12.000MHz
// clk_power->UPLLCON = S3C2440_UPLL_48MHZ_Fin16MHz; //fin=16.934MHz

 /* some delay between MPLL and UPLL */
       delay (4000);

 /* configure MPLL */
 clk_power->MPLLCON = S3C2440_MPLL_400MHZ;  //fin=12.000MHz
// clk_power->MPLLCON = S3C2440_MPLL_405MHZ;    //HJ 405MHz
// clk_power->MPLLCON = S3C2440_MPLL_440MHZ;    //HJ 440MHz
// clk_power->MPLLCON = S3C2440_MPLL_480MHZ;    //HJ 480MHz
// clk_power->MPLLCON = S3C2440_MPLL_399MHz;  //fin=16.934MHz
 /* some delay between MPLL and UPLL */
      delay (8000);
}

9、复制bootloader的第二阶段代码到RAM空间去。

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:    /* relocate U-Boot to RAM     */
         adr r0,  _start                         /* r0:当前代码的位置   */
         ldr  r1,  _TEXT_BASE          /* r1:代码段的链接地址 */
         cmp     r0, r1                       /* 测试现在是在Flash中还是在RAM 中 */
         beq     clear_bss               /*如果已经在RAM中就执行清除bss函数*/

         ldr  r2,  _armboot_start
         ldr  r3,  _bss_start
         sub r2, r3, r2  /* r2 <- size of armboot            */
#if 1
         bl  CopyCode2Ram  /* r0: source, r1: dest, r2: size */
#else
        add r2, r0, r2  /* r2 <- source end address         */

copy_loop:
        ldmia  r0!,  {r3-r10}  /* copy from source address [r0]    */
        stmia  r1!,   {r3-r10}  /* copy to   target address [r1]    */  //r1=_TEXT_BASE  是目标地址
        cmp r0, r2   /* until source end addreee [r2]    */
        ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

———————————————————————————————————

bl  CopyCode2Ram 在board/EmbedSky/boot_init.c里面,这个函数实现将启动代码拷贝到内存中。

———————————————————————————————————

int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
      unsigned int *pdwDest;
      unsigned int *pdwSrc;
      int i;

      if (bBootFrmNORFlash())
      {
            pdwDest = (unsigned int *)buf;
            pdwSrc  = (unsigned int *)start_addr;
            /* 从 NOR Flash启动 */
           for (i = 0; i < size / 4; i++)
           {
                   pdwDest[i] = pdwSrc[i];
            }
           return 0;
      }
      else
     {
            /* 初始化NAND Flash */
            nand_init_ll();

            /* 从 NAND Flash启动 */
           if (NF_ReadID() == 0x76 )
               nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
           else
               nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
           return 0;
      }
}

—————————————————————————————————————

10、清除bss段(初始值为0、无初始化值的全局变量,静态变量放在BSS段)

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

 

第一阶段的最后一步:C函数的运行环境一切准备好了,它将跳到第二阶段的入口点:

ldr pc, _start_armboot

_start_armboot: 

                .word start_armboot 

/* start_armboot是第二阶段的第一个C函数,在/lib_arm/board.c中定义  */

 

好了,U-Boot第一阶段代码就分析到这里,里面的语法大家慢慢体会,有不懂的可以互相交流一下。

祝愿大家学习愉快。^_^

 

 


你可能感兴趣的:(异常处理,单片机,makefile,编译器,汇编语言)