4、U-Boot移植基础:U-Boot简介、T113 U-Boot源码结构、U-Boot配置与编译、U-Boot启动流程分析
好,我们进入第四章。这一章是整个移植工作的基石——U-Boot。说白了,U-Boot就是嵌入式系统的“开机引导程序”。你想想看,芯片上电后,内存都还没初始化,怎么加载Linux内核?这时候就需要U-Boot来干这些脏活累活。
我个人习惯把U-Boot比作“硬件初始化总管”。它负责把CPU、内存、串口、网卡这些基本外设先跑起来,然后才把控制权交给内核。没有它,你的T113就是一坨废铁。
4.1 U-Boot简介
U-Boot的全称是“Universal Boot Loader”,通用引导加载程序。它支持几乎所有主流的嵌入式处理器架构,包括ARM、RISC-V、MIPS等。T113用的是ARM Cortex-A7,所以U-Boot对它来说是小菜一碟。
我在项目中遇到过一个问题:某次客户要求快速启动,从按下电源键到显示Logo不能超过2秒。嗯,这就要在U-Boot阶段做大量优化。说白了,U-Boot的启动速度直接决定了用户体验。
U-Boot的核心功能包括:
- 硬件初始化:时钟、DDR、串口、存储控制器等
- 引导内核:从Flash、SD卡或网络加载内核镜像
- 提供命令行:方便调试和测试硬件
- 环境变量管理:存储启动参数和配置
重要概念:U-Boot分为两个阶段——SPL(Secondary Program Loader)和U-Boot proper。SPL是精简版,负责初始化DDR并加载完整的U-Boot。T113的启动流程就是先跑SPL,再跑U-Boot proper。
4.2 T113 U-Boot源码结构
拿到全志官方提供的U-Boot源码后,第一件事就是看目录结构。我建议你先用 tree -L 2 命令扫一眼,心里有个底。
核心目录如下:
| 目录/文件 | 说明 |
|---|---|
arch/arm/ |
ARM架构相关代码,包括CPU启动、异常处理等 |
board/sunxi/ |
全志系列板级支持包,T113的代码就在这里 |
drivers/ |
各种外设驱动,如MMC、NAND、以太网等 |
include/configs/ |
板级配置文件,比如 sun8i.h 或 t113.h |
common/ |
U-Boot通用功能,如命令行解析、环境变量处理 |
你可能会问:“这么多代码,我该从哪里下手?”我的经验是——先看 board/sunxi/ 和 include/configs/。这两个目录直接决定了你的板子能不能跑起来。
举个例子,T113的板级初始化文件通常在 board/sunxi/board.c 里。这里面定义了GPIO初始化、时钟配置、DDR参数等。我曾经在调试一块新板子时,发现串口死活没输出,最后定位到是 board.c 里的GPIO复用配置错了。嗯,这种坑踩过一次就记住了。
小技巧:如果你要移植到自己的板子上,建议直接复制一份全志官方的板级目录,然后修改里面的文件名和配置。这样能省去很多重复工作。
4.3 U-Boot配置与编译
配置和编译U-Boot,说白了就是三步走:配置、编译、烧录。但每一步都有讲究。
第一步:配置
全志T113的配置命令通常是:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- t113_defconfig
这里的 t113_defconfig 是默认配置文件,位于 configs/ 目录下。如果你用的是自己的板子,可以基于这个文件修改。
我个人习惯在配置后执行 make menuconfig 进行微调。比如关闭不需要的驱动、调整DDR频率等。你想想看,默认配置里可能打开了十几个你用不到的驱动,编译出来体积大不说,启动还慢。
第二步:编译
编译命令很简单:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
这里 -j4 表示用4个线程并行编译。我建议根据你的CPU核心数来设置,比如8核CPU就用 -j8。
编译完成后,你会得到两个关键文件:
u-boot-sunxi-with-spl.bin:包含SPL和U-Boot proper的完整镜像u-boot.bin:仅U-Boot proper
注意:T113的启动要求SPL必须放在SD卡或NAND的特定偏移位置。比如SD卡启动时,SPL要放在8KB偏移处。这个参数在 include/configs/sunxi-common.h 里定义。
第三步:烧录
烧录到SD卡的命令示例:
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8
这里的 seek=8 表示从第8个扇区(8KB)开始写入。我曾经因为忘记加 seek 参数,直接把U-Boot写到了SD卡的开头,结果分区表被覆盖了...嗯,那次折腾了一下午才恢复数据。
4.4 U-Boot启动流程分析
理解启动流程,是移植工作的重中之重。你想想看,如果不知道芯片上电后先执行哪段代码,出了问题怎么定位?
T113的U-Boot启动流程大致如下:
- ROM代码:芯片上电后,固化在ROM里的代码先执行。它会初始化基本的时钟和SRAM,然后从启动介质(SD卡、NAND等)加载SPL到SRAM中。
- SPL阶段:SPL被加载到SRAM后开始执行。它的主要任务是初始化DDR控制器,然后从启动介质加载完整的U-Boot到DDR中。
- U-Boot proper阶段:完整的U-Boot在DDR中运行。它会初始化更多外设(如网卡、USB),解析环境变量,最后加载并启动内核。
具体到代码层面,启动入口在 arch/arm/cpu/armv7/start.S。这个汇编文件里定义了中断向量表、CPU模式切换等。我记得第一次看这个文件时,满屏的汇编指令看得头晕。但后来发现,其实只需要关注几个关键点:
_start标签:程序入口lowlevel_init:低层初始化,包括关闭看门狗、设置栈指针board_init_f:板级初始化前阶段,主要初始化DDRboard_init_r:板级初始化后阶段,初始化外设并进入命令行
关键点:SPL和U-Boot proper的分界线在 board_init_f 和 board_init_r 之间。SPL执行完 board_init_f 后,会跳转到U-Boot proper的 board_init_r。这个跳转过程涉及重定位,也就是把U-Boot代码从加载地址复制到运行地址。
我在调试启动流程时,习惯在关键函数入口加串口打印。比如在 board_init_f 开头打印一行 "Enter board_init_f\n"。这样就能知道程序跑到了哪个阶段。如果串口没输出,说明连SPL都没跑起来,问题出在ROM加载阶段。
最后,我想强调一点:U-Boot的启动流程虽然复杂,但核心逻辑是固定的。你只要掌握了SPL和U-Boot proper的分工,以及重定位的原理,剩下的就是根据具体硬件调整参数。说白了,移植U-Boot就是“配置+调试”的循环,耐心一点,总能跑起来。