####################
本文由极度寒冰原创,转载请注明出处。
####################
// start_armboot()函数主要初始化ARM系统的硬件和环境变量,包括FLASH存储器,FrameBuffer,网卡等,最后进入U-Boot应用程序主循环。
// 上一篇文章中,我们可以看到的是start_armboot是U-Boot启动过程中经历的第一个C语言的函数。 函数所在的文件为: lib_arm/board.c
typedef int (init_fnc_t) (void);
// 首先在这里定义了一个新的数据类型init_fnc_t, 这个数据类型的返回值为int,无参数。
int print_cpuinfo (void); /* test-only */
// init_sequence是一个指针数组,指向的是init_fnc_t类型的函数。 里面的每一个元素都是一个指针,指向的都是init_fnc_t类型的函数。。
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
// cpu架构方面的相关函数。
board_init, /* basic board dependent setup */
// 板极的初始化函数。 所在的位置为board下面的各个文件夹中。
interrupt_init, /* set up exceptions */
// 初始化中断。 所在文件的位置为./cpu/下面不同的处理器的interrupts.c中,是否所有的处理器都支持初始化中断,这点尚未研究。
env_init, /* initialize environment */
// 在./common/env_nvram.c中进行的实现。
init_baudrate, /* initialze baudrate settings */
// 初始化波特率的设置。
serial_init, /* serial communications setup */
// 串口通信的设置。一般都位于各个cpu,board的serial.c中。
console_init_f, /* stage 1 init of console */
// 控制台前期的初始化。具体实现位于 common/console.c 中
display_banner, /* say that we are here */
// 主要用来显示uboot的版本信息和编译时间。
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
//cat /proc/cpu_info可以显示cpu的信息。
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
// 显示板子的信息。 是否所有的板子都支持这个选项,现在还没有一一对比。
#endif
dram_init, /* configure available RAM banks */
// 这个函数是检查内存映射,即确定了板子上面使用了多少内存,它们的地址是什么。
display_dram_config,
// 打印出DRAM的大小。
NULL,
};
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
// init_fnc_ptr是一个指针,这个指针指向了一个指针数组,指针数组里面每一个元素都是一个指针,指向的是init_fnc_t类型的函数。。。
char *s;
#ifndef CFG_NO_FLASH
// Flash is usable now or not
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
// send framebuffer setup or not || use LCD controller or not ...
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
// 把gd作为全局数据结构体的指针,并给它安排空间。
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
//__asm__ 用于告诉编译器,在这个地方插入一段汇编语言。 __volatile__用于告诉编译器,严禁将此处的汇编语言与其他的语句重优化组合。即:原原本本按照原来的样子来处理这一段汇编。memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的register和cache中已经被缓存的内存单元中的数据将被作废。cpu将不得不在需要的时候重新读取内存中的数据。这样就阻止了cpu又将registers,cache中的数据去优化指令,而避免去访问内存。 ""::: 表示这个是个空指令。
memset ((void*)gd, 0, sizeof (gd_t));
// 将gd给清零 数组的初始化一样
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
// 给板子的数据变量gd->bd安排空间。
memset (gd->bd, 0, sizeof (bd_t));
// 数组的初始化。
monitor_flash_len = _bss_start - _armboot_start;
// 计算u-boot的长度。
// 顺序执行init_sequence数组中的初始化函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
// 初始化Flash存储器配置
display_flash_config (size);
// 显示Flash存储器配置
#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD
// do not send framebuffer setup
#
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);
// 计算Framebuffer内存地址
size = vfd_setmem (addr);
// 设置Framebuffer占用内存大小
gd->fb_base = addr;
// 设置Framebuffer内存起始地址
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
// us
e LCD controller or not ...
#
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);
// 计算FrameBuffer内存地址。
size = lcd_setmem (addr);
// 设置FrameBuffer大小。
gd->fb_base = addr;
// 设置FrameBuffer内存起始地址。
#endif /* CONFIG_LCD */
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
//初始化堆空间
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init();
/* go init the NAND */
// 初始化NAND Flash存储器
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
// 初始化hash表
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 */
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
// 设置网卡的IP地址, 从环境变量中获得IP地址。
/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
// 从网卡寄存器中读取MAC地址。
s = (i > 0) ? tmp : NULL; // 将mac地址保存到tmp中
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
// 为什么会小于6?这个是从哪儿来的?我认为是与tmp[64]对应的。2的6次方,这样的话方便对每一位进行操作。
if (s)
s = (*e) ? e + 1 : e;
}
#ifdef CONFIG_HAS_ETH1
// add support for "eth1addr"
i = getenv_r ("eth1addr", tmp, sizeof (tmp));
// 读取hash值
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
// 如果有第二块网卡的话,并且这个网卡是被声明可以使用的情况下,进入这一个选项。
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#endif
}
devices_init ();
/* get the devices list going. */
// 初始化开发板上面的设备。
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
jumptable_init ();
// 初始化跳转表,主要是保存了一些系统函数的指针。便于以后的引用~ 函数的具体实现在./common/exports.c
console_init_r ();
/* fully init console as a device */
// 在这个地方,对控制台进行了全面的初始化。函数的具体实现在./common/console.c
#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_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
// 获取CS8900网卡的MAC地址
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
// 设置SMC网卡的MAC地址
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
// 保存FrameBuffer
}
#endif
/* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT
board_late_init ();
// 开发板相关设备的初始化
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
// 设置网卡
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
// 进入主循环! 终于进入主循环了。。。。。
}
/* NOTREACHED - no way out of command loop except booting */
}