uboot2010.06移植之从nor flash启动分析

时间:2022-09-07 13:44:25

程序从start。S开始启动

start_code:
 /*
  * set the cpu to SVC32 mode  设置管理模式
  */
 mrs r0, cpsr       
 bic r0, r0, #0x1f      
 orr r0, r0, #0xd3
 msr cpsr, r0

#ifdef CONFIG_S3C24X0
 /* turn off the watchdog */

/*关看门狗 关中断*/

# if defined(CONFIG_S3C2400)
#  define pWTCON 0x15300000
#  define INTMSK 0x14400008 /* Interupt-Controller base addresses */
#  define CLKDIVN 0x14800014 /* clock divisor register */
#else
#  define pWTCON 0x53000000
#  define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
#  define INTSUBMSK 0x4A00001C
#  define CLKDIVN 0x4C000014 /* clock divisor register */
# endif

 ldr r0, =pWTCON
 mov r1, #0x0
 str r1, [r0]

 /*
  * mask all IRQs by setting all bits in the INTMR - default
  */
 mov r1, #0xffffffff
 ldr r0, =INTMSK
 str r1, [r0]
# if defined(CONFIG_S3C2410)    //shut up all interrupt
 ldr r1, =0x3ff
 ldr r0, =INTSUBMSK
 str r1, [r0]
# endif

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

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit          #跳转到cpu_init_crit处 对CPU的cashe mmu进行初始化 关闭
#endif

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:    /* relocate U-Boot to RAM     */#加载uboot到RAM对于nor flash 启动boot//本可以直接运行但是效率低所以加载到RAM中运行
 adr r0, _start  /* r0 <- current position of code   */   #加载运行起始地址到r0  如果从nor flash启动 这里为0x0
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */ #加载_TEXT_BASE到r1 _TEXT_BASE位于SDRAM中  默认为0x33f80000
 cmp r0, r1   /* don't reloc during debug         */      #比较r0 与 r1 即判断程序是从flash启动还是从RAM启动
 beq stack_setup   /*如果r0 == r1 就跳转到stack_up*/         #如果相等 表示从RAM启动 那么下面的代码搬运到RAM步骤就没比要了 直接跳到stack_setup运行

 ldr r2, _armboot_start           #代码搬运
 ldr r3, _bss_start       /*U-boot.lds中定义 uboot代码的结束地址*/
 sub r2, r3, r2  /* r2 <- size of armboot            */   #得到代码长度
 add r2, r0, r2  /* r2 <- source end address         */  #r2中存放uboot结束代码地址

copy_loop:                     /*将数据从norflash搬到SDRAM r0为_start程序开始地址r1为_TEXT_BASE属于SDRAM中的地址0x33f80000 所以我们的uboot 512KB*/
 ldmia r0!, {r3-r10}  /* copy from source address [r0]    */  #从起始地址开始搬_start
 stmia r1!, {r3-r10}  /* copy to   target address [r1]    */      #搬到目的地址 从_TEXT_BASE开始
 cmp r0, r2   /* until source end addreee [r2]    */             #判断是否搬完 即r0中的值是否==上面r2中值
 ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

 /* Set up the stack          */
stack_setup:                                                    #搬完了后设置栈  前面说的判断程序启动地址 如果从RAM启动 则直接来设置栈,有栈了后才能使用c语言。。。
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */   #栈为向下生长 在确定栈指针sp之前  先留出部分空间 放下面的内容
 sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area              */    #1  一段长度放置malloc段
 sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo                 */      #2  一段长度放置GBL段
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)   #3 还要留一段长度放置IRQ FIQ中断状态保存段
#endif
 sub sp, r0, #12  /* leave 3 words for abort-stack    */
 bic sp, sp, #7  /* 8-byte alignment for ABI compliance */                #接下来就是sp指针的位置了

clear_bss:                                                                                               #做完了后在开始跳到uboot第二阶段的C代码前 清BSS段 这里放了一些初始化为0的或者没有初始化的全局 #      变          量    局部变量  可以先清掉 在当要调用的时候再赋值
 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

 ldr pc, _start_armboot                                         #将指针指向_start_armboot  这里为第二阶段C函数的入口了

_start_armboot: .word start_armboot

 

