Uboot啟動流程分析(三)


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 = 0;    /* 是否具有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 0;
}

該函數用來設置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 = 0;
#endif

    return 0;
}

該函數用來設置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 0;
#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 0;
}

對於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", 10, CONFIG_BAUDRATE);
    return 0;
}

該函數用於初始化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 0;
}

該函數用來打印輸出uboot版本的字符串,類似如下的字符串:

U-Boot 2016.03 (Jan 02 2020 - 20:50:58 +0800)

接下來調用display_text_info()函數,該函數用於顯示一些debug調試信息,需要在板子的配置文件中定義宏DEBUG,才會開啟uboot的調試信息輸出。

函數繼續往下運行,則會調用print_cpuinfo()函數,該函數輸出板子上的CPU的相關信息,輸出類似如下:

CPU:   Freescale i.MX6UL rev1.0 528 MHz (running at 396 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 0;
}
#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調試串口的初始化,以及一些調試輸出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM