5.3519v101的Uboot的C语言阶段

C语言阶段,对于宏定义,首先看下include/configs/hi3519v101.h中是否包含了,但有时候宏定义是间接包含的,看不到,或者搜索起来,都是干扰项,怎么办呢?

还有一个万能的方法!就是编译一下uboot,然后在include目录下有个autoconfig.mk文件,配置过程中所有的宏定义,在这个文件中都可以找到,如果找不到的宏定义,说明没被包含!!!!!!

下面是脚本文件中对autoconfig.mk的介绍!

# 加载配置文件

sinclude $(OBJTREE)/include/autoconf.mk

(1)autoconfig.mk文件不是源码提供的,是配置过程自动生成的。

(2)这个文件的作用就是用来指导整个uboot的编译过程。这个文件的内容其实就是很多CONFIG_开头的宏(可以理解为变量),这些宏/变量会影响我们uboot编译过程的走向(原理就是条件编译)。在uboot代码中有很多地方使用条件编译进行编写,这个条件编译是用来实现可移植性的。(可以说uboot的源代码在很大程度来说是拼凑起来的,同一个代码包含了各种不同开发板的适用代码,用条件编译进行区别。)

(3)这个文件不是凭空产生的,配置过程也是需要原材料来产生这个文件的。原材料在源码目录的inlcude/configs/xxx.h头文件。(X210开发板中为include/configs/x210_sd.h)。这个h头文件里面全都是宏定义,这些宏定义就是我们对当前开发板的移植。每一个开发板的移植都对应这个目录下的一个头文件,这个头文件里每一个宏定义都很重要,这些配置的宏定义就是我们移植uboot的关键所在。

