4. 编译与烧录:从源码到板子跑起来

好,到了真正动手的环节了。前面几章我们把 U-Boot 的启动流程、参数结构都捋了一遍,但光看不练是学不会的。这一章,我们就来走一遍完整的流程:下载源码、配交叉编译环境、配置、编译、烧录。每一步我都会把坑点指出来,这些都是我实际项目中踩过的。

本章核心流程:源码获取 → 交叉工具链安装 → defconfig 配置 → make 编译 → 烧录到目标存储介质

U-Boot 编译与烧录全流程 下载 U-Boot 源码 配置交叉编译环境 make xxx_defconfig make 编译 烧录到 SD 卡 / NAND 💡 关键点:defconfig 决定板级配置,交叉编译链决定能否生成目标 CPU 的机器码

4.1 下载 U-Boot 源码

U-Boot 源码托管在 Denx 的 GitLab 上。我个人习惯直接从官方仓库拉取,这样能保证是最新、最干净的版本。

# 克隆主线仓库
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot

# 或者下载稳定版 tarball(推荐新手用)
wget https://source.denx.de/u-boot/u-boot/-/archive/v2024.01/u-boot-v2024.01.tar.bz2
tar -xjf u-boot-v2024.01.tar.bz2
cd u-boot-v2024.01

我的建议:新手先用稳定版 tag,比如 v2024.01 或 v2023.10。主线分支可能包含未稳定化的代码,我在项目中遇到过因为用了最新主线导致编译失败的情况,排查了半天才发现是某个驱动提交有问题。

4.2 配置交叉编译环境

说白了,交叉编译就是在你的 PC(x86)上生成目标板(ARM/RISC-V/MIPS)能跑的机器码。没有交叉工具链,你编译出来的东西板子根本不认识。

4.2.1 安装交叉编译工具链

不同架构用的工具链不一样。我列几个常用的:

目标架构 工具链前缀 安装方式(Ubuntu/Debian)
ARM 32-bit arm-linux-gnueabihf- sudo apt install gcc-arm-linux-gnueabihf
AArch64 (ARM64) aarch64-linux-gnu- sudo apt install gcc-aarch64-linux-gnu
RISC-V 64-bit riscv64-linux-gnu- sudo apt install gcc-riscv64-linux-gnu

安装完后,验证一下:

arm-linux-gnueabihf-gcc --version
# 应该能看到版本信息,而不是 "command not found"

我曾经踩过的坑:有次我装的是 gcc-arm-none-eabi(用于裸机开发),用它来编译 U-Boot,结果链接阶段报了一堆错。U-Boot 需要的是 Linux 交叉工具链(带 linux 字样的),因为 U-Boot 依赖 glibc 的某些启动代码。记住:U-Boot 用 arm-linux-gnueabihf-,不是 arm-none-eabi-。

4.2.2 设置环境变量

我个人习惯把工具链路径加到 ~/.bashrc 里,这样每次打开终端就不用重新设了:

export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

然后 source ~/.bashrc 使其生效。你也可以在每次 make 时临时指定:

make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm

嗯,这里要注意:CROSS_COMPILE 后面一定要带短横 -,否则工具链找不到。

4.3 make xxx_defconfig

U-Boot 支持几百种开发板和 SoC。你不可能手动配所有选项,所以官方提供了现成的默认配置。配置文件的命名规则一般是:

<板子名称>_defconfig

比如:

  • qemu_arm_defconfig — QEMU 模拟的 ARM 平台
  • raspberry_pi_3_defconfig — 树莓派 3
  • am335x_evm_defconfig — TI AM335x 评估板

怎么找到你的板子?

ls configs/ | grep -i "你的板子关键词"

比如我手头有块 i.MX6ULL 的板子:

ls configs/ | grep -i "mx6ull"
# 输出:mx6ull_14x14_evk_defconfig

然后执行:

make mx6ull_14x14_evk_defconfig

这一步会生成 .config 文件,里面包含了所有配置项。你可以用 make menuconfig 进一步微调,比如开启某个驱动、调整启动参数等。

小技巧:如果你用的是自己做的板子,没有现成的 defconfig,可以找一个最接近的官方板子配置,然后通过 make menuconfig 修改。我在项目中就是这么干的——拿官方的 EVK 配置做底,改掉 DDR 参数和网口配置,省了不少事。

4.4 make 编译

配置完,直接 make 就行:

make -j$(nproc)

