3、U-Boot编译与烧录:交叉编译工具链配置、make命令详解、烧录到NAND/NOR/MMC/SD卡
这一章,我们聊聊U-Boot的编译和烧录。说白了,就是把源码变成能在目标板上跑起来的二进制文件,然后塞进存储介质里。
很多新手觉得这步简单,不就是make一下嘛?但我在项目里见过太多次,卡在工具链不对、配置没选好、烧录地址写错这些坑上。嗯,咱们一个一个来拆解。
本章核心脉络:交叉编译环境搭建 → 配置选择 → make构建 → 镜像烧录到不同介质。
3.1 交叉编译工具链配置
U-Boot不是在PC的x86上跑的,它要运行在ARM、RISC-V、MIPS这些架构上。所以你得用交叉编译器——在PC上编译,生成目标板的机器码。
我个人习惯把工具链放在/opt/toolchains/下,然后加到PATH里。举个例子,ARM Cortex-A系列常用的是arm-linux-gnueabihf-前缀:
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
这里有两个关键变量:
- CROSS_COMPILE:指定交叉编译器的前缀。make会用它拼接出
gcc、ld、objcopy等工具名。 - ARCH:指定目标CPU架构。U-Boot的配置系统和链接脚本依赖这个变量。
我的经验:如果你用的是Linaro或ARM官方工具链,记得检查readelf -A输出的浮点类型。我曾经因为工具链是软浮点而板子是硬浮点,折腾了一整天。嗯,血的教训。
你也可以把这两个export写到~/.bashrc里,省得每次手动敲。但我建议在项目目录下放一个setenv.sh脚本,按项目隔离,更干净。
3.2 make命令详解
U-Boot的构建系统基于Kbuild,跟Linux内核同源。你想想看,这意味着什么?配置、编译、清理的套路几乎一样。
3.2.1 配置目标板
每个板子都有一个默认配置,存放在configs/目录下。比如:
ls configs/ | grep stm32mp
make stm32mp157d_atk_defconfig
执行后,.config文件就生成了。你可以用make menuconfig微调,跟内核配置一模一样。
注意:make xxx_defconfig只生成默认配置。如果你改了.config,记得make savedefconfig保存,否则下次make distclean就全丢了。
3.2.2 编译
配置搞定后,直接开编:
make -j$(nproc)
-j参数指定并行任务数,$(nproc)自动获取CPU核心数。我第一次编译U-Boot时没加-j,看着单核编译慢得想睡觉。
编译产物主要有:
| 文件 | 说明 |
|---|---|
| u-boot.bin | 纯二进制镜像,不带任何头部,直接烧录到偏移0处 |
| u-boot.img | 带U-Boot头部(含CRC、加载地址、入口地址),通常用于网络启动 |
| u-boot-spl.bin | SPL(Secondary Program Loader),用于启动ROM加载U-Boot主体 |
| u-boot.itb | FIT(Flattened Image Tree)格式,包含多个镜像和DTB |
你可能会问:到底烧哪个?这取决于你的启动流程。如果SoC内部ROM直接加载U-Boot,用u-boot.bin;如果ROM先加载SPL,再由SPL加载U-Boot,那就需要u-boot-spl.bin和u-boot.img。
3.2.3 清理
make clean # 删除编译产物,保留.config
make distclean # 全部清理,包括.config
我习惯在切换板型时用make distclean,避免旧配置污染新编译。
3.3 烧录到不同存储介质
编译出镜像只是第一步,把它烧到板子上才是真功夫。不同介质,烧录方式天差地别。
3.3.1 烧录到NAND Flash
NAND有坏块问题,烧录时必须处理ECC和OOB。我常用的方法是:
- 通过U-Boot的
nand write命令烧录。 - 或者用
flash_erase+nandwrite工具(在Linux用户空间)。
# 在U-Boot命令行中
tftp 0x82000000 u-boot.bin
nand erase 0x0 0x100000
nand write 0x82000000 0x0 0x100000
注意:NAND的擦除块大小通常是128KB或256KB。烧录前一定要确认你的镜像大小不超过分区大小。我曾经因为镜像比分区大,烧到一半卡死,板子直接变砖。
3.3.2 烧录到NOR Flash
NOR支持XIP(片上执行),烧录相对简单,没有坏块问题。但NOR的写入速度慢,而且通常需要先擦除再写。
# 使用flashrom工具(PC端)
flashrom -p ch341a_spi -w u-boot.bin
# 或在U-Boot中
sf probe 0
sf erase 0x0 0x100000
sf write 0x82000000 0x0 0x100000
NOR的地址空间是线性的,烧录偏移就是运行地址。比如你的U-Boot链接地址是0x60000000,那烧录偏移就是0x0(如果NOR映射到0x60000000)。
3.3.3 烧录到MMC/eMMC
MMC和eMMC是块设备,烧录方式跟硬盘类似。通常用dd命令:
# 在Linux主机上,通过读卡器或USB烧录
sudo dd if=u-boot.bin of=/dev/sdb bs=512 seek=2
# 或在U-Boot中
mmc dev 0
fatload mmc 0:1 0x82000000 u-boot.bin
mmc write 0x82000000 0x40 0x800
这里seek=2或0x40是什么意思?很多SoC的启动ROM会跳过前几个扇区(比如前1KB用于存放分区表或MBR),U-Boot从第2个扇区开始烧。
我的习惯:烧录eMMC前,先用mmc part查看分区表。有些板子的boot分区是独立的,需要mmc dev 0 1切换到boot分区再烧。
3.3.4 烧录到SD卡
SD卡跟MMC本质上是一回事,烧录方法几乎一样。区别在于SD卡通常用于开发阶段,方便插拔。
# 在PC上烧录SD卡
sudo dd if=u-boot.bin of=/dev/sdc bs=1k seek=1
sync
注意:seek=1表示跳过第1个1KB块。不同SoC的偏移要求不同,一定要查数据手册。我记得有一次帮同事调试,他烧到偏移0,结果板子死活不启动——后来发现该SoC要求U-Boot放在偏移8KB处。
3.4 烧录后的验证
烧完别急着断电。我建议做两步验证:
- 校验:用
md5sum或sha1sum对比原始镜像和烧录后的内容。 - 启动测试:通过串口观察U-Boot的启动日志,确认打印正常。
# 在U-Boot中校验NAND内容
nand dump 0x0 0x100000
# 对比md5值
如果启动日志卡在Starting kernel ...,别慌。先检查DTB地址对不对,再看内核镜像格式。嗯,这些坑我们后面章节会细聊。
一句话总结:交叉编译工具链配好环境变量,make三步走(配置→编译→烧录),不同介质注意偏移和块大小。烧录后务必验证,别让板子变砖。