uboot启动第二阶段要做的事情:
宏观上来讲,就是要初始化剩下的还未被初始化的硬件,主要是SoC外部硬件(网卡,iNand等)和uboot本身的一些东西(uboot命令,环境变量等).
uboot在何处完结:
下面开始uboot第二阶段详解:
uboot第一阶段完成后会自动跳转到start_armboot函数:
其中难以理解的:
init_fnc_t **init_fnc_ptr;
这个二重函数指针在另一篇文章里分析过了
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
定义了一个全局变量gd,这个全局变量是指针类型,指向gd_t类型
(1) gd_t定义在include/asm-arm/global_data.h中,是一个结构体
(2)gd_t中定义了很多全局变量,都是整个uboot使用的;其中有一个bd_t类型的指针,指向一个bd_t类型的变量,这个bd是开发板的板级信息的结构体,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存分布。
volatile修饰表示可变,register修饰表示优先放在寄存器中
后面的asm(“r8”)是gcc支持的一种语法,意思就是要把gd放到寄存器r8中
下面进行了内存分配:
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base; #gd在ddr中的基地址
#计算gd_base
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
gd = (gd_t*)gd_base; #
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); #为了防止高版本的gcc的优化造成错误
#清理内存
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
分配内存的原因:
(1)DECLARE_GLOBAL_DATA_PTR只能定义了一个指针,也就是说gd里的这些全局变量并没有被分配内存,我们在使用gd之前要给他分配内存,否则gd也只是一个野指针而已。
(2)gd和bd需要内存,内存当前没有被人管理(因为没有操作系统统一管理内存),大片的DDR内存散放着可以随意使用(只要使用内存地址直接去访问内存即可)。但是因为uboot中后续很多操作还需要大片的连着内存块,因此这里使用内存要本着够用就好,紧凑排布的原则。所以我们在uboot中需要有一个整体规划。
内存排布:
(1)uboot区 CFG_UBOOT_BASE-xx(长度为uboot的实际长度)
(2)堆区 长度为CFG_MALLOC_LEN,实际为912KB
(3)栈区 长度为CFG_STACK_SIZE,实际为512KB
(4)gd 长度为sizeof(gd_t),实际36字节
(5)bd 长度为sizeof(bd_t),实际为44字节左右
(6)内存间隔 为了防止高版本的gcc的优化造成错误。
计算出不同区的大概大小,各种宏定义在x210_sd.h中
3.进入
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
接下来介绍init_sequence中各种硬件的初始化函数:
1.cpu_init
cpu内部的初始化,第一阶段已经做过了,所以这里为空
2.board_init
开发板相关的初始化
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init(); #网卡初始化
#endif
gd->bd->bi_arch_number = MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
return 0;
}
3.interrupt_init
这个函数是用来初始化定时器Timer4(PWM定时器)的,这个定时器没有中断功能,CPU通过轮询方式查看TCNTO寄存器来确定时间到没到,设置方法和裸机差不多:通过get_PCLK函数得到PCLK_PSYS时钟频率,设置TCFG0,TCFG1分频,然后计算出设置10ms需要的值写入TCNTB,然后设置为autoload模式,最后开始计时。
4.env_init
环境变量的初始化,该函数只是对内存里那一份uboot的env做了判断是否存在,因为当前还没把环境变量从SD卡重定位到DDR,所以环境变量还不用
5.init_baudrate
波特率初始化,
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp));
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10)
: CONFIG_BAUDRATE;
return (0);
}
6.serial_init
串口初始化,第一阶段做过了,这里啥也没干
7.console_init_f
8.display_banner
9.printf_cpuinfo
打印CPU信息,通过各种软件计算得到的,和裸机时钟计算差不多
10.check_board
打印开发板信息
11.init_func_i2c
这个函数没有执行,因为 x210的uboot没有使用IIC,如果外接,可以通过开启宏来实现
12.dram_init
DDR初始化,在第一阶段已经初始化过了,这里是给gd->bd里面的关于DDR的全局变量赋值,让gd->bd记录当前开发板的DDR配置信息,以便UBOOT中使用内存,时间上就是初始化gd->bd->bi_dram这个结构体数组
13.display_dram_config
打印dram的配置信息
另:运行uboot时通过bdinfo命令可以打印出gd->bd中关于所有硬件的全局变量值
至此,init_sequence中所有函数结束,这里面的函数都是板级硬件的初始化以及gd ,gd->bd中数据结构的初始化
接下来返回start_armboot 487行继续:
#ifndef CFG_NO_FLASH #x210没有norflash,这两行只是打印了信息,并没有什么用
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
因为x210没有norflash,这个没用
#ifdef CONFIG_VFD
。。。
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
。。。
#endif /* CONFIG_LCD */
这个是uboot中自带的LCD显示的软件架构。但是实际上我们用LCD而没有使用uboot中设置的这套软件架构,后面添加了一个LCD显示的部分
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
DDR中预留了内存给堆使用,这套代码来管理这个堆内存。有了这些东西uboot中就可以用malloc、free这套机制来申请内存和释放内存
开发板独有的初始化,从536—768行都是相关开发板的mmc初始化,通过宏定义选取自己开发板相关的部分
#if defined(CONFIG_X210)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init();
#endif
#endif /* CONFIG_X210 */
初始化soc内部的sd/mmc控制器。
/* initialize environment */
env_relocate ();
devices_init (); /* get the devices list going. */
作用未知
#if !defined(CONFIG_SMDK6442)
console_init_r (); /* fully init console as a device */
/* enable exceptions */
enable_interrupts ();
这两个环境变量是内核启动相关的,启动内核时会参考这两个的值
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
最后没有初始化的且必须初始化的硬件设备都在这里做,对于x210来说这里为空
eth_initialize(gd->bd);
(1)网卡相关的初始化。这里不是SoC与网卡芯片连接时SoC这边的初始化,而是网卡芯片本身的一些初始化。
(2)对于X210(DM9000)来说,这个函数是空的。X210的网卡初始化在board_init函数中,网卡芯片的初始化在驱动中
#ifdef CONFIG_MPAD
extern int x210_preboot_init(void);
x210_preboot_init();
#endif
x210开发板启动起来之前的一些初始化,以及LCD屏幕上的LOGO显示
/* check menukey to update from sd */
extern void update_all(void);
if(check_menu_update_from_sd()==0)//update mode
{
puts ("[LEFT DOWN] update mode\n");
run_command("fdisk -c 0",0);
update_all();
}
else
puts ("[LEFT UP] boot mode\n");
for (;;) {
main_loop ();
}
init_sequence
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印DDR配置信息表
mem_malloc_init 初始化uboot自己维护的堆管理器的内存
mmc_initialize inand/SD卡的SoC控制器和卡的初始化
env_relocate 环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
devices_init 空的
jumptable_init 不用关注的
console_init_r 真正的控制台初始化
enable_interrupts 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 空的
eth_initialize 空的
x210_preboot_init LCD初始化和显示logo
check_menu_update_from_sd 检查自动更新
main_loop 主循环