00001: /*
00002: * (C) Copyright 2002-2006
00003: * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
00004: *
00005: * (C) Copyright 2002
00006: * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
00007: * Marius Groeger <mgroeger@sysgo.de>
00008: *
00009: * See file CREDITS for list of people who contributed to this
00010: * project.
00011: *
00012: * This program is free software; you can redistribute it and/or
00013: * modify it under the terms of the GNU General Public License a
00013: s
00014: * published by the Free Software Foundation; either version 2 o
00014: f
00015: * the License, or (at your option) any later version.
00016: *
00017: * This program is distributed in the hope that it will be usefu
00017: l,
00018: * but WITHOUT ANY WARRANTY; without even the implied warranty o
00018: f
00019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020: * GNU General Public License for more details.
00021: *
00022: * You should have received a copy of the GNU General Public Lic
00022: ense
00023: * along with this program; if not, write to the Free Software
00024: * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00025: * MA 02111-1307 USA
00026: */
00027:
00028: /*
00029: * To match the U-Boot user interface on ARM platforms to the U-
00029: Boot
00030: * standard (as on PPC platforms), some messages with debug char
00030: acter
00031: * are removed from the default U-Boot build.
00032: *
00033: * Define DEBUG here if you want additional info as shown below
00034: * printed upon startup:
00035: *
00036: * U-Boot code: 00F00000 -> 00F3C774 BSS: -> 00FC3274
00037: * IRQ Stack: 00ebff7c
00038: * FIQ Stack: 00ebef7c
00039: */
00040:
00041: #include <common.h>
00042: #include <command.h>
00043: #include <malloc.h>
00044: #include <stdio_dev.h>
00045: #include <timestamp.h>
00046: #include <version.h>
00047: #include <net.h>
00048: #include <serial.h>
00049: #include <nand.h>
00050: #include <onenand_uboot.h>
00051: #include <spi_flash.h>
00052: #include <mmc.h>
00053: #include <boot/customer.h>
00054:
00055: #ifdef CONFIG_BITBANGMII
00056: #include <miiphy.h>
00057: #endif
00058:
00059: #ifdef CONFIG_DRIVER_SMC91111
00060: #include "../drivers/net/smc91111.h"
00061: #endif
00062: #ifdef CONFIG_DRIVER_LAN91C96
00063: #include "../drivers/net/lan91c96.h"
00064: #endif
00065:
00066: DECLARE_GLOBAL_DATA_PTR;
00067:
00068: ulong monitor_flash_len;
00069:
00070: #ifdef CONFIG_HAS_DATAFLASH
00071: extern int AT91F_DataflashInit(void);
00072: extern void dataflash_print_info(void);
00073: #endif
00074:
00075: #ifndef CONFIG_IDENT_STRING
00076: #define CONFIG_IDENT_STRING ""
00077: #endif
00078:
00079: const char version_string[] =
00080: U_BOOT_VERSION" (" U_BOOT_DATE " - " U_BOOT_TIME
00080: ")"CONFIG_IDENT_STRING;
00081:
00082: #ifdef CONFIG_DRIVER_RTL8019
00083: extern void rtl8019_get_enetaddr (uchar * addr);
00084: #endif
00085:
00086: #if defined(CONFIG_HARD_I2C) || \
00087: defined(CONFIG_SOFT_I2C)
00088: #include <i2c.h>
00089: #endif
00090:
00091: #ifdef CONFIG_GENERIC_MMC
00092: #ifdef CONFIG_HIMCI_V200
00093: extern int mmc_flash_init(int dev_num);
00094: #else
00095: extern int mmc_flash_init(void);
00096: #endif
00097: #endif
00098: #ifdef CONFIG_DDR_TRAINING_V300
00099: extern int check_ddr_training(void);
00100: #endif /* CONFIG_DDR_TRAINING_V300 */
00101:
00126: ****************************************************************
00126: *******
00127: * Init Utilities *
00128: ***************************************************************
00128: *********
00129: * Some of this code should be moved into the core functions,
00130: * or dropped completely,
00131: * but let's get it working (again) first...
00132: */
00134: #if defined(CONFIG_ARM_DCC) && !defined(
00134: CONFIG_BAUDRATE)
00135: #define CONFIG_BAUDRATE 115200
00136: #endif
00137: static int init_baudrate (void)
00138: {
00139: char tmp[64];
00139: /* long enough for environment variables */
00140: int i = getenv_r ("baudrate", tmp, sizeof (tmp));
00141: gd->bd->bi_baudrate = gd->baudrate = (i > 0)
00142: ? (int) simple_strtoul (tmp, NULL, 10)
00143: : CONFIG_BAUDRATE;
00144:
00145: return (0);
00146: }
00147:
00148: static int display_banner (void)
00149: {
00150: printf ("\n\n%s\n\n", version_string);
00151: debug (
00151: "U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
00152: _armboot_start, _bss_start, _bss_end);
00153: #ifdef CONFIG_MODEM_SUPPORT
00154: debug ("Modem Support enabled\n");
00155: #endif
00156: #ifdef CONFIG_USE_IRQ
00157: debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
00158: debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
00159: #endif
00160:
00161: return (0);
00162: }
00163:
00164: /*
00165: * WARNING: this code looks "cleaner" than the PowerPC version,
00165: but
00166: * has the disadvantage that you either get nothing, or everythi
00166: ng.
00167: * On PowerPC, you might see "DRAM: " before the system hangs -
00167: which
00168: * gives a simple yet clear indication which part of the
00169: * initialization if failing.
00170: */
00196: #ifndef CONFIG_SYS_NO_FLASH
00197: static void display_flash_config (ulong size)
00198: {
00199: puts ("Flash: ");
00200: print_size (size, "\n");
00201: }
00202: #endif /* CONFIG_SYS_NO_FLASH */
00203:
00204: #if defined(CONFIG_HARD_I2C) || defined(
00204: CONFIG_SOFT_I2C)
00205: static int init_func_i2c (void)
00206: {
00207: puts ("I2C: ");
00208: i2c_init (CONFIG_SYS_I2C_SPEED,
00208: CONFIG_SYS_I2C_SLAVE);
00209: puts ("ready\n");
00210: return (0);
00211: }
00212: #endif
00213:
00214: #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
00215: #include <pci.h>
00216: static int arm_pci_init(void)
00217: {
00218: pci_init();
00219: return 0;
00220: }
00221: #endif /* CONFIG_CMD_PCI || CONFIG_PCI */
00222:
00223: /*
00224: * Breathe some life into the board...
00225: *
00226: * Initialize a serial port as console, and carry out some hardw
00226: are
00227: * tests.
00228: *
00229: * The first part of initialization is running from Flash memory
00229: ;
00230: * its main purpose is to initialize the RAM so that we
00231: * can relocate the monitor code to RAM.
00232: */
00233:
00234: /*
00235: * All attempts to come up with a "common" initialization sequen
00235: ce
00236: * that works for all boards and architectures failed: some of t
00236: he
00237: * requirements are just _too_ different. To get rid of the resu
00237: lting
00238: * mess of board dependent #ifdef'ed code we now make the whole
00239: * initialization sequence configurable to the user.
00240: *
00241: * The requirements for any new initalization function is simple
00241: : it
00242: * receives a pointer to the "global data" structure as it's onl
00242: y
00243: * argument, and returns an integer return code, where 0 means
00244: * "continue" and != 0 means "fatal error, hang the system".
00245: */
00246: typedef int (init_fnc_t) (void);
00247:
00248: int print_cpuinfo (void);
00249:
00250: init_fnc_t *init_sequence[] = {
00251: #if defined(CONFIG_ARCH_CPU_INIT)
00252: arch_cpu_init, /* basic arch cpu dependent setup */空函数,不管
00253: #endif
00254: timer_init, /* initialize timer before usb init */初始化定时器,暂时不看
00255: board_init, /* basic board dependent setup */

 

设置了MACH值。启动参数的地址,uart时钟,启动标志(记录启动模式)
00263: env_init, /* initialize environment */

1)env_init,看名字就知道是和环境变量有关的初始化。

