接上部分---->嵌入式之uboot源码分析-启动第二阶段学习笔记(上篇)

:如下内容来自朱老师物联网大讲堂uboot课件

3.2.14 CFG_NO_FLASH

(1)虽然NandFlash和NorFlash都是Flash,但是一般NandFlash会简称为Nand而不是Flash,一般讲Flash都是指的Norflash。这里2行代码是Norflash相关的。
(2)flash_init执行的是开发板中对应的NorFlash的初始化、display_flash_config打印的也是NorFlash的配置信息(Flash: 8 MB就是这里打印出来的)。但是实际上X210中是没有Norflash的。所以着两行代码是可以去掉的(我也不知道为什么没去掉?猜测原因有可能是去掉着两行代码会导致别的地方工作不正常,需要花时间去移植调试,然后移植的人就懒得弄。实际上不去掉除了显示有8MB Flash实际没用之外也没有别的影响)

代码实践:去掉Flash看会不会出错。
结论:加上CONFIG_NOFLASH宏之后编译出错,说明代码移植的不好,那个文件的包含没有被这个宏控制。于是乎移植的人就直接放这没管。

3.2.15 CONFIG_VFD和CONFIG_LCD

CONFIG_VFD和CONFIG_LCD是显示相关的,这个是uboot中自带的LCD显示的软件架构。但是实际上我们用LCD而没有使用uboot中设置的这套软件架构,我们自己在后面自己添加了一个LCD显示的部分。

3.2.16 mem_malloc_init

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);
}#define CFG_MALLOC_LEN      (CFG_ENV_SIZE + 896*1024)

