typedef int (init_fnc_t) (void) 函数类型,
init_fnc_t **init_fnc_ptr; 二重函数指针, 二重指针的作用有两个:一个是用来指向一重指针,一个是用来指向指针数组,因此这里的init_fnc_ptr可以用来指向一个函数指针数组。
(1)定义了一个宏,定义了指针类型全局变量,这个全局变量指针是一个结构体,这个结构体存放着用到的全局变量,包括硬件相关的参数。
(2)#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)
(3)定义了一个全局变量名字叫gd,指针类型,register修饰表示这个变量要尽量放到寄存器中,volatile可变的易变的。==asm (“r8”)==是gcc支持的一种语法,意思就是要把gd放到寄存器r8中。 gd (global data简称)uboot中的很重要的全局变量(这个全局变量是一个结构体,里面有很多内容,这个结构体就是uboot中常用的所有的全局变量的集合)
(4)这些变量由于经常使用,所以放在寄存器中提高运行效率。
unsigned long baudrate; 波特率
unsigned long env_valid; /* Checksum of Environment valid? * 内存中的环境变量是否可用 bool变量 只用了1位 0或1
(1)ulong bi_boot_params; /* where this board expects params */ 启动参数地址
(2)bi_dram[CONFIG_NR_DRAM_BANKS]; 保存DDR中的信息 结构体数组
(1) DECLARE_GLOBAL_DATA_PTR 只是定义了一个指针,gd里的全局变量并没有分配内存,在使用gd之前需要对其进行分配内存,否则gd也只是一个野指针而已。
(2)分配内存不能够malloc,因为现在是裸机程序,如果在操作系统可以这样申请。裸机只能使用功能内存。
(3)gd和bd需要内存,目前内存还没有被管理,大片DDR内存可以随意使用。但也要只有自己合理使用。保证安全,紧凑排布。
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
整体规划内存
(1)uboot区 CFG_UBOOT_BASE (0x33e00000)~200k左右
(2)堆区:CFG_MALLOC_LEN 16k+896k
#define CFG_MALLOC_LEN (CFG_ENV_SIZE(16k) + 8961024) =16k+896k
(3)栈区:CFG_STACK_SIZE
#define CFG_STACK_SIZE 5121024 = 512k
(4)gd sizeof(gd_t)
(5)bd sizeof(bd_t)
综合分析得到内存的分配图:
给bd分配空间 实例化bd空间=
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
内嵌汇编
/* compiler optimization barrier needed for GCC >= 3.4 */
asm volatile("": : :“memory”); 这段代目的是为了防止高版本的gcc的过度优化造成错误。
483行:
//init_fnc_ptr这个是一个二重指针,就指向一个函数指针数组
//*init_fnc_ptr 解引用后就得到一个函数指针,相等于*init_fnc_ptr!=NULL 这循环,等于这停止循环
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) { //函数正确时返回0
hang (); //函数返回不等于0则执行 挂起,这个启动失败。
}
}
//这个循环是在遍历这些函数指针数组 直到遍历到NULL位置为止,
void hang (void)
{
puts ("### ERROR ### Please RESET the board ###\n"); //此时任何初始化程序没有完成均调到这个函数,成为死循环,启动失败
for (;;);
}
int cpu_init (void)
{
/*
* setup up stacks if necessary
*/
#ifdef CONFIG_USE_IRQ //这个是空的没有,所以这个函数没有执行
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR; //在这个声明,为了gd使用方便
#ifdef CONFIG_DRIVER_SMC911X
smc9115_pre_init();
#endif
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init(); //网卡初始化 GPIO和端口的配置,驱动是不需要动的。
#endif
//开发板的机器码,uboot给开发板定义的唯一编号。和linxu内核之间进行比对和配置
gd->bd->bi_arch_number = MACH_TYPE;
//bd_info中另一个主要元素,uboot通过这里赋值地址,告诉linux传的参数放在内存的哪里,以便内核启动后到这个内存地址去取参数即可。
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
return 0;
}
== MACH_TYPE==; //关于开发板的机器码(uboot给的),需要与linux中的编码一致,否则当linux启动后检查自己的编码和通过uboot传参方式过来的机器码不一致,则无法启动内核。
背景知识:
这里初始化DDR,和汇编阶段中lowlevel int 中不同的,当时是硬件的初始化,现在是软件结构中的初始化一些DDR相关的属性,地址设置的初始化,是纯软件层面的
(1)软件层次初始化DDR的缘由:开发板不知道有几片DDR内存,每片的起始地址和长度信息。所以程序员在移植开发板时需要在x210_sd.h中用宏定义去配置出来扳子的信息,然后uboot读取即可。 还有一种思路是:uboot直接通过代码读取 硬件信息来指定DDR配置,但是uboot不这样,如PC的BIOS就是采用这种方式。
(2)如图相关信息的配置:
int interrupt_init(void)
(1)定时器的第一次时间是由时钟决定的也就是2级时钟分频器决定,在TCFG0,和TCFG1中指定,本函数只设定TCFG0,TCFG1用默认值。
(2)使用Timer4 来定时,本定时器不支持中断,也就是cpu不能做其他事情的同时定时,cpu只能使用轮询方式来不断查询看TCNTO寄存器才能知道定时时间是否到达。uboot是用Timer4来定时的,在内核中是不能用Timer4的(因为定时时还要做其他事情)。
这里TCFG1 没有设置,用默认分频1/1,TCNTB需要定的时间数值
(3)设置定时时间只要对TCNTB4进行设置即可
int env_init(void) 本函数只是用来基本初始化和判断环境变量有没有用,当前还没有进行环境变量从SD卡到DDR中的relocate,因此当前的环境变量不可以用。
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp)); //读取到的波特率存放在tmp中,是一个字符串
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10) //字符串转换为数字格式的波特率
: CONFIG_BAUDRATE; //如果没有baudrate 这个环境变量则 读取这个宏的值
return (0);
}
(1)在start.S中的lowlevel_init已经初始化过来,这里进来后发现什么都没有做,只做了延时和一个声明。
void serial_setbrg(void)
{
DECLARE_GLOBAL_DATA_PTR; // #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
int i;
for (i = 0; i < 100; i++);
}
/*
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*
*/
int serial_init(void)
{
serial_setbrg();
return (0);
}
后面_f是第一次初始化,_r表示第二阶段初始化
本结构体中不涉及第二阶段的初始化
为什么还没有初始化控制台完成,怎么可以打印信息?
在这里根本不是通过控制台打印信息的,是直接通过硬件串口打印的。通过控制台发送,最终也会映射到硬件串口打印信息的,控制台可以对信息进一步的加工处理,有缓冲的作用,cpu发送信息的时候,不是马上就通过串口把信息打印处理,我们可能不能立马看到信息,而是被缓冲起来,达到一定的量的时候才一起打印处理,这样可以减轻硬件的负担。
下面是包含了版本信息的头文件,这个头文件是编译时自动生成的。
printf("\nCPU: S5PV210@%ldMHz(%s)\n", get_ARMCLK()/1000000, ((result_set == 1) ? "OK" : "FAIL"));
printf("APLL = %ldMHz, HclkMsys = %ldMHz, PclkMsys = %ldMHz\n",get_FCLK()/1000000, get_HCLK()/1000000, get_PCLK()/1000000);
printf("MPLL = %ldMHz, EPLL = %ldMHz\n",get_MPLL_CLK()/1000000, get_PLLCLK(EPLL)/1000000);
printf("HclkDsys = %ldMHz, PclkDsys = %ldMHz\n",get_HCLKD()/1000000, get_PCLKD()/1000000);
printf("HclkPsys = %ldMHz, PclkPsys = %ldMHz\n",get_HCLKP()/1000000, get_PCLKP()/1000000);
printf("SCLKA2M = %ldMHz\n", get_SCLKA2M()/1000000);
static ulong get_PLLCLK(int pllreg)
{
ulong r, m, p, s;
if (pllreg == APLL) {
r = APLL_CON0_REG;
m = (r>>16) & 0x3ff; m =
} else if (pllreg == MPLL) {
r = MPLL_CON_REG;
m = (r>>16) & 0x3ff;
} else if (pllreg == EPLL) {
r = EPLL_CON_REG;
m = (r>>16) & 0x1ff;
} else
hang();
p = (r>>8) & 0x3f;
s = r & 0x7;
if (pllreg == APLL)
s= s-1;
//读取到M=125,P=3,S=1 ,CONFIG_SYS_CLK_FREQ=24MHZ
return (m * (CONFIG_SYS_CLK_FREQ / (p * (1 << s)))); 125*(24000000/3*1)=1000000000=1000Mhz
}
#elif defined(CONFIG_CLK_1000_200_166_133)
#define APLL_MDIV 0x7d //125
#define APLL_PDIV 0x3 //3
#define APLL_SDIV 0x1 //1
int checkboard(void)
{
#ifdef CONFIG_MCP_SINGLE
#if defined(CONFIG_VOGUES)
printf("\nBoard: VOGUESV210\n");
#else
printf("\nBoard: X210\n"); //这句被执行
#endif //CONFIG_VOGUES
#else
printf("\nBoard: X210\n");
#endif
return (0);
}
貌似这个函数被执行了,查看宏发现,这个宏被条件变量,不成立则没有创建CONFIG_HARD_I2C这个宏,不被执行
(1)Makefile中修改版本号
uboot根目录下,三星公司添加的sd_fusing目录主要用于烧入uboot到SD卡中
注意要重新编译下这个工具再使用,不然出错
(2)进入sd_fusing----->make clean---->make(重新编译下本目录)
(3)执行:./sd_fusing.sh /dev/sdb 烧入到SD卡
int dram_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
//x210有2片内存,0和1
gd->bd->bi_dram[0].start = PHYS_SDRAM_1; //第一片内存的起始地址
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; //第一片内存的大小
#if defined(PHYS_SDRAM_2)
gd->bd->bi_dram[1].start = PHYS_SDRAM_2; //第二片内存的起始地址
gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE; //第二片内存的大小
#endif
#if defined(PHYS_SDRAM_3)
gd->bd->bi_dram[2].start = PHYS_SDRAM_3;
gd->bd->bi_dram[2].size = PHYS_SDRAM_3_SIZE;
#endif
return 0;
}
static int display_dram_config (void)
{
int i;
#ifdef DEBUG
puts ("RAM Configuration:\n");
for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
print_size (gd->bd->bi_dram[i].size, "\n");
}
#else
ulong size = 0;
for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
size += gd->bd->bi_dram[i].size; //计算两片内存的的大小
}
puts("DRAM: ");
print_size(size, "\n"); //打印内存大小值
#endif
return (0);
}
intit_sequence中:都是板级硬件的初始化已经gd gd->bd中结构体的初始化。
网卡初始化,机器码初始化,内核参数DDR地址初始化,定时器初始化,波特率设置,console第一阶段初始化。
打印uboot的启动信息,打印cpu相关的设置信息,检查并打印开发板名字,ddr配置信息的初始化(gd->bd->bi_dram)\打印DDR总容量。
一般写Flash指的是Norflash,Nand是NandFlash的简称。
norflash相关的 norflash 初始化信息,和显示
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size); //Flash : 8MB x210中没有norflash的。 按理说这两行代码可以去掉,但是会报错可能其他地方引用到了,不删除对程序没有影响。但删除就可能报错,所以移植的工程师就干脆不删除了。
#endif /* CFG_NO_FLASH */
uboot自带的LCD显示架构,x210不使用这里的架构。
(1)用来初始化uboot的堆管理器。uboot中自己维护了一段堆内存,有了这些东西uboot中就可以用malloc,free来申请和释放内存。
/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
static void mem_malloc_init (ulong dest_addr)
{
mem_malloc_start = dest_addr;
mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
mem_malloc_brk = mem_malloc_start;
memset ((void *) mem_malloc_start, 0,
mem_malloc_end - mem_malloc_start); /清零堆内存
}
初始化soc内部的SD/MMC控制器
#if defined(CONFIG_X210)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd); //初始化SD卡控制器 读取大小
if (mmc_exist != 0)
{
puts ("0 MB\n");
#ifdef CONFIG_CHECK_X210CV3
check_flash_flag=0;//check inand error!
#endif
}
#ifdef CONFIG_CHECK_X210CV3
else
{
check_flash_flag=1;//check inand ok!
}
#endif
#endif
#if defined(CONFIG_MTD_ONENAND)
puts("OneNAND: ");
onenand_init();
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
#else
//puts("OneNAND: (FSR layer enabled)\n");
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init();
#endif
#endif /* CONFIG_X210 */
mmc_initialize(gd->bd);
INIT_LIST_HEAD(&mmc_devices); 初始化链表
int cpu_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMC
setup_hsmmc_clock(); //时钟初始化
setup_hsmmc_cfg_gpio();
return smdk_s3c_hsmmc_init(); //底层驱动初始化,驱动中的分层思想很重要
#else
return 0;
#endif
}
(1)环境变量到底从哪里来?sd卡中有一些8个独立的扇区作为环境变量的存储区域。系统部署时,但是我们烧入镜像是没有烧入环境变量,所以第一次启动系统时,uboot尝试去SD卡env分区读取环境变量时失败(CRC校验失败),uboot内部代码中设置的一套默认的环境变量出发来使用(硬编码),读取后写入SD卡env分区。
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);
#ifdef CONFIG_AMIGAONEG3SE
enable_nvram();
#endif
#ifdef ENV_IS_EMBEDDED //没有被定义的
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
show_boot_progress (-60);
#endif
set_default_env();
}
else {
env_relocate_spec (); //重定位 SD卡到DDR中
}
gd->env_addr = (ulong)&(env_ptr->data);
#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}
gd->bd->bi_ip_addr = getenv_IPaddr (“ipaddr”);
/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
放在这里初始化的设备都是驱动设备,uboot中这个函数里面的驱动都没有用到
/* get the devices list going. */
{
#ifndef CONFIG_ARM /* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif
/* Initialize the list */
devlist = ListCreate (sizeof (device_t));
if (devlist == NULL) {
eputs ("Cannot initialize the list of devices!\n");
return -1;
}
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD
drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER
drv_logbuff_init ();
#endif
drv_system_init ();
#ifdef CONFIG_SERIAL_MULTI
serial_devices_init ();
#endif
#ifdef CONFIG_USB_TTY
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE
drv_nc_init ();
#endif
return (0);
}
只是赋值,做了左值,没有被使用,不太重要
本身就是函数指针数组,记录了很多函数的函数名。这些都是工具函数。
void jumptable_init (void)
{
int i;
gd->jt = (void **) malloc (XF_MAX * sizeof (void *));
for (i = 0; i < XF_MAX; i++)
gd->jt[i] = (void *) dummy;
gd->jt[XF_get_version] = (void *) get_version;
gd->jt[XF_malloc] = (void *) malloc;
gd->jt[XF_free] = (void *) free;
gd->jt[XF_getenv] = (void *) getenv;
gd->jt[XF_setenv] = (void *) setenv;
gd->jt[XF_get_timer] = (void *) get_timer;
gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;
gd->jt[XF_udelay] = (void *) udelay;
gd->jt[XF_simple_strtol] = (void *) simple_strtol;
gd->jt[XF_strcmp] = (void *) strcmp;
#if defined(CONFIG_I386) || defined(CONFIG_PPC)
gd->jt[XF_install_hdlr] = (void *) irq_install_handler;
gd->jt[XF_free_hdlr] = (void *) irq_free_handler;
#endif /* I386 || PPC */
#if defined(CONFIG_CMD_I2C)
gd->jt[XF_i2c_write] = (void *) i2c_write;
gd->jt[XF_i2c_read] = (void *) i2c_read;
#endif
}
(1)uboot有很多同名函数,需要看清楚是那个。
(2)纯软件相关架构方面的初始化(给console相关的数据结构中填充相应的值)
(3)uboot的console实际上并没有干有意义的转化,也是直接调用串口通信的函数,用不用也没有什么差别。(linux内核中console可以起到缓冲机制)
int console_init_r (void)
{
device_t *inputdev = NULL, *outputdev = NULL;
int i, items = ListNumItems (devlist);
#ifdef CONFIG_SPLASH_SCREEN
/* suppress all output if splash screen is enabled and we have
a bmp to display */
if (getenv("splashimage") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif
/* Scan devices looking for input and output devices */
for (i = 1;
(i <= items) && ((inputdev == NULL) || (outputdev == NULL));
i++
) {
device_t *dev = ListGetPtrToItem (devlist, i);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
inputdev = dev;
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
outputdev = dev;
}
}
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile (stdout, outputdev);
console_setfile (stderr, outputdev);
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile (stdin, inputdev);
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
#ifndef CFG_CONSOLE_INFO_QUIET //没有被定义,下面被执行
/* Print information */
puts ("In: ");
if (stdio_devices[stdin] == NULL) {
puts ("No input devices available!\n");
} else {
printf ("%s\n", stdio_devices[stdin]->name);
}
puts ("Out: ");
if (stdio_devices[stdout] == NULL) {
puts ("No output devices available!\n");
} else {
printf ("%s\n", stdio_devices[stdout]->name);
}
puts ("Err: ");
if (stdio_devices[stderr] == NULL) {
puts ("No error devices available!\n");
} else {
printf ("%s\n", stdio_devices[stderr]->name);
}
#endif /* CFG_CONSOLE_INFO_QUIET */
#ifndef CONFIG_X210
/* Setting environment variables */
for (i = 0; i < 3; i++) {
setenv (stdio_names[i], stdio_devices[i]->name);
}
#endif
#if 0
/* If nothing usable installed, use only the initial console */
if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
return (0);
#endif
return (0);
}
(1)指的是CPSR中总中断标志位的使能
(2)uboot中没有使用中断, 给了一个空函数
void enable_interrupts (void)
{
return;
}
两个环境变量是内核启动有关的暂时不用了解。
实际是空的函数,就是开发板级别的晚期初始化,前面已经初始化过来,后面的初始化就在这里了。侧面说明开发板级别的硬件初始化已经基本完成了。
int board_late_init (void)
{
return 0;
}
(1)网卡本身的初始化,不是soc与网卡芯片连接时soc这边的初始化
int eth_initialize(bd_t *bis)
{
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_init();
#endif
...
}
void miiphy_init ()
{
INIT_LIST_HEAD (&mii_devs); //只初始化了一个链表
current_mii = NULL;
}
(1)开发板启动前的初始化
(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:要升级镜像放到sd卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(如果按下“LEFT”,则表示update mode,否则boot mode)。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用sd卡进行系统烧录部署。
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
(1)解析器
(2)开机倒数自动执行
(3)命令补全(uboot中没有实现)
(1)start_armboot 第二阶段入口
(2)开发版级别硬件初始化和软件初始化
(3)本阶段主要的初始化
函数 | 内嵌函数 | 作用 | 备注 |
---|---|---|---|
cpu_initcpu | cpu软件初始化 | 为空 | |
board_init | 板级初始化,网卡控制器初始化,设置开发板编号 | ||
interrupt_init | 定时器4初始化 | ||
env_init | 环境变量初始化,主要判断这些环境变量是否可用 | ||
init_baudrate | 波特率初始化,从环境变量中读取波特率到全局变量 | ||
serial_init | serial_setbrg | 串口初始化 | 为空,只做了延时 |
console_init_f | 控制台第一阶段 | 只完成了gd->have_console = 1; | |
display_banner | 显示版本信息 | ||
print_cpuinfo | 打印时钟相关参数 | ||
checkboard | 打印开发版信息 | ||
dram_init | 初始化DDR 设置内存的起始地址 | ||
display_dram_config | 计算并DDR容量 | ||
mem_malloc_init | 堆的初始化 | ||
env_relocate (); | 环境变量的重定位,把环境变量加载到DDR中 | ||
gd->bd->bi_ip_addr | gd结构体的赋值 | ||
gd->bd->bi_enetaddr[reg] | gd结构体的赋值 | ||
devices_init | 驱动的初始化 | uboot中没有用的这里的驱动 | |
jumptable_init | 跳转表 | ||
console_init_r | 控制台第二阶段初始化 | ||
enable_interrupts | 使能中断 | 这里为空 | |
board_late_init | 板级最后剩余的初始化 | ||
eth_initialize(gd->bd) | 网卡芯片初始化 | 没有做具体初始化 | |
x210_preboot_init | LCD初始化和logo显示 | ||
check_menu_update_from_sd | 自动更新镜像 | ||
main_loop | 进入uboot死循环,uboot的最终归宿 |
(1)x210_sd.h头文件中的宏定义
(2)特定硬件初始化函数的位置