U-Boot没有串口打印信息 调试u-boot

假如U-Boot没有任何串口打印信息,手头又没有硬件调试工具,那样怎么知道U-Boot执行到什么地方了呢?可以通过开发板上的LED指示灯判断。
  
开发板上最好设计安装八段数码管等LED,可以用来显示数字或者数字位。
U-Boot可以定义函数show_boot_progress (int status),用来指示当前启动进度。在include/common.h头文件中声明这个函数。

#ifdef CONFIG_SHOW_BOOT_PROGRESS
void    show_boot_progress (int status);
#endif

  
CONFIG_SHOW_BOOT_PROGRESS是需要定义的。这个在板子配置的头文件中定义。CSB226开发板对这项功能有完整实现,可以参考。在头文件include/configs/csb226.h中,有下列一行。

#define CONFIG_SHOW_BOOT_PROGRESS        1

  
函数show_boot_progress (int status)的实现跟开发板关系密切,所以一般在board目录下的文件中实现。看一下CSB226在board/csb226/csb226.c中的实现函数。

/** 设置CSB226板的0、1、2三个指示灯的开关状态
 * csb226_set_led: - switch LEDs on or off
 * @param led:   LED to switch (0,1,2)
 * @param state: switch on (1) or off (0)
 */
void csb226_set_led(int led, int state)
{
      switch(led) {
             case 0: if (state==1) {
                              GPCR0 |= CSB226_USER_LED0;
                    } else if (state==0) {
                            GPSR0 |= CSB226_USER_LED0;
                    }
                    break;
             case 1: if (state==1) {
                              GPCR0 |= CSB226_USER_LED1;
                    } else if (state==0) {
                              GPSR0 |= CSB226_USER_LED1;
                    }
                    break;
             case 2: if (state==1) {
                              GPCR0 |= CSB226_USER_LED2;
                  } else if (state==0) {
                          GPSR0 |= CSB226_USER_LED2;
                  }
                  break;
      }
      return;
}
/** 显示启动进度函数,在比较重要的阶段,设置三个灯为亮的状态(1, 5, 15)*/
void show_boot_progress (int status)
{
      switch(status) {
            case  1: csb226_set_led(0,1); break;
            case  5: csb226_set_led(1,1); break;
            case 15: csb226_set_led(2,1); break;
      }
      return;
}

  
这样,在U-Boot启动过程中就可以通过show_boot_progresss指示执行进度。比如hang()函数是系统出错时调用的函数,这里需要根据特定的开发板给定显示的参数值。

void hang (void)
{
      puts ("### ERROR ### Please RESET the board ###\n");
#ifdef CONFIG_SHOW_BOOT_PROGRESS
      show_boot_progress(-30);
#endif
      for (;;);
}

尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。
  开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。函数调用顺序如图6.3所示。
  看一下board/smsk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。
1.cpu/arm920t/start.S
  这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。


_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      //中断向量

 /* the actual reset code  */
reset:             //复位启动子程序
       /* 设置CPU为SVC32模式 */
       mrs    r0,cpsr
       bic    r0,r0,#0x1f
       orr    r0,r0,#0xd3
       msr    cpsr,r0
/* 关闭看门狗 */

/* 这些初始化代码在系统重起的时候执行,运行时热复位从RAM中启动不执行 */
#ifdef CONFIG_INIT_CRITICAL
       bl    cpu_init_crit
#endif

relocate:                        /* 把U-Boot重新定位到RAM    */
       adr    r0, _start            /* r0是代码的当前位置 */
       ldr    r1, _TEXT_BASE        /* 测试判断是从Flash启动,还是RAM */
       cmp     r0, r1           /* 比较r0和r1,调试的时候不要执行重定位 */
       beq     stack_setup    /* 如果r0等于r1,跳过重定位代码 */
       /* 准备重新定位代码 */
       ldr    r2, _armboot_start
       ldr    r3, _bss_start
       sub    r2, r3, r2            /* r2 得到armboot的大小   */
       add    r2, r0, r2            /* r2 得到要复制代码的末尾地址 */