-j$(nproc) 表示用所有 CPU 核心并行编译,能快不少。编译完成后,你会看到几个关键产物:

文件 说明
u-boot.bin 纯二进制镜像,直接烧录到偏移地址
u-boot.img 带 U-Boot 头部信息的镜像,用于某些启动方式
u-boot-dtb.bin U-Boot 和设备树打包在一起的镜像
u-boot-spl.bin SPL(第一阶段引导加载器),用于支持 SPL 的 SoC

注意:如果你的板子使用 SPL(比如 AM335x、i.MX6ULL),那么烧录时通常需要烧 u-boot-spl.bin 和 u-boot.img 两个文件,分别放到不同的偏移位置。只烧 u-boot.bin 是起不来的。我曾经在这个问题上浪费了半天,后来看了芯片手册才明白。

4.5 烧录到 SD 卡 / NAND

编译完了,怎么把镜像弄到板子上?常见的方式有两种:SD 卡烧录 和 NAND/NOR Flash 烧录。

4.5.1 烧录到 SD 卡

这是最常用的方式,尤其适合开发阶段。把 SD 卡插入电脑,先确认设备名:

lsblk
# 假设 SD 卡是 /dev/sdb

然后直接 dd 写进去:

# 对于不带 SPL 的板子
sudo dd if=u-boot.bin of=/dev/sdb bs=1k seek=1

# 对于带 SPL 的板子(比如 AM335x)
sudo dd if=u-boot-spl.bin of=/dev/sdb bs=1k seek=1
sudo dd if=u-boot.img of=/dev/sdb bs=1k seek=69

seek 参数表示跳过多少块,这个偏移量由 SoC 的启动 ROM 决定。比如 i.MX6ULL 要求 U-Boot 放在 SD 卡的第 1KB 偏移处,所以 seek=1(bs=1k 时,1 块 = 1KB)。

关键点:不同 SoC 的偏移地址不一样。烧录前一定要查芯片手册或官方文档,确认正确的偏移值。烧错位置,板子根本不会加载 U-Boot。

4.5.2 烧录到 NAND Flash

NAND 烧录稍微复杂一些,通常需要先通过某种方式(比如串口、网络)把 U-Boot 加载到内存,然后用 U-Boot 自己的命令写到 NAND:

# 在 U-Boot 命令行中
tftp 0x82000000 u-boot.bin          # 从 TFTP 服务器下载到内存
nand erase 0x0 0x80000              # 擦除 NAND 前 512KB
nand write 0x82000000 0x0 0x80000   # 写入 NAND

如果板子还没 U-Boot,那就得用 JTAG/SWD 调试器 或者 SD 卡启动 先把 U-Boot 烧进去,再用 U-Boot 烧 NAND。说白了,NAND 烧录是个「先有鸡还是先有蛋」的问题,第一次烧录往往需要借助其他手段。

我的经验:开发阶段我强烈建议用 SD 卡启动。SD 卡烧录快、改起来方便,大不了换张卡重烧。NAND 烧录慢,而且擦写次数有限,不适合频繁调试。等 U-Boot 完全调通了,再考虑烧到 NAND 里做量产。

4.6 验证烧录结果

烧完后,把 SD 卡插到板子上,上电,看串口输出。如果看到类似这样的信息,说明 U-Boot 跑起来了:

U-Boot 2024.01 (Jan 15 2025 - 10:30:00 +0800)

CPU:   i.MX6ULL 792MHz
Board: MYBOARD
DRAM:  512 MiB
MMC:   FSL_SDHC: 0
In:    serial
Out:   serial
Err:   serial
Net:   eth0: ethernet@2188000
Hit any key to stop autoboot:  0
=>

如果串口没输出,别慌。先检查:

  • 串口线接对了没?波特率设对了没?(通常是 115200)
  • 烧录的偏移地址对不对?
  • SD 卡格式化了没?(U-Boot 不需要文件系统,但有些板子要求 SD 卡是 MBR 分区表)

我曾经遇到的情况:有次烧完 SD 卡,板子死活没输出。折腾了两小时,最后发现是 SD 卡插槽接触不良。换个卡槽就好了。所以,硬件问题也要考虑进去,别一味怀疑软件。

好,这一章的内容就到这。从下载源码到板子跑出 U-Boot 提示符,每一步都有它的门道。你按这个流程走一遍,基本就能把 U-Boot 编译烧录这件事拿下了。