1、前言

在前面的文章《Uboot启动流程分析(二)》中,链接如下:

https://www.cnblogs.com/Cqlismy/p/12002764.html

已经对_main函数的整个大体调用流程,以及函数的实现的各个功能进行了简单地分析,接下来,本篇文章将对board_init_f函数进行分析,在此之前,先来回顾一下_main函数的简单调用流程,如下所示:

_main
|
board_init_f_alloc_reserve-->reserve gd and early malloc area
|
board_init_f_init_reserve-->initialize global data
|
board_init_f-->initialize ddr,timer...,and fill gd_t
|
relocate_code-->relocate uboot code
|
relocate_vectors-->relocate vectors
|
board_init_r-->calling board_init_r

2、board_init_f函数

接下来,将对board_init_f()函数进行分析,该函数的定义在文件

uboot-imx-rel_imx_4.1.15/common/board_f.c

该函数的实现源码(忽略无关宏)如下所示:

/* 该函数在_main函数中进行调用 */
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags; /* 启动标志 */
gd->have_console = ; /* 是否具有console标志 */ if (initcall_run_list(init_sequence_f))
hang();
}

该函数调用后,首先会设置gd->flags这个标志位,然后将gd结构体中的have_console成员变量设置为0,表示此时并没有console,该函数的重点在if这个判断语句,将会调用initcall_run_list,并传入init_sequence_f,也就是初始化序列,接下来,重点关注init_sequence_f这个初始化序列。

init_sequence_f包含了一系列的初始化函数,定义同样在board_f.c文件中,它包含了大量的条件编译的相关函数,去掉一些无关的条件编译后,init_sequence_f的定义如下:

/* 初始化序列函数数组 */
static init_fnc_t init_sequence_f[] = {
setup_mon_len, /* 设置gd的mon_len成员,表示uboot代码的长度 */
initf_malloc, /* 初始化gd中与malloc相关的成员 */
initf_console_record,
/* 初始化arm架构相关的东西 */
arch_cpu_init, /* basic arch cpu dependent setup */
initf_dm, /* 初始化驱动模型相关 */
arch_cpu_init_dm,
mark_bootstage, /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f, /* 与板子的早期外设初始化,imx6ul用来初始化串口 */
#endif #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
defined(CONFIG_SPARC)
timer_init, /* initialize timer */ /* 初始化Cortex-A7中的定时器 */
#endif #if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
get_clocks, /* 获取时钟值 */
#endif /* 和环境变量有关,设置gd的env_addr成员 */
env_init, /* initialize environment */
init_baud_rate, /* initialze baudrate settings */ /* 初始化串口波特率 */
serial_init, /* serial communications setup */ /* 初始化串口 */
console_init_f, /* stage 1 init of console */ /* 设置console标志位 */
display_options, /* say that we are here */ /* 显示uboot版本和编译时间字符串 */
display_text_info, /* show debugging info if required */
/* 显示cpu的相关信息 */
print_cpuinfo, /* display cpu info (and speed) */ #if defined(CONFIG_DISPLAY_BOARDINFO)
show_board_info, /* 显示board的相关信息 */
#endif INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
init_func_i2c, /* 初始化i2c接口(实际初始化的是SPD_BUS) */
#endif #if defined(CONFIG_HARD_SPI)
init_func_spi, /* i.mx6ul并没有初始化SPI接口 */
#endif
announce_dram_init, /* 输出"DRAM:"字符串 */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
dram_init, /* configure available RAM banks */ /* 获取DDR的大小 */
#endif
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*
* Reserve memory at end of RAM for (top down in that order):
* - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
* - protected RAM
* - LCD framebuffer
* - monitor code
* - board info struct
*/
setup_dest_addr,/* 设置目的地址(gd->ram_size,gd->ram_top,gd->relocaddr) */
reserve_round_4k, /* 对gd->relocaddr做4K对齐 */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
defined(CONFIG_ARM)
reserve_mmu, /* 留出mmu的TLB表位置 */
#endif reserve_trace, /* 留出ddr调试追踪的内存 */
#if !defined(CONFIG_BLACKFIN)
reserve_uboot, /* 留出重定位uboot占用的位置 */
#endif
#ifndef CONFIG_SPL_BUILD
reserve_malloc, /* 留出malloc的内存位置和ENV的内存大小 */
reserve_board, /* 留出bd所占用的内存大小(80字节) */
#endif
setup_machine, /* 对于i.mx6ul该函数无效 */
reserve_global_data, /* 留出gd_t结构的内存大小(248字节) */
reserve_fdt, /* 留出设备树的内存大小(i.mx6ul没有用) */
reserve_arch, /* 空函数 */
reserve_stacks, /* 留出栈空间(16字节)并做16字节对齐 */
setup_dram_config, /* 设置DRAM的信息 */
show_dram_config, /* 显示DRAM的位置 */
display_new_sp, /* 显示新的sp位置 */
INIT_FUNC_WATCHDOG_RESET
reloc_fdt, /* 重定位fdt(没有用) */
setup_reloc, /* 设置gd结构体的一些其他成员 */
NULL,
};

