2.6.U-Boot源码分析2-启动第二阶段-U-Boot和系统移植第6部分视频课程笔记

1、开始uboot启动第二阶段

  • 函数目录:Board.c (f:\si_preject\uboot_jiuding\uboot\lib_arm):void start_armboot (void)
  • void start_armboot (void)
    整个函数构成了uboot启动的第二阶段,uboot的第二阶段就是要初始化剩下的还没有初始化的硬件,主要soc外部硬件,譬如:Inand 网卡芯片,,uboot本身的一些命令,环境变量等。然后最终初始化完必要的东西后进入uboot的命令行准备接受命令。
    (1)第二阶段执行:开始初始化soc外设硬件,打印相关信息,boot进入了倒数bootdelay秒然后执行bootcmd对应的启动命令----如无用户干预—启动加载内核程序—uboot结束自己的使命。
    (2)如果uboot启动后倒计时结束前被用户干预,uboot就进入命令行的死循环,循环体内不断重复:接收命令、解析命令、执行命令。这就是uboot的最终归宿
    在这里插入图片描述

2、start_armboot解析1

typedef int (init_fnc_t) (void) 函数类型,
init_fnc_t **init_fnc_ptr; 二重函数指针, 二重指针的作用有两个:一个是用来指向一重指针,一个是用来指向指针数组,因此这里的init_fnc_ptr可以用来指向一个函数指针数组。

2.1、DECLARE_GLOBAL_DATA_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)这些变量由于经常使用,所以放在寄存器中提高运行效率。

2.2、gd_t结构体定义

在这里插入图片描述
unsigned long baudrate; 波特率
unsigned long env_valid; /* Checksum of Environment valid? * 内存中的环境变量是否可用 bool变量 只用了1位 0或1

2.3、bd_t开发板信息相关

在这里插入图片描述
(1)ulong bi_boot_params; /* where this board expects params */ 启动参数地址
(2)bi_dram[CONFIG_NR_DRAM_BANKS]; 保存DDR中的信息 结构体数组

2.4、内存使用排布

(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 512
1024 = 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 (;;);
}

在这里插入图片描述
在这里插入图片描述

2.5、开发板级别的初始化

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)如图相关信息的配置:
在这里插入图片描述

2.6、定时器的 TCNTB4 初始化

int interrupt_init(void)

(1)定时器的第一次时间是由时钟决定的也就是2级时钟分频器决定,在TCFG0,和TCFG1中指定,本函数只设定TCFG0,TCFG1用默认值。
(2)使用Timer4 来定时,本定时器不支持中断,也就是cpu不能做其他事情的同时定时,cpu只能使用轮询方式来不断查询看TCNTO寄存器才能知道定时时间是否到达。uboot是用Timer4来定时的,在内核中是不能用Timer4的(因为定时时还要做其他事情)。
这里TCFG1 没有设置,用默认分频1/1,TCNTB需要定的时间数值
(3)设置定时时间只要对TCNTB4进行设置即可
在这里插入图片描述

2.7、env_init 环境变量基本初始化

在这里插入图片描述
int env_init(void) 本函数只是用来基本初始化和判断环境变量有没有用,当前还没有进行环境变量从SD卡到DDR中的relocate,因此当前的环境变量不可以用。

2.8、加载环境变量到内存中

在这里插入图片描述

2.9、设置串口波特率

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);
}

2.10、初始化串口通信

(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);
}

2.11、console_init_f 控制台第一阶段初始化

后面_f是第一次初始化,_r表示第二阶段初始化
在这里插入图片描述
本结构体中不涉及第二阶段的初始化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为什么还没有初始化控制台完成,怎么可以打印信息?
在这里根本不是通过控制台打印信息的,是直接通过硬件串口打印的。通过控制台发送,最终也会映射到硬件串口打印信息的,控制台可以对信息进一步的加工处理,有缓冲的作用,cpu发送信息的时候,不是马上就通过串口把信息打印处理,我们可能不能立马看到信息,而是被缓冲起来,达到一定的量的时候才一起打印处理,这样可以减轻硬件的负担。
在这里插入图片描述

下面是包含了版本信息的头文件,这个头文件是编译时自动生成的。
在这里插入图片描述
在这里插入图片描述

2.12、print_cpuinfo display cpu info (and speed)

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

在这里插入图片描述