2)为什么有很多env_init函数,主要原因是uboot支持各种不同的启动介质(譬如norflash、nandflash、inand、sd卡•••••),我们一般从哪里启动就会把环境变量env放到哪里。而各种介质存取操作env的方法都是不一样的。因此uboot支持了各种不同介质中env的操作方法。所以有好多个env_xx开头的c文件。实际使用的是哪一个要根据自己开发板使用的存储介质来定(这些env_xx.c同时只有1个会起作用,其他是不能进去的,通过.h中配置的宏来决定谁被包含的),3)经过基本分析,这个函数只是对内存里维护的那一份uboot的env做了基本的初始化或者说是判定(判定里面有没有能用的环境变量)。当前因为我们还没进行环境变量从flash到DDR中的relocate,因此当前环境变量是不能用的。

4)在start_armboot函数中(776行)调用env_relocate才进行环境变量从flash中到DDR中的重定位。重定位之后需要环境变量时才可以从DDR中去取,重定位之前如果要使用环境变量只能从flash中去读取。


00264: init_baudrate, /* initialze baudrate settings */

1)init_baudrate看名字就是初始化串口通信的波特率的。

2)getenv_r函数用来读取环境变量的值。用getenv函数读取环境变量中"baudrate"的值(注意读取到的不是int型而是字符串类型),然后用simple_strtoul函数将字符串转成数字格式的波特率。

3)baudrate初始化时的规则是:先去环境变量中读取"baudrate"这个环境变量的值。如果读取成功则使用这个值作为环境变量,记录在gd->baudrate和gd->bd->bi_baudrate中;如果读取不成功则使用.h中的的CONFIG_BAUDRATE的值作为波特率。从这可以看出:环境变量的优先级是很高的。


00265: serial_init, /* serial communications setup */

1)serial_init看名字是初始化串口的。(疑问:start.S中已经使用汇编初始化过串口了,这里怎么又初始化?这两个初始化是重复的还是各自有不同?)

2)SI中可以看出uboot中有很多个serial_init函数,我们使用的是serial.c中的serial_init函数。

3)进来后发现serial_init函数其实什么都没做。因为在汇编阶段串口已经被初始化过了,因此这里就不再进行硬件寄存器的初始化了。


00266: console_init_f, /* stage 1 init of console */

1)console_init_f是console(控制台)的第一阶段初始化。_f表示是第一阶段初始化,_r表示第二阶段初始化。有时候初始化函数不能一次一起完成,中间必须要夹杂一些代码,因此将完整的一个模块的初始化分成了2个阶段。(我们的uboot中start_armboot的826行进行了console_init_r的初始化)

2)console_init_f在uboot/common/console.c中,仅仅是对gd->have_console设置为1而已,其他事情都没做。