接下来,对上面的函数进行分析:

首先是setup_mon_len()函数,它的定义(去掉条件编译)如下:

static int setup_mon_len(void)
{
#if defined(__ARM__) || defined(__MICROBLAZE__)
gd->mon_len = (ulong)&__bss_end - (ulong)_start;
#endif
return ;
}

该函数用来设置gd结构中的mon_len成员变量,该变量用于保存uboot代码的长度。

接下来调用initf_malloc()函数,该函数的定义如下:

int initf_malloc(void)
{
#ifdef CONFIG_SYS_MALLOC_F_LEN
assert(gd->malloc_base); /* Set up by crt0.S */
gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN; /* malloc的大小0x400 */
gd->malloc_ptr = ;
#endif return ;
}

该函数用来设置gd结构中与malloc相关的变量,对于imx6ul的配置文件中,定义有CONFIG_SYS_MALLOC_F_LEN宏,该宏的值定义为0x400,malloc_limit变量表示malloc的内存大小。

接下来调用initf_console_record()函数,该函数的定义如下:

static int initf_console_record(void)
{
#if defined(CONFIG_CONSOLE_RECORD) && defined(CONFIG_SYS_MALLOC_F_LEN)
return console_record_init();
#else
return ;
#endif
}

对于该函数,由于imx6ul中没有定义CONFIG_CONSOLE_RECORD宏,所以该函数直接返回0。

接下来调用arch_cpu_init()函数,该函数用与初始化一些与arm架构相关的东西,继续调用initf_dm()函数,初始化一些与驱动模型相关的东西,arch_cpu_init_dm()函数没有实现,继续调用mark_bootstage()函数,该函数用于做某些标志。

接下来,判断CONFIG_BOARD_EARLY_INIT_F宏是否定义,对于imx6ul中的配置文件中,定义了该宏,因此,board_early_init_f()函数将会被调用,该函数的定义如下:

int board_early_init_f(void)
{
/* board相关的debug串口1引脚初始化 */
setup_iomux_uart(); return ;
}

对于imx6ul,该函数用来对SoC中的串口1引脚初始化,主要是完成相关引脚的复用配置和电气属性配置。

接下来,调用timer_init()函数来初始化ARM内核中的定时器。

继续往下运行,调用get_clocks()函数,该函数用于获取芯片内某些外设时钟。

然后调用env_init()函数,该函数用来初始化gd结构中和env相关的成员,设置gd结构中的env_addr成员,也就是环境变量的保存地址。

接下来,调用init_baud_rate()函数,该函数的定义如下:

/* 将gd中的baudrate成员进行初始化,获取"baudrate"环境变量值 */
static int init_baud_rate(void)
{
gd->baudrate = getenv_ulong("baudrate", , CONFIG_BAUDRATE);
return ;
}

该函数用于初始化debug调试串口的波特率,设置gd结构体中的baudrate成员,该值是从"baudrate"环境变量中读取的。

接下来,则是调用serial_init()函数,该函数的定义如下所示:

 /* 初始化串口 */
int serial_init(void)
{
gd->flags |= GD_FLG_SERIAL_READY;
return get_current()->start();
}

该函数用于对串口进行初始化,会调用串口驱动设备中注册的start()函数完成初始化。

函数console_init_f()将会设置gd结构体的have_console成员为1,标志则此时已经有了console,也就是上面已经初始化好的串口终端。

接下来,将会调用display_options()函数,函数的定义如下:

int display_options (void)
{
#if defined(BUILD_TAG)
printf ("\n\n%s, Build: %s\n\n", version_string, BUILD_TAG);
#else
printf ("\n\n%s\n\n", version_string);
#endif
return ;
}

该函数用来打印输出uboot版本的字符串,类似如下的字符串:

U-Boot 2016.03 (Jan   - :: +)

接下来调用display_text_info()函数,该函数用于显示一些debug调试信息,需要在板子的配置文件中定义宏DEBUG,才会开启uboot的调试信息输出。

函数继续往下运行,则会调用print_cpuinfo()函数,该函数输出板子上的CPU的相关信息,输出类似如下:

CPU:   Freescale i.MX6UL rev1.  MHz (running at  MHz)
CPU: Industrial temperature grade (-40C to 105C) at 37C
Reset cause: WDOG

由于在板子的配置文件中,定义了宏CONFIG_DISPLAY_BOARDINFO,因此show_board_info()函数将会被调用,该函数用于显示板子的相关信息,类似如下:

Board: MX6UL 14x14 COMP6UL

