千家信息网

u-boot第二阶段启动流程是什么

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍"u-boot第二阶段启动流程是什么",在日常操作中,相信很多人在u-boot第二阶段启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"u-b
千家信息网最后更新 2026年11月03日u-boot第二阶段启动流程是什么

这篇文章主要介绍"u-boot第二阶段启动流程是什么",在日常操作中,相信很多人在u-boot第二阶段启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"u-boot第二阶段启动流程是什么"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

tq2440的uboot目录有点繁琐. 用nor中的uboot下载uboot到nand中可以使用tftp方式. 1.连接开发板与pc 2.选择tftp下载

3. 设置pc与开发板同一个网段

之后在pc上安装tftp server软件. 然后在uboot的tftp下载模式下输入 1. 因为tq2440的uboot源码写死了通过tftp下载的uboot文件名称为 u-boot.bin, 所以将天嵌提供的uboot改名为 u-boot.bin 放到 tftp工具的同级目录里即可自动完成下载, 下载前最好先格式化nand.

个人感觉uboot第一阶段就是为了芯片能够运行起来保证最最低的运行要求, 第二阶段才是真正干活的代码.

第二阶段总体概览

uboot启动的第二阶段流程大致如图所示:

进入 start_armboot 之后首先为 gd , gd->bd 安排存储空间, 然后将该空间清0.

一 init_sequence中的函数

1. board_init
int board_init (void){        S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//获取S3C24X0_GPIO_BASE, GPIO寄存器的基地址        /* set up the I/O ports */        gpio->GPACON = 0x007FFFFF;        ... ...        gpio->GPJUP = 0x00001fff;        /* arch number of TQ2440-Board */        gd->bd->bi_arch_number = MACH_TYPE_S3C2440;        /* adress of boot parameters */        gd->bd->bi_boot_params = 0x30000100;        //机器ID和ram中taglist的基地址在do_boot_linux中作为参数传递给kernel, [theKernel(0, machid, bd->bi_boot_params)]                /* 开启 指令/数据 cache*/        icache_enable();        dcache_enable();        return 0;}
2. interrupt_init
int interrupt_init (void){        S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();        /* use PWM Timer 4 because it has no output */        /* prescaler for Timer 4 is 16 */        timers->TCFG0 = 0x0f00;        if (timer_load_val == 0)        {            /* for 10 ms clock period @ PCLK with 4 bit divider = 1/2 (default) and prescaler = 16. Should be 10390 @33.25MHz and 15625 @ 50 MHz */            timer_load_val = get_PCLK()/(2 * 16 * 100);        }        /* load value for 10 ms timeout */        lastdec = timers->TCNTB4 = timer_load_val;        /* auto load, manual update of Timer 4 */        timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;        /* auto load, start Timer 4 , 自动重装*/        timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;        timestamp = 0;        return (0);}

(1) 预分频值计算

这里使用没有输出引脚的定时器 TIM4, 定时 10ms, 自动重装.

TCFG0              Bit      Description             Prescaler 1       [15:8]    These 8 bits determine prescaler value for Timer 2, 3 and 4.    Prescaler 0       [7:0]     These 8 bits determine prescaler value for Timer 0 and 1.        0x00

TCFG0=0x0F00, 预分频值Prescaler 1 = 1/16;

TCFG1=0x0000, 分频值 1/2 (默认值0000为2分频)

(2) 定时器初值计算
timer_load_val = get_PCLK()/(2 * 16 * 100);

get_PCLK()位于cpu/arm920t/s3c24x0/speed.c 中,取得pclk的过程如下

FCLK => HCLK = FCLK / 2 => PCLK = HCLK / 2, FCLK : HCLK : PCLK = 4 : 2 : 1

即先通过MPLLCON的值计算得出FCLK,然后根据CLKDIVN的分频率比得出HCLK 和 PCLK. 要做一个10ms的定时器,所以这里计算初值。上面prescaler1 = 16, 且此处没有设置TCFG1,所以TCFG1为默认值0, 则divider选择了1/2; 1s = 10ms * 100;

于是,需要的三个参数全都出来了, 分频后频率为 (clk /(2 * 16)),即每秒要做(clk/(2 * 16))次减计数,那么10ms就要做 clk / (2 * 16 * 100)次减计数。

(3) TCNTB4
lastdec = timers->TCNTB4 = timer_load_val;

将定时器计数初值保存在 TCNTB4 中, 在减计数完成时自动将 TCNTB4 赋值给 TCNT4

(4) TCON
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000; // ==> bit[22:20] = 110    auto reload mode, update TCNTB4timers->TCON = (timers->TCON & ~0x0700000) | 0x500000; // ==> bit[22:20] = 101    auto reload mode, start for timer4 此时,真正开始定时器4

后期版本的uboot中的interrupt_init貌似更新为timer_init了

3. env_init

环境变量应该是如果在flash里有环境变量那就使用flash里的, 如果没有就使用默认的环境变量.

这个函数在多个文件中定义, 但是每个文件开头都有一个宏定义来将整个文件包含起来, 如在env_nand.c中就有

#if defined(CFG_ENV_IS_IN_NAND)     /* Environment is in Nand Flash */

在 include/configs/smdk2440.h(EmbedSky.h)中如果定义了 CFG_ENV_IS_IN_NAND 就表示参数保存在nand中.

int env_init(){    gd->env_addr  = (ulong)&default_environment[0];    gd->env_valid = 1;}

将默认的环境变量数组地址赋值给gd->env_addr;

default_environment[]定义在 common/env_common.c

4. init_baudrate

getenv_r ("baudrate", tmp, sizeof (tmp)); 这里是对应上一步的 env_init, 如果设置了环境变量就依据 env_ptr 去寻找 "baudrate">

5. serial_init
/* * 使用给定的 波特率 初始化串口. 始终设置为 8个数据位  无校验 1个停止位 无开始位 */int serial_init (void){        serial_setbrg ();        return (0);}void serial_setbrg (void){        S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);        int i;        unsigned int reg = 0;        /* value is calculated so : (int)(PCLK/16./baudrate) -1 , 这里是为了计算波特率寄存器的值: RBRDIV*/        reg = get_PCLK() / (16 * gd->baudrate) - 1;        /* FIFO enable, Tx/Rx FIFO clear */        uart->UFCON = 0x07;        uart->UMCON = 0x0;        /* Normal,No parity,1 stop,8 bit */        uart->ULCON = 0x3;        /*         * tx=level,rx=edge,disable timeout int.,enable rx error int.,         * normal,interrupt or polling         */        uart->UCON = 0x245;        uart->UBRDIV = reg;#ifdef CONFIG_HWFLOW        uart->UMCON = 0x1; /* RTS up */#endif        for (i = 0; i < 100; i++);}

使用上一步获取到的波特率来初始化串口, 串口默认设置为 8 N 1 无.

6. console_init_f

gd->have_console = 1; 等于设置了一个标志位, 表示uboot使用命令终端.

7. display_banner & print_cpuinfo

这两个函数都是调试使用的, 他们打印出调试信息, 这里要注意的是打印函数, printf ; 在/common/console.c中它调用puts来实现输出

void puts (const char *s){    //GD_FLG_DEVINIT表示了是向自己还是串口输出    if (gd->flags & GD_FLG_DEVINIT) {        /* Send to the standard output */        fputs (stdout, s);    } else {        /* Send directly to the handler */        serial_puts (s);    }}
8. dram_init
首先要在 /include/configs/smdk2440.h(embedsky.h)中定义:#define CONFIG_NR_DRAM_BANKS               1          /* 只有一块sdram */#define PHYS_SDRAM_1                       0x30000000 /* SDRAM Bank #1 起始地址*/#define PHYS_SDRAM_1_SIZE          0x08000000 /* 128 MB */int dram_init (void){        gd->bd->bi_dram[0].start = PHYS_SDRAM_1;        //bi_dram[0] 表示了 dram 起始地址和大小        gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;        return 0;}

最后总结一下最重要的两个结构体:

(1) /include/asm-arm/global_data.h 中的 gd_t , 其中保存了 u-boot 的配置信息

typedef     struct  global_data {        bd_t            *bd;        unsigned long      flags;        unsigned long      baudrate;        unsigned long      have_console;   /* serial_init() was called */        unsigned long      reloc_off;      /* Relocation Offset */        unsigned long      env_addr;       /* Address  of Environment struct */        unsigned long      env_valid;      /* Checksum of Environment valid? */        unsigned long      fb_base;        /* base address of frame buffer */        void            **jt;           /* jump table */} gd_t;

(2) /include/asm-arm/u-boot.h 中的 bd_t

typedef struct bd_info {    int                     bi_baudrate;    /* serial console baudrate */    unsigned long      bi_ip_addr;     /* IP Address */    unsigned char      bi_enetaddr[6]; /* Ethernet adress */    struct environment_s *bi_env;    //下面两个参数在进入内核时候会作为参数 r1 r2 传递给内核启动函数    ulong           bi_arch_number; /* unique id for this board , 目标板id */    ulong           bi_boot_params; /* where this board expects params , taglist的地址*/       struct                          /* RAM configuration */    {        ulong start;        ulong size;    }                  bi_dram[CONFIG_NR_DRAM_BANKS];} bd_t;

在/include /configs/smdk2440.h(embedsky.h) 中定义的 CFG_GBL_DATA_SIZE 为 128 , gd_t 和 bd_t 都保存在该区域中.

二 以上为 init_sequence 中的函数, 下边为 start_armboot 中接下来的代码解析:

flash_init

由内部的代码可知这里是norflash的初始化

#define PHYS_FLASH_1    0x00000000flashbase = PHYS_FLASH_1;   //因为cpu从0地址处开始执行代码, 2440在0地址处只可能有nor-flash和内部4k的sram, 所以这里是nor-flash的初始化无疑

该函数中初始化了nor-flash的各个sector起始地址和大小, 在display_flash_config中打印出了flash的配置信息

显示配置
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);    //计算 framebuffer内存地址size = lcd_setmem (addr);                                  //设置 framebuffer尺寸大小gd->fb_base = addr;                                        //设置 framebuffer起始地址mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);        //将 堆区(malloc) 全部清零
初始化nand

注意要在 /include/configs/smdk2440.h(embedsky.h) 中打开 CONFIG_COMMANDS 和 CFG_CMD_NAND 对 nand 的支持

    nand_init();
初始化环境变量参数
/* initialize environment */env_relocate ();   //初始化环境变量
设置ip地址
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
从网卡读出mac地址
/* MAC Address */                                                                                                                //从网卡寄存器读出mac地址        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;//这里问号表达式的优先级高于 =  即:先判断s是否为真, 根据结果再赋值                if (s)                                                                                                                             s = (*e) ? e + 1 : e;        }
开发板上的设备初始化
devices_init();   //初始化 iic/lcd/video/logbuff/system/serial等设备
初始化跳转表

jumptable_init(); //初始化跳转表

初始化控制台

console_init_r();

打开中断

enable_interrupt();

保存"加载地址"
/* Initialize from environment */if ((s = getenv ("loadaddr")) != NULL){        load_addr = simple_strtoul (s, NULL, 16);}

查找环境变量里是否含有 load_addr , 有的话将他的值写入到 load_addr 中, 他是加载地址, 在bootm中还会见到他. 他表示uboot将kernel加载到内存的地址.

保存framebuffer
if ((s = getenv ("bootfile")) != NULL) {        copy_filename (BootFile, s, sizeof (BootFile));             //保存framebuffer}
开发板初始化
board_late_init ();        //因为每个板子都不一样, 板子特殊的初始化可以写在这个函数里
网卡信息打印

eth_initialize

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

截止目前还没有讲到uboot如何启动linux, 而这最关键的一部分代码就在main_loop中.

main_loop中主要是完成了 1. 设置启动次数, 有些项目需要检测设备的启动次数, 如果大于某值则不予启动 2. modem功能 3. uboot版本号 4. 命令自动补全功能(类似shell的自动补全命令) 5. 启用倒计时启动功能 6. 进入死循环 执行 read_line(), 读取控制台(一般都是串口)输入的命令. 执行命令 run_command() 如果想要知道如何解析命令可以去跟踪一下 run_command() 函数 7. 启动linux

可见main_loop函数的重要性, 我们在下一篇中继续分析main_loop函数

到此,关于"u-boot第二阶段启动流程是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

地址 函数 阶段 变量 环境 参数 命令 流程 串口 代码 定时器 信息 文件 学习 开发 起始 两个 初值 功能 大小 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 南京欣网互联网络科技有限公司 美国互联网保险保险科技 厦门中拓互联网络科技有限公司 互联网科技的发展 深圳齐家互联网科技 苏州互联网科技贷款公司 天扬科技互联网广告 中国互联网金融科技基金 科技与互联网的利与弊 广州慧亚互联网科技怎么样 黑科技闪耀互联网之光 互联网科技公司有风险吗 未来五年的互联网科技 广西互联网科技孵化中心 黑科技 体验场景 互联网大会 卡尔迅互联网科技有限公司 2021世界互联网领先科技成果发布 九思连强互联网电子科技有限公司 10位科技互联网大佬 互联网科技创业有什么好项目 深圳市人人互联网科技有限公司 伟宏互联网科技 金融科技还是互联网 华中科技大学互联网 广州凡科互联网科技 新华互联网科技2019 互联网数据科技专业 科技赋能视角下互联网保险发展 世界互联网科技成果奖 高科技和互联网的现代国家
0