00267: display_banner, /* say that we are here */

1)display_banner用来串口输出显示uboot的logo

2)display_banner中使用printf函数向串口输出了version_string这个字符串。那么上面的分析表示console_init_f并没有初始化好console怎么就可以printf了呢?

3)通过追踪printf的实现,发现printf->puts,而puts函数中会判断当前uboot中console有没有被初始化好。如果console初始化好了则调用fputs完成串口发送(这条线才是控制台);如果console尚未初始化好则会调用serial_puts(再调用serial_putc直接操作串口寄存器进行内容发送)。

4)控制台也是通过串口输出,非控制台也是通过串口输出。究竟什么是控制台?和不用控制台的区别?实际上分析代码会发现,控制台就是一个用软件虚拟出来的设备,这个设备有一套专用的通信函数(发送、接收•••),控制台的通信函数最终会映射到硬件的通信函数中来实现。uboot中实际上控制台的通信函数是直接映射到硬件串口的通信函数中的,也就是说uboot中用没用控制器其实并没有本质差别。

5)但是在别的体系中,控制台的通信函数映射到硬件通信函数时可以用软件来做一些中间优化,譬如说缓冲机制。(操作系统中的控制台都使用了缓冲机制,所以有时候我们printf了内容但是屏幕上并没有看到输出信息,就是因为被缓冲了。我们输出的信息只是到了console的buffer中,buffer还没有被刷新到硬件输出设备上,尤其是在输出设备是LCD屏幕时)

6)U_BOOT_VERSION在uboot源代码中找不到定义,这个变量实际上是在makefile中定义的,然后在编译时生成的include/version_autogenerated.h中用一个宏定义来实现的。


00268: #if defined(CONFIG_DISPLAY_CPUINFO)
00269: print_cpuinfo, /* display cpu info (and speed) */打印CPU的名字和速度等参数,没定义!
00270: #endif
00271: #if defined(CONFIG_DISPLAY_BOARDINFO)
00272: checkboard, /* display board info */ 打印板子的名称
00273: #endif
00274: #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
00275: init_func_i2c,  //先不管
00276: #endif
00277: dram_init, /* configure available RAM banks */

1)dram_init看名字是关于DDR的初始化。疑问:在汇编阶段已经初始化过DDR了否则也无法relocate到第二部分运行,怎么在这里又初始化DDR?

2)dram_init都是在给gd->bd里面关于DDR配置部分的全局变量赋值,让gd->bd数据记录下当前开发板的DDR的配置信息,以便uboot中使用内存。

3)从代码来看,其实就是初始化gd->bd->bi_dram这个结构体数组。


00278: #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
00279: arm_pci_init,

//pcie的板级初始化
00280: #endif
00281: /* display_dram_config */
00282: NULL,
00283: };
为什么这么长的函数,怎么不分成两三个函数?主要因为这个函数整个构成了uboot启动的第二阶段。

1.2一个函数组成uboot第二阶段

1.3宏观分析:uboot第二阶段应该做什么

1)概括来讲uboot第一阶段主要就是初始化了SoC内部的一些部件,然后初始化DDR并且完成重定位。

2)由宏观分析来讲,uboot的第二阶段就是要初始化剩下的还没被初始化的硬件。主要是SoC外部硬件(譬如iNand、网卡芯片••••)、uboot本身的一些东西(uboot的命令、环境变量等••••)。然后最终初始化完必要的东西后进入uboot的命令行准备接受命令。

1.4思考:uboot第二阶段完结于何处?

1)uboot启动后自动运行打印出很多信息(这些信息就是uboot在第一和第二阶段不断进行初始化时,打印出来的信息)。然后uboot进入了倒数bootdelay秒然后执行bootcmd对应的启动命令。

2)如果用户没有干涉则会执行bootcmd进入自动启动内核流程(uboot就死掉了);此时用户可以按下回车键打断uboot的自动启动进入uboot的命令行下。然后uboot就一直工作在命令行下。

