2、U-Boot源码结构:顶层目录、board、arch、common、drivers等关键目录解析
拿到一份U-Boot源码,第一件事是什么?我个人的习惯是——先看目录结构。别急着编译,也别急着改代码。先把家底摸清楚,后面移植的时候才能少走弯路。
U-Boot的源码目录,说白了就是一套分层解耦的设计。顶层负责调度,arch管CPU架构,board管具体板子,drivers管外设。每一层各司其职,互不干扰。你想想看,如果所有代码都堆在一个目录里,那移植一个新板子得改多少地方?
好,咱们一层一层拆开看。
2.1 顶层目录:U-Boot的“总指挥部”
顶层目录下的文件和文件夹,是整个U-Boot的骨架。我列个表,你对照着源码看会更清楚。
| 目录/文件 | 作用 | 我的备注 |
|---|---|---|
arch/ |
CPU架构相关代码(ARM、RISC-V、x86等) | 移植时最常改的地方之一 |
board/ |
具体板级支持代码 | 每个板子一个子目录 |
common/ |
通用核心函数(命令解析、环境变量等) | 一般不动,但得知道它在哪 |
drivers/ |
外设驱动(串口、网卡、MMC、USB等) | 驱动适配的主战场 |
include/ |
全局头文件和配置文件 | configs/ 子目录放板级配置 |
lib/ |
通用库函数(字符串、压缩、加密等) | 基本不用改 |
net/ |
网络协议栈(TFTP、NFS、DHCP等) | 调试网络启动时重点关注 |
fs/ |
文件系统支持(ext4、FAT、UBIFS等) | 按需开启即可 |
tools/ |
主机端工具(mkimage、env工具等) | 打包镜像时离不开它 |
Makefile |
顶层构建脚本 | 入口文件,定义了编译流程 |
Kconfig |
图形化配置系统入口 | 类似Linux内核的menuconfig |
arch/、board/、include/configs/ 这三个目录,90%的移植工作就搞定了。别被其他目录分散精力。
2.2 arch/ 目录:CPU架构的“方言区”
不同的CPU架构,启动流程、寄存器布局、指令集都不一样。U-Boot用 arch/ 目录把这些差异隔离起来。
以ARM为例,进去看看:
arch/arm/
├── cpu/ # 具体CPU核的代码(armv7、armv8、cortex-a系列等)
├── dts/ # 设备树源文件(.dts/.dtsi)
├── include/ # 架构相关的头文件
├── lib/ # 架构通用函数(cache、MMU设置等)
├── mach-*/ # 厂商/SoC相关代码(mach-sunxi、mach-rockchip等)
└── config.mk # 架构编译规则
这里有个关键点:mach-*/ 目录。每个SoC厂商都有自己的初始化逻辑。比如全志的芯片在 mach-sunxi 里,瑞芯微的在 mach-rockchip 里。移植时,你得先找到对应厂商的目录。
mach-* 目录,结果编译出来的U-Boot连串口都不打印。后来才发现,两个SoC的时钟树完全不一样。所以,不要偷懒,每个SoC的底层代码都得重新适配。
2.3 board/ 目录:板级定制的“身份证”
board/ 目录存放的是具体电路板的代码。一个板子一个子目录,命名规则通常是 board/<厂商>/<板子名>/。
举个例子:
board/ti/am335x/ # TI AM335x系列板子
board/freescale/mx6q/ # 飞思卡尔i.MX6Q板子
board/rockchip/evb_rk3399/# 瑞芯微RK3399 EVB板
每个板子目录下通常包含:
board.c:板级初始化函数(时钟、DDR、GPIO等)Kconfig:板级配置选项MAINTAINERS:维护者信息Makefile:编译规则
说白了,board/ 就是你的板子在U-Boot世界里的“身份证”。移植的第一步,往往就是在这里新建一个目录。
2.4 common/ 目录:U-Boot的“通用工具箱”
common/ 目录里装的是U-Boot的核心逻辑。比如:
- 命令处理:
cmd/子目录,每个命令一个文件(cmd_bootm.c、cmd_nand.c等) - 环境变量:
env/子目录,管理环境变量的存储和读取 - 启动流程:
main.c、board_r.c、board_f.c等 - 控制台:串口输入输出、命令行解析
这个目录我一般不动。但有个例外——如果你需要自定义一个启动命令,或者修改环境变量的存储方式,就得来这里翻一翻。
common/ 里的代码是“通用”的,不要为了一个板子的特殊需求去修改它。正确的做法是:通过配置选项(Kconfig)或板级回调函数来实现定制。
2.5 drivers/ 目录:外设驱动的“大本营”
U-Boot的驱动模型(Driver Model,简称DM)是从2014年之后逐步引入的。现在的 drivers/ 目录结构非常清晰:
drivers/
├── serial/ # 串口驱动
├── net/ # 网卡驱动(设计以太网、PHY等)
├── mmc/ # MMC/SD卡驱动
├── spi/ # SPI总线及设备驱动
├── i2c/ # I2C总线及设备驱动
├── gpio/ # GPIO驱动
├── usb/ # USB主机/设备驱动
├── mtd/ # NAND Flash、SPI Flash等存储驱动
├── video/ # 显示驱动(LCD、HDMI等)
└── ... # 其他外设
每个驱动目录下,又按厂商或芯片系列细分。比如串口驱动:
drivers/serial/
├── serial_ns16550.c # 标准16550 UART
├── serial_s3c24x0.c # 三星S3C24x0
├── serial_pl01x.c # ARM PrimeCell PL011
└── ... # 其他
移植时,如果你的板子用的是标准IP核(比如NS16550串口),那基本不用改驱动,配好寄存器地址和时钟就行。但如果是厂商自研的IP,那就得自己写驱动了。
drivers/serial/ 是我移植时第一个要搞定的目录。串口通了,后面的一切才有意义。
2.6 其他关键目录速览
除了上面几个大头,还有几个目录也值得了解:
| 目录 | 一句话说明 |
|---|---|
include/configs/ |
板级配置头文件,定义宏开关和参数 |
dts/ |
设备树源文件,描述硬件拓扑 |
scripts/ |
构建脚本和辅助工具 |
test/ |
单元测试和自动化测试用例 |
doc/ |
文档(README、移植指南等) |
2.7 一张图看懂U-Boot源码结构
说了这么多,不如一张图来得直观。下面这张SVG图,展示了U-Boot源码的核心层次关系:
2.8 移植时,我重点关注哪些目录?
好了,目录结构讲完了。最后总结一下,如果你要移植U-Boot到一个新板子,按这个顺序来:
arch/arm/cpu/:确认CPU核类型,选对编译选项arch/arm/mach-*/:找到SoC厂商的初始化代码board/<厂商>/<板子名>/:新建板级目录,写board.cinclude/configs/<板子名>.h:配置DDR参数、时钟、外设地址drivers/serial/:搞定串口,让U-Boot能打印drivers/net/:网卡驱动,用于网络启动drivers/mmc/:存储驱动,用于加载内核和设备树
嗯,这个顺序我踩过不少坑。尤其是DDR参数,如果配错了,U-Boot根本跑不起来。所以,先确认DDR初始化代码是否正确,再往下走。
好了,这一章的内容就到这里。U-Boot的源码结构,说白了就是一套分层设计。你只要记住:arch/ 管CPU,board/ 管板子,drivers/ 管外设,include/configs/ 管配置。剩下的,都是辅助。