接下来,调用init_func_i2c()函数用于初始化I2C接口,函数定义如下:

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
static int init_func_i2c(void)
{
puts("I2C: ");
#ifdef CONFIG_SYS_I2C
i2c_init_all(); /* 对所有i2c接口进行初始化 */
#else
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
puts("ready\n");
return ;
}
#endif

函数调用完成后,将会在终端输出下面字符串:

I2C:   ready

程序继续往下运行,调用announce_dram_init()函数,该函数比较简单,只是输出"DRAM:   "字符串。

接下来,调用函数dram_init(),该函数并没有初始化DRAM,只是设置gd结构体中ram_size成员变量,也就是DRAM的大小,在comp6ul核心板中,DRAM的大小为256MB。

程序继续运行后,就是重点内容,后半部分留到下篇文章介绍,该函数的后半部分,将完成DRAM的内存分配,以及对gd结构的一些成员进行填充初始化。

3、小结

本篇文章主要是对board_init_f函数的前半部分进行了简要分析,在init_sequence_f函数初始化序列的前半部分,主要完成的功能有debug调试串口的初始化,以及一些调试输出。

Uboot启动流程分析(三)的更多相关文章

  1. imx6 uboot启动流程分析

    参考http://blog.csdn.net/skyflying2012/article/details/25804209 这里以imx6平台为例,分析uboot启动流程对于任何程序,入口函数是在链接 ...

  2. Uboot启动流程分析(二)

    1.前言 在前面的文章Uboot启动流程分析(一)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12000889.html 已经简单地分析了low_level_i ...

  3. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  4. Uboot启动流程分析(一)

    1.前言 Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从flash中拷贝到DDR中,最后启动Linux内核,uboot的 ...

  5. Uboot启动流程分析(转载)

    最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...

  6. 嵌入式Linux驱动学习之路(五)u-boot启动流程分析

    这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...

  7. u-boot启动流程分析(1)_平台相关部分

    转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...

  8. am335x uboot启动流程分析

    基本指令含义 .globl _start .globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号 b,bl b是不带返回的跳转  bl带返回的跳 ...

  9. U-BOOT启动流程分析--start.s(二)

    一.概述 u-boot的启动流程: 从文件层面上看主要流程是在两个文件中:cpu/arm920t/start.s,lib_arm/board.c, 先来分析start.s    在flash中执行的引 ...

随机推荐

  1. Linux常用命令之重启关机命令

    shutdown命令 shutdown命令用来系统关机命令.shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作. 实例 指定现在立即关机: shutdown -h now ...

  2. oracle学习笔记(十六) PL/SQL 异常和goto语句

    PL/SQL 异常和goto语句 异常 预定义异常 oracle常见预定义异常: 错误号 异常错误信息名称 说明 ORA-0001 DUP_VAL_ON_INDEX 试图破坏一个唯一性限制 ORA-0 ...

  3. sql server 列字段拼接 —— STUFF

    原始数据: sql语句 SELECT DISTINCT l.family_id, )) ,,'' ) isc_id FROM dbo.Addresses l 结果数据:

  4. C#A类派生类强转基类IL居然还是可以调用派生类中方法的例子

    大家都知道在C#中,如果B类继承自A类,如果一个对象是B类型的但是转换为A类型之后,这个对象是无法在调用属于B类型的方法的,如下例子: 基类A: public class A { } 派生类B: pu ...

  5. go实现整型的二进制转化

    go中已经实现了int->bin的转化函数,我这里只是化过程逻辑的实现,至于原理我就假设大家都知道了 本案例只考虑 int->bin  的转化 包含了正整数,负整数,0 的转化 packa ...

  6. 升鲜宝V2.0_杭州生鲜配送行业,条码标签管理之批量打印标签与分配配送任务相关操作说明_升鲜宝生鲜配送系统_15382353715_余东升

       升鲜宝V2.0_杭州生鲜配送行业,条码标签管理之批量打印标签与分配配送任务相关操作说明_升鲜宝供应链管理生鲜配送系统    题外话,随着国家对食材安全这个行业重视性越来越强,最近国家又出具了一些 ...

  7. PHP代码篇(七)--PHP及MySQL已经使用过的函数

    一.PHP常用函数 //数组转字符串 $str = implode(',',$device_string); //字符串转数组 $arr = explode(',',$device_string); ...

  8. bayaim_linux_install_oracle_11g - 20181102

    -- 2018-11-2 15:12:12— baipingyang -- bayaim-- bayaim_linux_install_oracle_11g: -------------------- ...

  9. Python—实现ssl认证

    https://blog.csdn.net/vip97yigang/article/details/84721027 https://www.cnblogs.com/lsdb/p/9397530.ht ...

  10. AtCoder - 2037 (dp)

    题意 https://vjudge.net/problem/AtCoder-2037 选一些数使得和的平均值等于a,问方案数. 思路 设dp[i][j]为选i个数和为j的方案数,如果当前选了x,那么d ...