copy_loop:     /* 重新定位代码 */
       ldmia    r0!, {r3-r10}    /*从源地址[r0]复制 */
       stmia    r1!, {r3-r10}    /* 复制到目的地址[r1] */
       cmp    r0, r2            /* 复制数据块直到源数据末尾地址[r2] */
       ble    copy_loop

       /* 初始化堆栈等    */
stack_setup:
       ldr    r0, _TEXT_BASE                /* 上面是128 KiB重定位的u-boot */
       sub    r0, r0, #CFG_MALLOC_LEN        /* 向下是内存分配空间 */
       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        /* 为abort-stack预留3个字 */
clear_bss:
       ldr    r0, _bss_start        /* 找到bss段起始地址 */
       ldr    r1, _bss_end        /*  bss段末尾地址   */
       mov     r2, #0x00000000        /* 清零 */
clbss_l:str    r2, [r0]        /* bss段地址空间清零循环...  */
       add    r0, r0, #4
       cmp    r0, r1
       bne    clbss_l
       /* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */
       ldr    pc, _start_armboot
_start_armboot:    .word start_armboot        //start_armboot函数在lib_arm/board.c中实现
/* 关键的初始化子程序 */
cpu_init_crit:
……     //初始化CACHE,关闭MMU等操作指令
       /* 初始化RAM时钟。
       * 因为内存时钟是依赖开发板硬件的,所以在board的相应目录下可以找到memsetup.S文件。
       */
       mov    ip, lr
       bl    memsetup        //memsetup子程序在board/smdk2410/memsetup.S中实现
       mov    lr, ip
       mov    pc, lr

2.lib_arm/board.c
   start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。


void start_armboot (void)
{
       DECLARE_GLOBAL_DATA_PTR;
       ulong size;
       init_fnc_t **init_fnc_ptr;
       char *s;
       /* Pointer is writable since we allocated a register for it */
       gd = (gd_t*)(_armboot_start - CFG_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));
       monitor_flash_len = _bss_start - _armboot_start;
       /* 顺序执行init_sequence数组中的初始化函数 */
       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
              if ((*init_fnc_ptr)() != 0) {
                      hang ();
              }
       }
       /*配置可用的Flash */
       size = flash_init ();
       display_flash_config (size);
       /* _armboot_start 在u-boot.lds链接脚本中定义 */
       mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
       /* 配置环境变量,重新定位 */
       env_relocate ();
       /* 从环境变量中获取IP地址 */
       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
       /* 以太网接口MAC 地址 */
       ……
       devices_init ();        /* 获取列表中的设备 */
       jumptable_init ();
       console_init_r ();     /* 完整地初始化控制台设备 */
       enable_interrupts ();    /* 使能例外处理 */
       /* 通过环境变量初始化 */
       if ((s = getenv ("loadaddr")) != NULL) {
               load_addr = simple_strtoul (s, NULL, 16);
       }
       /* main_loop()总是试图自动启动,循环不断执行 */
       for (;;) {
               main_loop ();         /* 主循环函数处理执行用户命令 -- common/main.c */
       }
       /* NOTREACHED - no way out of command loop except booting */
}

3.init_sequence[]
   init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。

init_fnc_t *init_sequence[] = {
       cpu_init,                 /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */
       board_init,             /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */
       interrupt_init,         /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
       env_init,                 /* 初始化环境变量 -- common/cmd_flash.c */
       init_baudrate,         /* 初始化波特率设置 -- lib_arm/board.c */
       serial_init,             /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
       console_init_f,         /* 控制台初始化阶段1 -- common/console.c */
       display_banner,         /* 打印u-boot信息 -- lib_arm/board.c */
       dram_init,             /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
       display_dram_config,      /* 显示RAM的配置大小 -- lib_arm/board.c */
       NULL,
};

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值