(1)mem_malloc_init函数用来初始化uboot的堆管理器。
(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。

3.2.17 mmc_initialize

(1)从536到768行为开发板独有的初始化。意思是三星用一套uboot同时满足了好多个系列型号的开发板,然后在这里把不同开发板自己独有的一些初始化写到了这里。用#if条件编译配合CONFIG_xxx宏来选定特定的开发板。
(2)X210相关的配置在603行到629行。
(3)mmc_initialize看名字就应该是MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。
(4)uboot中对硬件的操作(譬如网卡、SD卡···)都是借用的linux内核中的驱动来实现的,uboot根目录底下有个drivers文件夹,这里面放的全都是从linux内核中移植过来的各种驱动源文件。
(5)mmc_initialize是具体硬件架构无关的一个MMC初始化函数,所有的使用了这套架构的代码都掉用这个函数来完成MMC的初始化。mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。
(6)cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,这里面又间接的调用了drivers/mmc/s3c_mmcxxx.c中的驱动代码来初始化硬件MMC控制器。这里面分层很多,分层的思想一定要有,否则完全就糊涂了。

  • 程序中出现了双链表的访问

3.2.18 env_relocate

1.判断我们是否是内嵌环境变量(我们这里ENV_IS_EMBEDDED有被宏定义),如果不是则使用malloc分配

#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
  1. 如果环境变量是无效的,并且环境变量也不存在的就输出Warning,同时将环境变量设置为默认值,这里调用set_default_env()函数,实现将系统默认的环境变量复制到分配的env_ptr->data中,更新CRC,并将env_valid设为1 ,下次启动就可直接重定位了。
知识点:什么是CRC校验

CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
更多CRC相关请参考----->CRC校验原理及步骤

void set_default_env(void)
{if (sizeof(default_environment) > ENV_SIZE) {puts ("*** Error - default environment is too large\n\n");return;}memset(env_ptr, 0, sizeof(env_t));memcpy(env_ptr->data, default_environment,sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENTenv_ptr->flags = 0xFF;
#endifenv_crc_update ();gd->env_valid = 1;
}
  1. 如果环境变量是有效的,就执行环境变量的重定位
    if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE)/* Environment not changable */puts ("Using default environment\n\n");
#elseputs ("*** Warning - bad CRC, using default environment\n\n");show_boot_progress (-60);
#endifset_default_env();}else {env_relocate_spec ();}gd->env_addr = (ulong)&(env_ptr->data);

(1)env_relocate是环境变量的重定位,完成从SD卡中将环境变量读取到DDR中的任务。
(2)环境变量到底从哪里来?SD卡中有一些(8个)独立的扇区作为环境变量存储区域的。但是我们烧录/部署系统时,我们只是烧录了uboot分区、kernel分区和rootfs分区,根本不曾烧录env分区。所以当我们烧录完系统第一次启动时ENV分区是空的,本次启动uboot尝试去SD卡的ENV分区读取环境变量时失败(读取回来后进行CRC校验时失败),我们uboot选择从uboot内部代码中设置的一套默认的环境变量出发来使用(这就是默认环境变量);这套默认的环境变量在本次运行时会被读取到DDR中的环境变量中,然后被写入(也可能是你saveenv时写入,也可能是uboot设计了第一次读取默认环境变量后就写入)SD卡的ENV分区。然后下次再次开机时uboot就会从SD卡的ENV分区读取环境变量到DDR中,这次读取就不会失败了。
(3)真正的从SD卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。

3.2.19 IP地址

    /* IP Address */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");IPaddr_t getenv_IPaddr (char *var)
{return (string_to_ip(getenv(var)));
}

(1)开发板的IP地址是在gd->bd中维护的,来源于环境变量ipaddr。getenv函数用来获取字符串格式的IP地址,然后用string_to_ip将字符串格式的IP地址转成字符串格式的点分十进制格式。
(2)IP地址由4个0-255之间的数字组成,因此一个IP地址在程序中最简单的存储方法就是一个unsigend int(0xC0A80102)。但是人类容易看懂的并不是这种类型,而是点分十进制类型(192.168.1.2)。这两种类型可以互相转换。

3.2.20 MAC地址

我们开发板的MAC地址为:ethaddr=00:40:5c:26:0a:5b

    /* 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;}#ifdef CONFIG_HAS_ETH1i = getenv_r ("eth1addr", tmp, sizeof (tmp));s = (i > 0) ? tmp : NULL;for (reg = 0; reg < 6; ++reg) {gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;//用来我们的地址之间有字符之间有“:”或“.”符号,simple_strtoul 函数执行//结束后,s就会指向“:”或“.”,所以我们使用如下的函数去除“:”或“.”if (s)s = (*e) ? e + 1 : e;}
#endif}
知识点1 :进制的表示
  • 八进制:以0开头,由0~7组成的数。如,0126,050000
  • 十进制:除表示正负的符号外,以1~ 9开头,由0~9组成。如,128,+234,-278
  • 十六进制:以0X或0x开头,由0~ 9,A~ F或a~f 组成。如,0x12A,0x5a000
知识点2 :simple_strtoul函数解析

功能:将一个字符串转换成unsigend long long型数据。
返回:返回转换后数据。
参数:cp指向字符串的开始,endp指向分析的字符串末尾的位置,base为要用的基数(进制数)

更多参考simple_strtoul()


unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{unsigned long result = 0,value;//isxdigit判断cp[1]是否为十六进制数,是则返回非0值,不是则返回0,
//我们发现函数先判断首字符是否为0,然后判断下一个字符是否为xif (*cp == '0') {cp++;//第一个字符为0,第二个字符为x则表示16进制if ((*cp == 'x') && isxdigit(cp[1])) {base = 16;cp++;}//第一个字符为0,第二个字符不是x,并且base不等于0,就表示为8进制if (!base) {base = 8;}}//如果首字符不是0,并且base不等于0,则就表示为十进制if (!base) {base = 10;}//如果*cp的值小于等于9则*cp-0,
//进行拆分(islower(*cp) ? toupper(*cp) : *cp)表示将小写字母转换为大写
//(islower(*cp) ? toupper(*cp) : *cp)-'A'+10表示将字符转换为十进制
//所以如下就表示将值转化为十进制
(value = isdigit(*cp) ? *cp-'0' : (islower(*cp) ? toupper(*cp) : *cp)-'A'+10)//然后将计算出来的值和进制比较 ,如果小于说明是合法的,则进行转换,大于则直接退出while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)? toupper(*cp) : *cp)-'A'+10) < base) {result = result*base + value;cp++;}if (endp)*endp = (char *)cp;return result;
}

3.2.21 devices_init

(1)devices_init看名字就是设备的初始化。这里的设备指的就是开发板上的硬件设备。放在这里初始化的设备都是驱动设备,这个函数本来就是从驱动框架中衍生出来的。uboot中很多设备的驱动是直接移植linux内核的(譬如网卡、SD卡),linux内核中的驱动都有相应的设备初始化函数。linux内核在启动过程中就有一个devices_init(名字不一定完全对,但是差不多),作用就是集中执行各种硬件驱动的init函数。
(2)uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。

3.2.22 jumptable_init

知识点:二重指针的作用

1)二重指针指向一重指针的地址
2)二重指针指向指针数组的地址
我们这里就是第二种作用指向了函数指针数组

(1)jumptable跳转表,本身是一个函数指针数组,里面记录了很多函数的函数名。看这阵势是要实现一个函数指针到具体函数的映射关系,将来通过跳转表中的函数指针就可以执行具体的函数。这个其实就是在用C语言实现面向对象编程。在linux内核中有很多这种技巧。
(2)通过分析发现跳转表只是被赋值从未被引用,因此跳转表在uboot中根本就没使用。

3.2.23 console_init_r


在我们启动过程中如上的信息就是通过这个函数打印出来的
(1)console_init_f是控制台的第一阶段初始化,console_init_r是第二阶段初始化。实际上第一阶段初始化并没有实质性工作,第二阶段初始化才进行了实质性工作。
(2)uboot中有很多同名函数,使用SI工具去索引时经常索引到不对的函数处(回忆下当时start.S中找lowlevel_init.S时,自动索引找到的是错误的,真正的反而根本没找到。)
(3)console_init_r就是console的纯软件架构方面的初始化(说白了就是去给console相关的数据结构中填充相应的值),所以属于纯软件配置类型的初始化。
(4)uboot的console实际上并没有干有意义的转化,它就是直接调用的串口通信的函数。所以用不用console实际并没有什么分别。(在linux内console就可以提供缓冲机制等不用console不能实现的东西)。

2.2.24 enable_interrupts

#ifdef CONFIG_USE_IRQ
/* enable IRQ interrupts */
void enable_interrupts(void)
{unsigned long temp;__asm__ __volatile__("mrs %0, cpsr\n" "bic %0, %0, #0x80\n" "msr cpsr_c, %0":"=r"(temp)::"memory");
}#else
void enable_interrupts(void)
{return;
}

(1)看名字应该是中断初始化代码。这里指的是CPSR中总中断标志位的使能。
(2)因为我们uboot中没有使用中断,因此没有定义CONFIG_USE_IRQ宏,因此我们这里这个函数是个空壳子。

知识点:uboot中如何调用函数

方案一:在调用函数处使用条件编译,然后函数体实际完全提供代码。
方案二:在调用函数处直接调用,然后在函数体处提供2个函数体,一个是有实体的一个是空壳子,用宏定义条件编译来决定实际编译时编译哪个函数进去。

2.2.25 loadaddr、bootfile两个环境变量

这两个环境变量都是内核启动有关的,在启动linux内核时会参考这两个环境变量的值。

2.2.26 board_late_init

#else
int board_late_init (void)
{return 0;
}

(1)看名字这个函数就是开发板级别的一些初始化里比较晚的了,就是晚期初始化。所以晚期就是前面该初始化的都初始化过了,剩下的一些必须放在后面初始化的就在这里了。侧面说明了开发板级别的硬件软件初始化告一段落了。
(2)对于X210来说,这个函数是空的。

2.2.27 eth_initialize


(1)看名字应该是网卡相关的初始化。这里不是SoC与网卡芯片连接时SoC这边的初始化,而是网卡芯片本身的一些初始化。
(2)对于X210(DM9000)来说,这个函数是空的。X210的网卡初始化在board_init函数中,网卡芯片的初始化在驱动中。

2.2.28 x210_preboot_init(LCD和logo显示)

x210开发板在启动起来之前的一些初始化,以及LCD屏幕上的logo显示。

2.2.29 update_all

(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:我们可以将要升级的镜像放到SD卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(是一个按键。按键中标志为"LEFT"的那个按键,这个按键如果按下则表示update mode,如果启动时未按下则表示boot mode)。如果进入update mode则uboot会自动从SD卡中读取镜像文件然后烧录到iNand中;如果进入boot mode则uboot不执行update,直接启动正常运行。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用SD卡进行系统烧录部署。

2.2.30 主循环

(1)解析器
(2)开机倒数自动执行
(3)命令补全

启动第二阶段流程梳理

首先第一阶段结束后跳转到start_armboot函数处
1.内存分配
2.执行init_sequence函数对board级别的各种硬件初始化,其中包含:

  • cpu_init(空)
  • Board_init(网卡初始化、开发板的机器码、传参的内存地址)
  • Interrupt_init(初始化定时器Timer4)
  • Env_init(环境变量有关的初始化)
  • Init_baudrate(初始化串口通信的波特率)
  • Serial_init(空)
  • console_init_f(控制台第一阶段初始化)
  • display_banner(串口输出显示uboot的版本)
  • print_cpuinfo(输出CPU的时钟频率信息)
  • Checkboard(确认开发板名称)
  • init_func_i2c(未使用)
  • dram_init(初始化DDR)
  • display_dram_config(显示DRAM大小)

3.CFG_NO_FLASH(无Norflash)
4.CONFIG_VFD和CONFIG_LCD是显示相关的
5.mem_malloc_init(初始化uboot自己维护的堆管理器的内存)
6.mmc_initialize(初始化SoC内部的SD/MMC控制器)
7.env_relocate(环境变量的重定位)
8.IP地址字符串转十进制并对gd数据结构赋值
9.MAC地址的字符串转十六进制并对gd数据结构赋值
10.devices_init(设备的初始化
11.jumptable_init(未使用)
12.console_init_r(控制台第二阶段纯软件配置类型的初始化)
13.enable_interrupts(未使用)
14.loadaddr、bootfile (环境变量读出初始化全局变量)
15.board_late_init(空)
16.eth_initialize(空)
17.x210_preboot_init(LCD屏幕上的logo显示)
18.update_all(启动过程中使用按键可进入自动更新模式进行烧录)
19.Main_loop主循环

启动过程特征总结
(1)第一阶段为汇编阶段、第二阶段为C阶段
(2)第一阶段在SRAM中、第二阶段在DRAM中
(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部

嵌入式之uboot源码分析-启动第二阶段学习笔记(下篇)相关推荐

  1. 嵌入式之uboot源码分析-启动第一阶段学习笔记

    注: 以下的内容来自朱老师物联网大讲堂uboot部分课件 Uboot启动第一阶段start.S执行步骤 1.头文件包含 <config.h>(x210的各种宏定义) <version ...

  2. uboot源码分析-启动第一阶段

    注:基于九鼎x210 uboot 在SourceInsight软件下 一.start.S引入 1.u-boot.lds中找到start.S入口 (1)在C语言中整个项目的入口就是main函数(这是C语 ...

  3. uboot源码分析(基于S5PV210)之启动第一阶段

    目录 一.start.S引入 1.u-boot.lds中找到start.S入口 2.SourceInsight中如何找到文件 3.SI中找文件技巧 二.start.S解析 1.不简单的头文件包含 2. ...

  4. linux uboot 源码分析,UBoot源码分析1.pdf

    UBoot源码分析1 • UBoot源码解析(一) 主要内容 • 分析UBoot是如何引导Linux内核 • UBoot源码的一阶段解析 BootLoader概念 • Boot Loader 就是在操 ...

  5. golang源码分析-启动过程概述

    golang源码分析-启动过程概述 golang语言作为根据CSP模型实现的一种强类型的语言,本文主要就是通过简单的实例来分析一下golang语言的启动流程,为深入了解与学习做铺垫. golang代码 ...

  6. springboot集成mybatis源码分析-启动加载mybatis过程(二)

    springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...

  7. X210之uboot源码分析

    uboot源码分析 Makefile分析1 自己参考源码. Makefile分析2 ifdef O ifeq ("$(origin O)", "command line& ...

  8. 【OpenHarmony-v3.2代码分析】02 - device目录 uboot源码分析

    [OpenHarmony-v3.2代码分析]02 - device目录 uboot源码分析 1. device 目录分析 从本文开始 ,我们正式来分析OpenHarmony-V3.2的源码的 uboo ...

  9. uboot源码分析笔记

    前几天看了ucos的源码,后面开始学习uboot的源码 网上看到一些uboot的文章,当然都是牛人写出来的,不过基本版本有点老,我这越是初学者,越想学习新版本的代码 我下载的是u-boot-2014. ...

最新文章

  1. 计算机视觉多目标检测整合算法
  2. Redis最佳实践:7个维度+43条使用规范,带你彻底玩转Redis | 附实践清单
  3. genrsa out php,PHP进行RSA加密解密
  4. python 递归 分叉_浅谈Python 递归算法指归
  5. Silverlight与HTML双向交互
  6. java 获取当前月份的第一天_JAVA获取本月的第一天和最后一天
  7. 46. 全排列015(回溯法求解)
  8. android wifi热点项目总结,高通Android wifi移植和wifi热点问题总结
  9. php培训js重要么,php何以被戏称为“世界上最好的语言”?请看它与js的对比!...
  10. 从词袋到 Transfomer,NLP 十年突破史
  11. 滚动到div的底部?
  12. Map Reduce学习
  13. [您有新的未分配科技点][BZOJ3545BZOJ3551]克鲁斯卡尔重构树
  14. 博客园文章中图片太大显示不全的解决办法
  15. 暗黑破坏神(DIABLOII 1.11B)BOT 及源代码公开下载
  16. ANC主动降噪理论及Matlab代码实现
  17. BIP学习第一课,创建一个应用和实体并设计页面(图文)
  18. [推荐]白纸上的黑点和黑纸上的白点
  19. 量子计算机与新南威尔士大学,新南威尔士大学声称量子计算的准确性
  20. 网站点击流日志数据分析

热门文章

  1. Tomcat部署步骤
  2. Python加密工具包pycrypto的安装
  3. Linux之xinetd安装及实践
  4. Latex 的交叉引用
  5. html 表格自动编号,excel表格批量自动编号
  6. 编程java摇号购房,用java编写摇号器
  7. prompt tuning
  8. 柯西积分不等式的证明题
  9. 无人驾驶仿真软件PanoSim:(1)介绍
  10. matlab Logistic回归模型