3)uboot的命令行就是一个死循环,循环体内不断重复:接收命令、解析命令、执行命令。这就是uboot最终的归宿。
00285: void start_armboot (void)
00286: {
00287: init_fnc_t **init_fnc_ptr;

typedef int (init_fnc_t) (void);定义一个函数类型,函数的参数列表为空,返回值为int类型。

init_fnc_ptr是一个二重函数指针,回顾高级C语言中讲过:二重指针的作用有2个(其中一个是用来指向一重指针),一个是用来指向指针数组。因此这里的init_fuc_ptr可以用来指向一个函数指针数组。我们可以进一步分析,这是一个数组,数组里面都是函数,我们知道函数名就是函数指针,因此整个数组的元素就是若干个int (init_fnc_t) (void)。
00288: char *s;
/*Pointer is writable since we allocated a register for it */
00297: gd = (gd_t*)(_armboot_start -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));


00298: /* compiler optimization barrier needed for GCC >= 3.4 */ 

/*下面这局话请参考内存屏障*/
00299: __asm__ __volatile__("": : :"memory");
00300:
00301: memset ((void*)gd, 0, sizeof (gd_t));  ///*对板块信息指针初始化,对结构体内存进行清零*/
00302: gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 将gd指针转化成字符型,bd_t是结构体类型。
00303: memset (gd->bd, 0, sizeof (bd_t));

00305: gd->flags |= GD_FLG_RELOC;
00307: monitor_flash_len = _bss_start - _armboot_start;

//_armboot_start 在start.S中定义为_start,而_start为代码的起始地址

//只包含 RO(TEXT) 和 RW(DATA) 段.重定位前的值为0x0,此时指向flash,

//重定位后则指向RAM中的某一地址

//由此可以知道: _bss_start - _armboot_start 的值即是在第一阶段从

//flash中重定位到RAM中的那部分代码的长度,也即TEXT和DATA数据段,

//这个值与start.S中的重定位那部分代码所计算的值是相等的

//所以,monitor_flash_len表示从flash中搬来的代码的长度


00309: for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
00310: if ((*init_fnc_ptr)() != 0) {
00311: hang ();
00312: }
00313: }
00314:

1)init_sequence是一个函数指针数组,数组中存储了很多个函数指针,这些指向指向的函数都是init_fnc_t类型(特征是接收参数是void类型,返回值是int)。

2)init_sequence在定义时就同时给了初始化,初始化的函数指针都是一些函数名。(C语言高级专题中讲过:函数名的实质)

3)init_fnc_ptr是一个二重函数指针,可以指向init_sequence这个函数指针数组。

4)用for循环肯定是想要去遍历这个函数指针数组(遍历的目的也是去依次执行这个函数指针数组中的所有函数)。思考:如何遍历一个函数指针数组?有2种方法:第一种也是最常用的一种,用下标去遍历,用数组元素个数来截至。第二种不常用,但是也可以。就是在数组的有效元素末尾放一个标志,依次遍历到标准处即可截至(有点类似字符串的思路)。

我们这里使用了第二种思路。因为数组中存的全是函数指针,因此我们选用了NULL来作为标志。我们遍历时从开头依次进行,直到看到NULL标志截至。这种方法的优势是不用事先统计数组有多少个元素。

5)init_fnc_t的这些函数的返回值定义方式一样的,都是:函数执行正确时返回0,不正确时返回-1.所以我们在遍历时去检查函数返回值,如果遍历中有一个函数返回值不等于0则hang()挂起。从分析hang函数可知:uboot启动过程中初始化板级硬件时不能出任何错误,只要有一个错误整个启动就终止,除了重启开发板没有任何办法。

6)init_sequence中的这些函数,都是board级别的各种硬件初始化。下面来讲解 下init_sequence函数指针数组中的各种函数:


00315: /*
00315: armboot_start is defined in the board-specific linker script */初始化堆栈
00316: mem_malloc_init (_armboot_start -CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN);
00318:
00319: #ifndef CONFIG_SYS_NO_FLASH  //没定义!
00320: /* configure available FLASH banks */
00321: display_flash_config (flash_init ());     //flash初始化,与读取大小
00322: #endif /* CONFIG_SYS_NO_FLASH */
00357: spi_flash_probe(0, 0, 0, 0);

hifmc100_spi_nor_probe

hifmc100_driver_probe

hifmc_ip_ver_check

hifmc100_spi_nor_scan

hifmc_spi_nor_probe

