第4章:U-Boot移植实战:获取源码、配置、编译、烧录到目标板
好,咱们进入正题。这一章是U-Boot移植的实操环节。说白了,就是要把U-Boot这个“引导程序”从源码变成能在你板子上跑起来的二进制文件,再把它烧进去。
我刚开始做移植的时候,总觉得这步很简单——不就是下载、配置、编译、烧录嘛。结果第一次就栽了跟头,板子直接变砖。嗯,后来才明白,每一步都有坑。今天我把这些坑都给你指出来。
4.1 获取U-Boot源码
源码从哪来?两个主流渠道:
- 官方主线仓库:
git://git.denx.de/u-boot.git。这是最干净的版本,但支持你的板子吗?不一定。 - 芯片厂商提供的分支:比如NXP、TI、Rockchip都有自己的U-Boot仓库。我建议你优先用这个,因为厂商已经帮你适配好了大部分外设。
我个人习惯用厂商分支做起点。为什么呢?因为主线版本虽然新,但很多板级支持文件(board support files)还没合进去。你想想看,自己从头写设备树和板级初始化,那工作量可不是一般的大。
下载命令很简单:
git clone https://github.com/your-soc-vendor/u-boot.git
cd u-boot
git checkout your-board-branch
4.2 配置U-Boot
配置这一步,说白了就是告诉U-Boot:“你要为哪块板子服务?”
U-Boot的配置系统基于Kconfig,跟Linux内核类似。你执行:
make <board_name>_defconfig
这个命令会生成一个.config文件。里面包含了所有配置选项。比如:
- CPU主频设置
- DDR时序参数
- 外设使能(网卡、串口、USB等)
- 环境变量存储位置
我遇到过最坑的事是什么?有一次我忘了配置DDR参数,直接用默认值。结果U-Boot能启动,但一加载内核就崩溃。查了两天才发现是DDR时序不对。所以,DDR配置一定要跟硬件设计一致。
配置完成后,你可以用make menuconfig微调。我个人习惯先跑一遍默认配置,再进去调整:
make menuconfig
这里有个小技巧:把CONFIG_BOOTDELAY设成0,可以跳过启动倒计时。调试阶段建议设成3秒,给自己留点反应时间。
4.3 编译U-Boot
编译前,先确认交叉编译工具链。我一般用:
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
然后直接:
make -j4
编译完成后,你会得到几个关键文件:
| 文件 | 说明 |
|---|---|
| u-boot.bin | 纯二进制镜像,直接烧录到Flash |
| u-boot.img | 带U-Boot头部的镜像,用于网络加载 |
| u-boot.srec | SREC格式,用于串口下载 |
| MLO / SPL | 第一级引导程序(如果芯片需要) |
嗯,这里要特别说一下SPL。很多现代SoC(比如TI的AM335x)需要两级引导:SPL先初始化DDR和时钟,再加载完整的U-Boot。我第一次做AM335x移植时,只烧了u-boot.bin,结果板子毫无反应。后来才发现,必须先把MLO(就是SPL)烧到特定位置。
4.4 烧录到目标板
烧录方法取决于你的板子支持什么。我列几种常见方式:
4.4.1 通过SD卡烧录
这是最常用的方法。把SD卡插入电脑,然后:
sudo dd if=u-boot.bin of=/dev/sdb bs=512 seek=1
注意seek=1这个参数。很多芯片要求U-Boot放在SD卡的第一个扇区之后(偏移512字节)。为什么?因为第一个扇区要留给分区表或者SPL。
我曾经犯过一个低级错误:用seek=0,结果把SD卡的分区表覆盖了。整张卡废掉。所以,烧录前一定确认偏移量。
4.4.2 通过JTAG/SWD烧录
如果板子还没启动过,或者Flash是空的,就得用调试器。我常用OpenOCD配合J-Link:
openocd -f interface/jlink.cfg -f target/your_soc.cfg
# 然后在OpenOCD命令行中:
flash write_image erase u-boot.bin 0x08000000
这里0x08000000是Flash的基地址。具体地址看芯片手册。我建议你第一次烧录时,先用flash probe 0确认Flash型号和大小。
4.4.3 通过网络烧录
如果板子上已经有U-Boot或者BootROM支持网络,可以用TFTP:
# 在板子的U-Boot命令行中:
setenv serverip 192.168.1.100
setenv ipaddr 192.168.1.10
tftp 0x82000000 u-boot.bin
sf probe 0
sf erase 0x0 0x80000
sf write 0x82000000 0x0 0x80000
这个流程是:先下载到内存,再擦除Flash,最后写入。注意sf erase的地址和大小一定要匹配你的Flash布局。
4.5 验证启动
烧录完成后,上电。通过串口看输出。如果一切正常,你会看到:
U-Boot 2023.04 (Apr 10 2024 - 15:30:00 +0800)
CPU: ARM Cortex-A7
DRAM: 512 MiB
MMC: mmc@48000000: 0
Net: eth0: ethernet@48400000
Hit any key to stop autoboot: 0
=>
看到=>提示符,说明U-Boot已经跑起来了。如果没看到输出,别慌。先检查:
- 串口波特率对不对(常见115200、57600)
- 串口引脚有没有接反(TX/RX交叉)
- 电源是否稳定
我遇到过最诡异的情况:串口有输出,但全是乱码。查了半天,发现是DDR初始化失败,导致U-Boot代码被破坏。重新检查DDR配置后,问题解决。
4.6 小结
这一章的内容,说白了就是三步:拿源码、配参数、烧进去。但每一步都有细节。我建议你:
- 先找厂商的参考板配置,别自己从头写
- 编译前确认工具链和架构
- 烧录前确认偏移量和Flash布局
- 第一次启动时,用串口盯着看输出
下一章我们会讲设备树的移植。设备树是U-Boot和内核之间的桥梁,搞不懂它,后面寸步难行。咱们下章见。