2、U-Boot源码结构:顶层目录结构、关键子目录功能、Makefile与Kconfig体系
好,咱们直接切入正题。U-Boot的源码,说白了就是一个大型的嵌入式启动引导工程。我第一次接触它的时候,也被那密密麻麻的文件夹吓了一跳。但别慌,它的结构其实很有章法。你把它拆开来看,无非就是“板级配置”、“CPU相关”、“驱动”、“通用库”这几大块。
我个人习惯,拿到一个新板子,第一件事不是急着改代码,而是先把这个目录结构摸清楚。这就像你搬家,得先知道厨房在哪儿、厕所在哪儿,对吧?
2.1 顶层目录结构:一眼看穿全局
你解压U-Boot源码后,在终端里敲个 ls,会看到一堆文件夹。我挑几个最关键的跟你说说。
| 目录/文件 | 作用 | 我的经验 |
|---|---|---|
board/ |
板级相关代码,每个板子一个子目录 | 移植时80%的改动都在这里 |
cpu/ |
CPU架构相关代码(ARM、RISC-V、x86等) | 一般不动,除非换CPU核 |
drivers/ |
各种外设驱动(网卡、串口、MMC等) | 调驱动时天天泡在这里 |
include/ |
头文件,包括板级配置头文件 | 配置宏定义全在这 |
common/ |
通用函数,比如命令行解析 | 一般不动,但可以学习 |
lib/ |
库函数,比如字符串操作、加密 | 很少改 |
net/ |
网络协议栈 | 调网络启动时有用 |
fs/ |
文件系统支持(ext4、fat等) | 需要读文件系统时才关注 |
tools/ |
主机端工具,比如mkimage | 打包镜像时用 |
Makefile |
顶层构建文件 | 理解构建流程的关键 |
Kconfig |
配置系统入口 | 图形化配置的基础 |
board/ 是“你家”,drivers/ 是“工具箱”,include/ 是“说明书”。其他目录,用到的时候再查。
2.2 关键子目录功能:深入核心
board/ —— 你的主战场
这个目录下,每个板子都有一个专属文件夹。比如 board/ti/am335x/ 就是TI的AM335x开发板。里面通常有:
board.c:板级初始化函数,比如DDR初始化、时钟配置。Kconfig:这个板子的配置选项。Makefile:编译这个板子需要的文件列表。
我在项目中遇到过最头疼的事,就是DDR参数配错了。板子死活起不来,串口也没输出。后来我学乖了,每次改 board.c 里的DDR参数,都会先对照芯片手册,再拿示波器量一下波形。嗯,这步不能省。
cpu/ —— 芯片的“魂”
这里存放的是CPU架构相关的代码。比如 cpu/armv7/ 就是ARM Cortex-A7/A8/A9通用的代码。里面包含:
- 异常向量表
- MMU(内存管理单元)设置
- Cache(缓存)操作
- 启动汇编代码(比如
start.S)
说实话,这个目录我平时很少动。除非你要换CPU核,或者要优化启动速度。否则,保持原样就好。
drivers/ —— 驱动大本营
这个目录结构跟Linux内核很像。里面有 serial/(串口)、net/(网卡)、mmc/(SD卡)、spi/、i2c/ 等等。每个驱动都是一个独立的模块。
你想想看,U-Boot的驱动模型(Driver Model)其实挺巧妙的。它把驱动分成了“设备”和“驱动”两部分,通过设备树(Device Tree)来匹配。我刚开始移植网卡驱动时,就是照着 drivers/net/ 里一个类似的驱动改的。改完发现不工作,折腾了两天,最后发现是设备树里中断号写错了。哎,这种低级错误,犯过一次就记住了。
include/ —— 配置的“总开关”
这个目录下的 configs/ 子目录,存放着所有板子的默认配置文件。比如 include/configs/am335x_evm.h。这个文件里定义了一大堆宏,比如:
#define CONFIG_BOOTDELAY 3
#define CONFIG_BOOTCOMMAND "run mmcboot"
#define CONFIG_SYS_TEXT_BASE 0x80800000
这些宏控制了U-Boot的行为。比如 CONFIG_BOOTDELAY 就是启动倒计时秒数。我个人习惯,在调试阶段会把倒计时设成0,省得每次都要敲回车打断。
Kconfig 替代了传统的 #define 配置方式。但很多老项目还在用头文件配置。两种方式你都得会。
2.3 Makefile与Kconfig体系:构建与配置的艺术
这部分是U-Boot移植的“内功心法”。不懂Makefile和Kconfig,你连编译都过不了。
Makefile —— 构建的“指挥家”
U-Boot的Makefile体系非常庞大,但核心逻辑很简单:
- 顶层Makefile读取
.config文件(由Kconfig生成)。 - 根据
.config中的配置,决定编译哪些文件。 - 递归进入各个子目录,执行子Makefile。
- 链接生成
u-boot和u-boot.bin。
我曾经为了搞清楚一个编译错误,追了三个小时的Makefile。最后发现是某个驱动没选上,导致符号未定义。所以我的建议是:遇到编译问题,先检查 .config 文件,看看你想要的驱动有没有被 CONFIG_ 宏打开。
Kconfig —— 配置的“菜单”
Kconfig是Linux内核引入的配置系统。U-Boot从2014年开始逐步迁移。你运行 make menuconfig 时,看到的那个图形界面,就是Kconfig生成的。
每个子目录下都有一个 Kconfig 文件,定义了该目录下的配置选项。比如 drivers/net/Kconfig 里会有:
config NET_TI_CPSW
bool "TI CPSW Ethernet driver"
help
This driver supports the TI CPSW Ethernet controller.
你选中这个选项后,.config 里就会多一行 CONFIG_NET_TI_CPSW=y,然后Makefile就会编译对应的驱动文件。
.config 文件沟通。移植新板子时,你需要在 board/你的板子/Kconfig 里添加你自己的配置选项,然后在顶层Kconfig里引用它。
2.4 知识体系结构图
下面这张图,是我自己总结的U-Boot源码结构关系。你看一眼,就能明白各个模块是怎么协作的。
这张图你看懂了吗?顶层Makefile是总指挥,它从 .config 里读取配置,然后决定编译哪些模块。Kconfig是配置的入口,你通过 menuconfig 选好功能后,它会生成 .config。各个模块(board、cpu、drivers等)根据配置编译,最后链接成 u-boot.bin。
嗯,到这里,U-Boot的源码结构你应该有个整体印象了。说白了,它就是一个高度模块化的嵌入式系统。你移植新板子,就是在 board/ 里加一个新文件夹,然后在Kconfig里加上你的板子选项,再配置好DDR、时钟、串口这些基础外设。剩下的,就是调试和填坑了。
可可精品资料库