SPI FLASH的操作流程:

1)首先:uboot中定义了一个结构体数组,来存放特定的几个spi flash,包括型号、大小、id

2)uboot通过一套指令流程去读取spi flash的id

3)利用id与结构体数组中的flash进行匹配,成功则确定flash型号,将其参数赋值到一个结构体数组中。

4)后面对spi flash的操作,均在此结构体基础上进行的操作。


00383: #if defined(CONFIG_ARCH_HI3536)  || defined(CONFIG_HI3518EV200) || defined(CONFIG_HI3516CV300) \ || defined(CONFIG_ARCH_HI3519) \ || defined(CONFIG_ARCH_HI3519V101) \|| defined(CONFIG_ARCH_HI3516AV200) \|| defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556)
00390: #ifdef CONFIG_GENERIC_MMC
00394: puts("MMC: ");
00395: mmc_initialize(0);
00396: mmc_flash_init(0);

我们这里没有使用EMMC,其实可以去掉的,宏定义去掉!
00400: #endif
00451: /* initialize environment */
00452: env_relocate ();

//初始化由于在配置文件中要定义环境变量区域的大小,即#define CFG_ENV_SIZE 0x40000,这里从heap堆里分配出这么大的空间来,并用env_ptr指向它

环境变量重定位总结:

1)env_relocate是环境变量的重定位,完成从SD卡中将环境变量读取到DDR中的任务。

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

第一次运行uboot时板子会打印如下信息
   *** Warning - bad CRC or NAND, using default environment

3)真正的从flash卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。

小结;env_relocate 所做的事情有3件

1. 从heap中分配一段空间,用于env_t结构

2. 找到环境变量(或从内存中找或从nand中找),填充env_t结构

3. 将gd->env_addr指向env_ptr->data, 这个也就是这里的relocate所在吧。


00458:
00459: #ifdef CONFIG_SERIAL_MULTI
00460: serial_initialize();
00461: #endif
00462:
00463: /* IP Address */
00464: gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

将环境变量弄完之后,紧接着就是从环境变量的相应项中获取信息,环境变量是用户与u-boot的一个交互方式,有了它之后,用户即可通过修改环境变量来修改板子的一些信息配置。这里的ip地址和网卡地址即是其中的一个典型例子。来看上面的程序:获取ip地址,注意gd->bd->bi_ip_addrungisned long 类型,而ip地址是类似于"192.168.1.111"的字符串。往下跟踪:

     1    IPaddr_t getenv_IPaddr (char *var)
     2    {
     3        return (string_to_ip(getenv(var)));
     4    }

getenv("ipaddr") 即在环境变量中找到ipaddr这一项对应的字符串,假设这里为"192.168.1.111""192.168.1.111"传入string_to_ip IPaddr_t 类型是unsigned long 的一个typedef

5    IPaddr_t string_to_ip(char *s)
     6    {
     7        IPaddr_t addr;
     8        char *e;
     9        int i;

    10        if (s == NULL)
    11            return(0);

    12        for (addr=0, i=0; i<4; ++i) {
    13            ulong val = s ? simple_strtoul(s, &e, 10) : 0;
    14            addr <<= 8;
    15            addr |= (val & 0xFF);
    16            if (s) {
    17                s = (*e) ? e+1 : e;
    18            }
    19        }

    20        return (htonl(addr));
    21    }

12~19 192.168.1.111 分为四个段,也就是要做4 simple_strtoul()转换成10进制的整型
第次转换后的值赋给valaddrunsigned long型,32位的,将其分为4段,每8位存储ip地址中的一个段,比如这里,最后addr = (((((192 << 8) | 168) << 8) | 1) << 8 ) | 111 = 0xc0a8016f
20,主机字节顺序转换为网络字节顺序返回
CPU为小端模式时,addr如下存储

31           24 23           16 15             8 7               0
+--------------+---------------+----------------+-----------------+
| 192 = 0xc0   | 168 = 0xa8    |   1 = 0x01     |  111 = 0x6f     |
+--------------+---------------+----------------+-----------------+
     3                2                  1               0

CPU为大端模式时,addr如下存储