void start_armboot (void)
{
 init_fnc_t **init_fnc_ptr;
 char *s;
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
 unsigned long addr;
#endif

 /* Pointer is writable since we allocated a register for it */
 gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
 /* compiler optimization barrier needed for GCC >= 3.4 */
 __asm__ __volatile__("": : :"memory");

 memset ((void*)gd, 0, sizeof (gd_t));
 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
 memset (gd->bd, 0, sizeof (bd_t));

 gd->flags |= GD_FLG_RELOC;

 monitor_flash_len = _bss_start - _armboot_start;

 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {                        //这里为调用一系列初始化函数  具体见uboot源码,就是初始化各个外设模块 串口啊 GPIO USB
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
 }

 /* armboot_start is defined in the board-specific linker script */
 mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
   CONFIG_SYS_MALLOC_LEN);

#ifndef CONFIG_SYS_NO_FLASH
 /* configure available FLASH banks */
 display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */

#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
# endif
 /*
  * reserve memory for VFD display (always full pages)
  */
 /* bss_end is defined in the board-specific linker script */
 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 vfd_setmem (addr);
 gd->fb_base = addr;
#endif /* CONFIG_VFD */

#ifdef CONFIG_LCD
 /* board init may have inited fb_base */
 if (!gd->fb_base) {
#  ifndef PAGE_SIZE
#    define PAGE_SIZE 4096
#  endif
  /*
   * reserve memory for LCD display (always full pages)
   */
  /* bss_end is defined in the board-specific linker script */
  addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
  lcd_setmem (addr);
  gd->fb_base = addr;
 }
#endif /* CONFIG_LCD */

#if defined(CONFIG_CMD_NAND)
 puts ("NAND:  ");
 nand_init();  /* go init the NAND */
#endif

#if defined(CONFIG_CMD_ONENAND)
 onenand_init();
#endif

#ifdef CONFIG_HAS_DATAFLASH
 AT91F_DataflashInit();
 dataflash_print_info();
#endif

 /* initialize environment */
 env_relocate ();

#ifdef CONFIG_VFD
 /* must do this after the framebuffer is allocated */
 drv_vfd_init();
#endif /* CONFIG_VFD */

#ifdef CONFIG_SERIAL_MULTI
 serial_initialize();
#endif

 /* IP Address */
 gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

 stdio_init (); /* get the devices list going. */

 jumptable_init ();

#if defined(CONFIG_API)
 /* Initialize API */
 api_init ();
#endif

 console_init_r (); /* fully init console as a device */

#if defined(CONFIG_ARCH_MISC_INIT)
 /* miscellaneous arch dependent initialisations */
 arch_misc_init ();
#endif
#if defined(CONFIG_MISC_INIT_R)
 /* miscellaneous platform dependent initialisations */
 misc_init_r ();
#endif

 /* enable exceptions */
 enable_interrupts ();

 /* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
 /* XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (const u_int8_t *addr);
 if (getenv ("ethaddr")) {
  uchar enetaddr[6];
  eth_getenv_enetaddr("ethaddr", enetaddr);
  davinci_eth_set_mac_addr(enetaddr);
 }
#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
 /* XXX: this needs to be moved to board init */
 if (getenv ("ethaddr")) {
  uchar enetaddr[6];
  eth_getenv_enetaddr("ethaddr", enetaddr);
  smc_set_mac_addr(enetaddr);
 }
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

 /* Initialize from environment */
 if ((s = getenv ("loadaddr")) != NULL) {
  load_addr = simple_strtoul (s, NULL, 16);
 }
#if defined(CONFIG_CMD_NET)
 if ((s = getenv ("bootfile")) != NULL) {
  copy_filename (BootFile, s, sizeof (BootFile));
 }
#endif

#ifdef BOARD_LATE_INIT
 board_late_init ();
#endif

#ifdef CONFIG_GENERIC_MMC
 puts ("MMC:   ");
 mmc_initialize (gd->bd);
#endif

#ifdef CONFIG_BITBANGMII
 bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
 puts ("Net:   ");
#endif
 eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
 debug ("Reset Ethernet PHY\n");
 reset_phy();
#endif
#endif
 /* main_loop() can return to retry autoboot, if so just run it again. */
 for (;;) {
  main_loop ();                     //上面各个板级外设初始化完成后 开始进入main_loop()死循环
 }

 /* NOTREACHED - no way out of command loop except booting */
}

void main_loop (void)

s = getenv ("bootdelay");
 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;        //如果定义了延时 加载这条  这里涉及一个重要的函数getenv()获得环境变量

run_command (s, 0);       //死循环中运行命令函数  我们在串口输入各种命令 就是靠这个函数解析