2.13、checkboard, /* display board info */

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);
}

2.14、init_func_i2c

貌似这个函数被执行了,查看宏发现,这个宏被条件变量,不成立则没有创建CONFIG_HARD_I2C这个宏,不被执行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.15、uboot烧入实验

(1)Makefile中修改版本号
uboot根目录下,三星公司添加的sd_fusing目录主要用于烧入uboot到SD卡中
注意要重新编译下这个工具再使用,不然出错
(2)进入sd_fusing----->make clean---->make(重新编译下本目录)
(3)执行:./sd_fusing.sh /dev/sdb 烧入到SD卡

2.16、dram_init

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;
}

2.17、display_dram_config 打印DDR信息

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);
}

2.18、阶段总结:

intit_sequence中:都是板级硬件的初始化已经gd gd->bd中结构体的初始化。
网卡初始化,机器码初始化,内核参数DDR地址初始化,定时器初始化,波特率设置,console第一阶段初始化。
打印uboot的启动信息,打印cpu相关的设置信息,检查并打印开发板名字,ddr配置信息的初始化(gd->bd->bi_dram)\打印DDR总容量。

2.19、CFG_NO_FLASH(NorFlash初始化)

一般写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 */

2.19、CONFIG_VFD和CONFIG_LCD 显示相关的

uboot自带的LCD显示架构,x210不使用这里的架构。

2.20、CONFIG_MEMORY_UPPER_CODE

(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);  /清零堆内存
}

2.21、mmc初始化(开发板独有的初始化)

初始化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); 初始化链表

在这里插入图片描述

2.22、board_mmc_init和cpu_mmc_init(bis);具体硬件的mmc控制器初始化工作。

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
}

在这里插入图片描述
等价于:capacity512/10241024

2.23、env_relocate (); /* initialize environment */

(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
}

在这里插入图片描述

2.24、/* IP Address */读取环境变量的ip

gd->bd->bi_ip_addr = getenv_IPaddr (“ipaddr”);
在这里插入图片描述

2.25、/* MAC Address */

/* 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;
		}

2.26、devices_init ();设备驱动初始化

放在这里初始化的设备都是驱动设备,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);
}

2.27、jumptable_init (); 跳转表

只是赋值,做了左值,没有被使用,不太重要
本身就是函数指针数组,记录了很多函数的函数名。这些都是工具函数。
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
}

2.28、console_init_r 控制台第二阶段初始化

(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);
}

2.29、enable_interrupts (); 中断初始化

(1)指的是CPSR中总中断标志位的使能
(2)uboot中没有使用中断, 给了一个空函数

void enable_interrupts (void)
{
	return;
}

2.30、loadaddr、bootfile

两个环境变量是内核启动有关的暂时不用了解。

2.31、board_late_init ();

实际是空的函数,就是开发板级别的晚期初始化,前面已经初始化过来,后面的初始化就在这里了。侧面说明开发板级别的硬件初始化已经基本完成了。

int board_late_init (void)
{
	return 0;
}

2.32、eth_initialize(gd->bd);网卡相关初始化

(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;
}

2.33、x210_preboot_init(void) LCD和logo显示

(1)开发板启动前的初始化

2.34、check_menu_update_from_sd()

(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:要升级镜像放到sd卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(如果按下“LEFT”,则表示update mode,否则boot mode)。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用sd卡进行系统烧录部署。
在这里插入图片描述

2.35、main_loop ()最终进入死循环(uboot的最终归宿)

/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop ();
	}

(1)解析器
(2)开机倒数自动执行
(3)命令补全(uboot中没有实现)

2.36、本章总结

(1)start_armboot 第二阶段入口
(2)开发版级别硬件初始化和软件初始化
(3)本阶段主要的初始化

函数内嵌函数作用备注
cpu_initcpucpu软件初始化为空
board_init板级初始化,网卡控制器初始化,设置开发板编号
interrupt_init定时器4初始化
env_init环境变量初始化,主要判断这些环境变量是否可用
init_baudrate波特率初始化,从环境变量中读取波特率到全局变量
serial_initserial_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_addrgd结构体的赋值
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_initLCD初始化和logo显示
check_menu_update_from_sd自动更新镜像
main_loop进入uboot死循环,uboot的最终归宿

3、移植注意点

(1)x210_sd.h头文件中的宏定义
(2)特定硬件初始化函数的位置

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值