31           24 23           16 15             8 7               0
+--------------+---------------+----------------+-----------------+
| 111 = 0x6f   |  1 = 0x01     |  168 = 0xa8    |  192 = 0xc0     |
+--------------+---------------+----------------+-----------------+
      3                2                 1               0

当与另一台计算机通信时,通常不知道对方存储数据时是先存放最高位字节 (MSB)还是最低位字节 (LSB)恰恰网络字节顺序跟大端模式时相同,htonl函数就是将主机字节顺序转为网络字节顺序,在最高位字节(MSB)-最前 的系统上,这些函数什么都不做。在 最低位字节(LSB)-最前的系统上它们将值转换为正确的顺序。最后将值返回给了gd->bd->bi_ip_addr 所以其值应该是 0x6f01800a


00466: stdio_init (); /* get the devices list going. */
1) stdio_init看名字就是标准设备的初始化。这里的设备指的就是开发板上的硬件设备。放在这里初始化的设备都是驱动设备,这个函数本来就是从驱动框架中衍生出来的。uboot中很多设备的驱动是直接移植linux内核的(譬如网卡、SD卡),linux内核中的驱动都有相应的设备初始化函数。linux内核在启动过程中就有一个devices_init(名字不一定完全对,但是差不多),作用就是集中执行各种硬件驱动的init函数。

2)uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。


00468: jumptable_init ();
1)jumptable跳转表,本身是一个函数指针数组,里面记录了很多函数的函数名。看这阵势是要实现一个函数指针到具体函数的映射关系,将来通过跳转表中的函数指针就可以执行具体的函数。这个其实就是在用C语言实现面向对象编程。在linux内核中有很多这种技巧。

2)通过分析发现跳转表只是被赋值从未被引用,因此跳转表在uboot中根本就没使用。


00475: console_init_r ();
00475: /* fully init console as a device */

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不能实现的东西)。

#if defined(CONFIG_MISC_INIT_R)

      misc_init_r ();   //设置了一个环境变量:version = n,和自动升级的功能,不看了

#endif
00486: /* enable exceptions */
00487: enable_interrupts ();
1)看名字应该是中断初始化代码。这里指的是CPSR中总中断标志位的使能。

2)因为我们uboot中没有使用中断,因此没有定义CONFIG_USE_IRQ宏,因此我们这里这个函数是个空壳子。

3)uboot中经常出现一种情况就是根据一个宏是否定义了来条件编译决定是否调用一个函数内部的代码。uboot中有2种解决方案来处理这种情况:方案一:在调用函数处使用条件编译,然后函数体实际完全提供代码。方案二:在调用函数处直接调用,然后在函数体处提供2个函数体,一个是有实体的一个是空壳子,用宏定义条件编译来决定实际编译时编译哪个函数进去。


00509: /* Initialize from environment */这两个环境变量都是内核启动有关的,在启动linux内核时会参考这两个环境变量的值。
00510: if ((s = getenv ("loadaddr")) != NULL) { load_addr = simple_strtoul (s, NULL, 16); }
00513: #if defined(CONFIG_CMD_NET)
00514: if ((s = getenv ("bootfile")) != NULL) {copy_filename (BootFile, s, sizeof (BootFile));
00516: }
00517: #endif
00518:
00519: #ifdef BOARD_LATE_INIT

1)看名字这个函数就是开发板级别的一些初始化里比较晚的了,就是晚期初始化。所以晚期就是前面该初始化的都初始化过了,剩下的一些必须放在后面初始化的就在这里了。侧面说明了开发板级别的硬件软件初始化告一段落了。没定义,不看了!
00520: board_late_init ();
00521: #endif

00526: #if defined(CONFIG_CMD_NET)
00530: eth_initialize(gd->bd);           //实际上啥都没做,只是初始化了一个MII的链表,都没赋值
00531: #endif
00550: product_control(); //啥都没做!
00560: /*
00560: main_loop() can return to retry autoboot, if so just run it aga in. */

00561: for (;;) {
00562: main_loop ();
00563: }
00564:
00565: /*
00565: NOTREACHED - no way out of command loop except booting */
00566: } ? end start_armboot ?
00567:
00568: void hang (void)
00569: {
00570: puts ("### ERROR ### Please RESET the board ###\n"
00570: );
00571: for (;;);
00572: }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值