2. U-Boot源码结构:顶层目录、板级目录、CPU架构目录、驱动目录解析

拿到一份U-Boot源码,第一眼看到几十个文件夹,很多人会懵。我刚开始接触U-Boot时也是这样,对着目录结构发愁——到底该从哪里看起?

其实U-Boot的目录设计很有章法。它把通用代码、硬件相关代码、板级特有代码分得清清楚楚。你只要抓住三条主线:架构无关、架构相关、板级相关,整个源码脉络就清晰了。

核心原则:U-Boot源码按"三层分离"组织——通用层(arch无关)、架构层(arch)、板级层(board)。移植新板卡时,你只需要关注板级层和少量架构层配置。

2.1 顶层目录:U-Boot的"总控室"

先看顶层目录。这些目录是U-Boot的骨架,每个都有明确职责。

目录/文件 作用 我的备注
arch/ CPU架构相关代码(ARM、RISC-V、x86等) 移植重点目录之一
board/ 板级初始化、外设配置 每个板卡一个子目录
common/ 通用命令、控制台、环境变量 基本不用动
drivers/ 各类设备驱动 串口、网卡、存储都在这里
include/ 头文件、配置宏、板级配置 configs/ 子目录放板级配置
lib/ 通用库函数(字符串、加密、压缩) 与架构无关
net/ 网络协议栈(TFTP、NFS、DHCP) 调试时经常改
fs/ 文件系统支持(ext4、FAT、UBIFS) 启动内核时用到
cmd/ 命令行命令实现 可以自己加命令
tools/ 主机端工具(mkimage、env工具) 打包镜像用
doc/ 文档、README 移植前先看这里

我个人习惯,拿到一个新板卡,先看 board/ 和 include/configs/。这两个目录直接决定了板卡能不能跑起来。

小技巧:顶层 Makefile 是整个构建的入口。你执行 make menuconfig 时,它调用 scripts/kconfig/ 下的配置系统。想理解编译流程,从 Makefile 开始追是最快的。

2.2 板级目录:你的板卡"身份证"

board/ 目录下每个子目录对应一个板卡或一个厂商系列。比如 board/ti/am335x/ 就是TI AM335x系列板卡的代码。

一个典型的板级目录包含这些文件:

  • board.c — 板级初始化函数 board_init()、board_late_init()
  • Kconfig — 板级配置选项
  • MAINTAINERS — 维护者信息
  • Makefile — 编译规则
  • mux.c — 引脚复用配置(很多板卡都有)
  • u-boot.lds — 链接脚本(有时在 arch/ 下)

我记得有一次移植一块新板卡,板级目录里只有个空壳。我花了整整两天配置DDR参数和时钟树——这些都在 board.c 的 board_init_f() 里完成。

避坑指南:我曾经在 board.c 里漏掉了 gd->bd->bi_arch_number 的赋值,结果内核启动时传参错误,系统直接挂掉。板级目录里的每个函数都有它的作用,不要随意删减。

2.3 CPU架构目录:硬件差异的"隔离层"

arch/ 目录是U-Boot最复杂的部分之一。它按CPU架构分门别类:

arch/
├── arm/          # ARM架构(最常用)
│   ├── cpu/      # 具体CPU型号(armv7、armv8、arm11等)
│   ├── dts/      # 设备树文件
│   ├── include/  # 架构相关头文件
│   ├── lib/      # 架构通用函数(cache、MMU、中断)
│   └── mach-*/   # 厂商特有代码(mach-sunxi、mach-rockchip等)
├── riscv/        # RISC-V架构
├── x86/          # x86架构
├── mips/         # MIPS架构
├── powerpc/      # PowerPC架构
└── sandbox/      # 模拟测试架构

ARM目录下,cpu/ 子目录存放不同ARM核的启动代码。比如 armv7/ 下的 start.S 就是Cortex-A系列的第一条指令入口。

