嵌入式u-boot浅析

来源:互联网 发布:剃头软件叫什么 编辑:程序博客网 时间:2024/05/21 08:50
u-boot主要目的是为操作系统的运行提供准备工作,根据其运行流程简单的分为四部分:_start、board_init_f、relocate_code和board_init_r。其中_start和relocate_code是运行在flash上,而board_init_f和board_init_r是运行于DRAM上的。
下面对其四个部分进行简单的介绍分析:
1、_start
这是u-boot的起始部分,程序从/u-boot/cpu/mips/start.S文件中的_start代码段开始执行的。由于此时内存未初始化,所以该部分是运行于flash上的。其主要的内容有:
(1)    对cpu相关寄存器的初始化;
(2)    TLB(Translation Lookaside Buffer)初始化;
(3)    初始化一块临时内存作为栈空间scratch memory,为内存初始化之前调用c语言使用;
(4)    初始化全局符号表指针GOT pointer;

(5)    使用跳转命令跳转到board_init_f;

2、board_init_f

    该部分是在上面申请的临时内存空间中运行的,主要是内存的初始化及整个寻址空间的部分初始化。其主要是循环调用init_sequence函数指针数组中的成员,如下所示

init_fnc_t *init_sequence[] = {      octeon_boot_bus_init,  // GPIO使能、flash空间映射及大小设置等;      timer_init,      env_init,                    early_board_init,   //cpu主频、板子型号等初始化;      init_baudrate,  //串口频率配置;      serial_init, //串口初始化;      console_init_f,      display_banner,              init_dram,  //内存初始化;      checkboard,  //一些检测;      init_func_ram,      switch_init, //初始化switch的配置;      NULL,};

完成上述初始化后,将会申请1MB的内存空间用于存放u-boot代码,并初始化这一内存空间。然后跳转到relocate_code。
3、relocate_code
     该部分主要是将u-boot拷贝到上面申请的1MB内存空间中,然后跳转到board_init_r。
4、board_init_r
  该部分主要是完成后续环境的初始化,如堆空间的分配、串口初始化和网卡初始化等。到目前为止u-boot启动基本完成,已为操作系统运行准备了必要的环境。

5、调试
u-boot的调试方法有很多种,下面介绍两种方式:使用led灯(即点灯)、利用一个已调试完成的u-boot引导调试另一个u-boot等。
在调试过程中在串口未初始化时可以使用点灯的方式跟踪,如果串口已初始化而未输出调试信息,可以关注对串口的配置是否正确。


(1)点灯
所谓的点灯既是在串口设备未初始化之前,驱动led灯的闪烁来判断程序的执行流程来完成调试。使用点灯调试首先需要了解到cpu芯片和led的连接方式,以及所使用的寄存器等。根据pcb图可以得知cpu芯片和led是通过GPIO的进行连接的,再从cpu芯片厂商提供的datasheet查询操作GPIO的相关寄存器和方法。
点灯可以分为两种方式:一种是使用汇编指令进行点灯操作,具体的汇编指令依赖所使用的cpu架构,本文的cpu架构为MIPS;另一种是使用u-boot提供的相关C语言接口进行点灯操作。下面均以GPIO 10的操作分别进行简单介绍:
1)汇编指令点灯操作

#if 1        nop        dli     t0, 0x8001070000000850 /* GPIO 10 */        ld      t1, 0(t0)        or      t1, 1        sd      t1, 0(t0)        nop        ld      t1, 1        nopled:        ld      t1, 1        bne     zero, t1, led        nop#endif

2)C语言接口点灯操作

void gpio_set(int bit)//bit为GPIO号{                uint64_t val = read_csr(GPIO_BIT_CFGX(bit));     write_csr(GPIO_BIT_CFGX(bit), val | 1);     val = read_csr(GPIO_BOOT_ENA);     write_csr(GPIO_BOOT_ENA, val & ~(1<<bit));     write_csr(GPIO_TX_SET, 1 << bit);             }

(2)u-boot引导u-boot
使用u-boot调试u-boot的方式主要是通过已调试完成的u-boot去调试一个待调试的u-boot,本文使用已调试完成的first_uboot作为引导。调试的方式分为两种:
1)待first_uboot启动完成之后,使用其提供的命令(如go)跳转到待调试的u-boot;
2)在first_uboot刚启动时,通过控制汇编跳转指令跳转到待调试的u-boot。
上述两种方式都是为了在调试u-boot过程中减少将flash摘除到擦写器中擦写的次数,提高调试效率。
两者的调试方式虽然类似,但是使用的时期具有一定的区别。第一种方式通过go命令跳转说明first_uboot已经将硬件环境进行了相关的初始化,所以跳转执行时会将待调试的u-boot拷贝到内存中执行,这将导致待调试u-boot初始化硬件的过程不完整。所以建议第一种调试方式主要用于进行初步调试,查看一些不依赖硬件的相关参数。第二种方式可以通过控制跳转点的位置来确保safe_loader还未硬件初始化就跳转到待调试u-boot,这样就能较为完整的调试u-boot,过程中需要结合上述的点灯等操作。虽然这样跳转能确保大部分硬件得到初始化,但是仍然会有一定的问题,目前发现的问题是该cpu为双核,但是通过这样的引导之后加载linux内核未能将第二颗核启动。调试完成之后只使用该u-boot可以顺利将第二颗核启动。

1)go命令跳转
使用go命令跳转进行调试时需要确定已调试的u-boot是否支持该命令,以及待调试u-boot中的相关配置需要进行调整。本文使用已调试完成的first_uboot可以通过命令go跳转到待调试的u-boot进行调试。其中待调试的u-boot需要修改顶层Makefile中的指定其在flash的位置,本文改为0xbec30000。
从上图可以看到需要将first_uboot和u-boot烧录到flash中,先启动first_uboot,完成之后执行go命令跳转到待调试的u-boot中。在调试过程中需要打开u-boot中的相关调试开关,也可以在关键地方增加相应调试信息。

2)汇编指令跳转
根据pcb图并结合u-boot提供的启动汇编代码可以得出,可以使用设备上的RST键进行跳转。从如下截图中可以看出RST键是通过GPIO连接到CPU芯片的GPIO 5号脚。
 
据上面的分析可以设计的调试方式是在启动first_uboot时按RST键则跳转到调试的u-boot,否则继续执行。如下的汇编代码实现了这一调试需求,即在first_uboot刚加载时读取GPIO 5的值,用于判断执行分支。注意跳转指令所使用的地址,j指令为直接跳转,其跳转的地址范围是28位即最大跳转地址空间为256MB;如果跳转地址大过该范围,需要使用寄存器跳转方式,如jr   t1。

#define GPIO_BIT 5     //GPIO 引脚号#define GPIO_RX_DAT  0x8001070000000880 //GPIO状态寄存器dli     t0, GPIO_RX_DATld      t1, 0(t0)andi    t1, (0x1 << GPIO_BIT)bne     zero, t1, boot_contnopli      t1, 0xbfc30000  //跳转地址赋值;j      t1   // 跳转到指定地址,即调试u-boot的起始地址


原创粉丝点击