为什么会这样设计?说白了,不同CPU核的初始化流程不一样。ARMv7需要设置SVC模式、关中断、初始化MMU和Cache;RISC-V则要设置CSR寄存器。U-Boot通过 arch/ 目录把这些差异封装起来,上层代码不用关心具体架构细节。

关键点:arch/arm/cpu/armv7/start.S 是U-Boot的"第一滴血"。CPU上电后第一条指令就在这里。它负责设置栈指针、清零BSS段、跳转到 board_init_f()。如果你想理解U-Boot的启动流程,这个文件是必读的。

2.4 驱动目录:设备驱动的"大本营"

drivers/ 目录是U-Boot设备驱动的集合。它按设备类型组织:

子目录 包含的驱动 典型文件
serial/ 串口驱动 ns16550.c、pl011.c
net/ 网卡驱动 designware.c、fec_mxc.c
mmc/ MMC/SD卡驱动 sdhci.c、dwc_mmc.c
spi/ SPI控制器驱动 spi_flash.c、omap3_spi.c
i2c/ I2C控制器驱动 i2c_core.c、mvtwsi.c
gpio/ GPIO驱动 gpio-uclass.c
usb/ USB主机/设备驱动 ehci-hcd.c、musb_udc.c
mtd/ NAND/SPI NOR Flash驱动 nand_base.c、sf_probe.c
video/ 显示驱动 simple_panel.c

U-Boot现在的驱动模型(Driver Model,简称DM)很成熟。每个驱动都注册为一个 udevice,通过 uclass 统一管理。你想想看,以前写驱动要手动调用初始化函数,现在只需要在设备树里配好,驱动框架自动帮你搞定。

经验之谈:调试串口驱动时,我建议先确认 drivers/serial/ 下有没有现成的驱动。很多SoC的串口都是16550兼容的,直接配寄存器地址和时钟就能用。我踩过最大的坑是忘了配置引脚复用——串口驱动本身没问题,但引脚没切换到UART功能,死活不出log。

2.5 知识体系总览

下面这张图帮你理清U-Boot源码的整体结构。我把它画成了分层架构图,方便你理解各目录之间的关系。

U-Boot源码结构分层架构图 U-Boot 源码结构分层架构 板级层(Board Layer) board/ 目录 board_init() · 引脚复用 · DDR配置 · 板级特有外设 CPU架构层(Architecture Layer) arch/ 目录 ARM (armv7/armv8) RISC-V x86 / MIPS / PowerPC start.S · cache · MMU 驱动层(Driver Layer) drivers/ 目录 · Driver Model (DM) serial · net · mmc · spi · i2c · gpio · usb · mtd · video 通用层(Common Layer) common/ · lib/ · net/ · fs/ · cmd/ · include/ 💡 移植新板卡时,主要修改板级层(board/)和架构层(arch/)的配置,驱动层和通用层基本不动。

从这张图可以看得很清楚:板级层在最上面,直接面对你的硬件板卡;架构层在中间,隔离不同CPU的差异;驱动层和通用层在下面,提供跨平台的基础服务。这种分层设计让U-Boot能支持上千种板卡,而核心代码改动很小。

2.6 几个实用技巧

最后分享几个我在项目中积累的经验:

  • 快速定位板卡配置:执行 ls board/ 和 ls include/configs/,找到你的板卡名。比如 include/configs/am335x_evm.h 就是AM335x评估板的配置头文件。
  • 理解编译依赖:U-Boot的Kconfig系统在 scripts/kconfig/ 下。执行 make _defconfig 时,它会读取 arch/../Kconfig 和 board/../Kconfig 生成 .config。
  • 调试驱动加载顺序:在 drivers/ 下加 debug 打印,或者在 common/board_r.c 的 init_sequence_r[] 数组里跟踪初始化顺序。
  • 查看设备树绑定:U-Boot的设备树文件在 arch/arm/dts/ 下。驱动通过 compatible 字符串匹配设备节点。

重要提醒:不要直接修改 common/ 和 lib/ 下的通用代码。这些代码被所有板卡共享,改错了会影响整个U-Boot生态。正确的做法是在板级目录或驱动目录里做定制。