资讯动态

U-Boot移植入门:如何把自己的开发板登记进U-Boot源码体系

发布时间:2026/10/22 12:07:10 来源:尧图企业网站定制
最近在整理U-Boot移植的第二阶段时发现很多同学不是不会改代码而是卡在“怎么让U-Boot认识我的板子”这一关。手里拿着自己画的板子或者某个冷门评估板翻遍网上的教程发现全是拿官方开发板举例一到“如何把自己的板子加进去”要么含糊带过要么直接说“改一个现有板子就行”。我在给一块基于Cortex-A7双核SoC的自研板做移植时也走过这段弯路翻了不少补丁邮件和官方文档才彻底弄明白添加开发板这件事本质上是在U-Boot的源码组织体系里完成一次完整的“身份登记”让Kconfig菜单、配置系统和编译脚本都能找到它。这篇文章就结合Ubuntu 20.04环境把第一阶段的操作完整捋一遍。内容不算高深但每步的“为什么”我都会讲清楚适合正在做U-Boot移植、被海量宏定义绕晕的开发者参考。1. 为什么要自己“添加开发板”直接改现成的不好吗很多人的第一反应是U-Boot已经支持了同系列SoC的官方板子那我直接在官方板的defconfig和头文件上改不就行了短时间实验也许能跑但这不是移植是在打补丁。等你想换一颗DDR颗粒、换一个启动介质、想让代码进Git管理的时候混乱就开始出现了。1.1 U-Boot是怎么“认识”一块新板的放在十年前U-Boot靠根目录下的boards.cfg文件维护所有板卡列表改一行记录编译系统就能识别。现在这套机制已经被彻底淘汰取而代之的是Kconfig体系。你执行make xxx_defconfig时实际发生的事件链是根据configs/xxx_defconfig里的配置项生成.configKconfig根据.config把依赖关系解析出来生成include/generated/autoconf.h编译系统再根据CONFIG_SYS_CONFIG_NAME这个关键变量去include/configs/目录寻找对应的板级配置头文件最终所有宏汇总参与C源码的预处理和编译。所以所谓“添加自己的开发板”就是要在源码里做好三件事提供板级支持代码board/vendor/board目录、提供配置入口configs/board_defconfig、在SoC对应的Kconfig中登记新板子让菜单里出现这个选项。三者缺一不可。1.2 不添加板的两种错误移植姿势第一种姿势是“借壳上市”。直接把官方板的board.c改一改板子名字、DDR参数、环境变量位置全塞进去。代码能编过但当你需要修改启动流程时会很痛苦官方板和自研板的外设、时钟、复位逻辑往往不一样改来改去最后这个目录里堆积了无数#ifdef连原作者都认不出来。第二种姿势是“只复制不登记”。不少初学者会把一个官方板目录整个复制一份改成自己的名字然后兴奋地执行make myboard_defconfig结果提示找不到文件。原因很简单configs/目录下没有对应的defconfigKconfig菜单里也不存在这个目标板选项编译系统根本不会把新目录编译进去。代码写得再好编译系统不知道它的存在等于白写。正确理解是U-Boot不是把“你的板子”当作一个独立项目而是把它视作源码库里的一员。你想让它被支持就必须让源码库的“登记制度”认可它。这也是为什么官方一直鼓励直接向社区提交新板支持补丁而不是自己私底下改。2. Ubuntu 20.04交叉编译环境准备好再动手标题里特意带了Ubuntu 20.04说明这个版本本身没坑。但我在不少交流群里看到有人卡在环境上不是包没装全就是工具链选错。UBoot构建依赖了一系列主机软件包缺少任何一个都会在编译中途爆出莫名其妙的错误。2.1 依赖包清单和安装命令先执行一遍系统更新然后一次性安装下面这些包sudo apt update sudo apt install -y build-essential git flex bison \ device-tree-compiler libssl-dev libncurses5-dev \ u-boot-tools逐个说下它们的作用build-essential提供gcc、make等基础编译工具flex、bison生成设备树编译器dtc需要的解析器代码缺少时会报flex: command not foundlibssl-dev编译U-Boot里的工具程序比如mkimage时需要openssl头文件少它会报openssl/xxx.h: No such file or directorylibncurses5-devmake menuconfig依赖的终端图形库device-tree-compiler提供dtc命令用于编译设备树u-boot-tools提供mkimage等工具后续打包镜像会用。如果你用的是Ubuntu 22.04及以后libncurses5-dev会变成过渡包直接装libncurses-dev即可。20.04还是老名字按上面来就好。2.2 交叉编译工具链的选择这里说的交叉编译工具链是指运行在x86主机上、生成ARM机器码的编译器。工具链能不能用对直接决定后面是顺利还是折腾。以ARM 32位平台为例最简单的方式是直接用Ubuntu源里自带的工具链sudo apt install -y gcc-arm-linux-gnueabihf装好后编译器的前缀固定为arm-linux-gnueabihf-。如果你的SoC是ARM 64位Cortex-A53、A72等则应安装sudo apt install -y gcc-aarch64-linux-gnu对应前缀为aarch64-linux-gnu-。这里特别提醒32位和64位的工具链不要混用。我之前见过有人拿32位工具链去编译64位U-Boot汇编阶段报了一堆莫名其妙的指令错误看起来像源码问题其实就是工具链位数不匹配。为什么推荐Ubuntu自带的工具链而不是去网上下某个Linaro版本因为20.04源里的gcc-arm-linux-gnueabihf是9.x版本编译U-Boot完全够用省去了配置环境变量、处理libstdc.so.6缺失等麻烦。只有当你需要编译原厂SDK里的其他组件比如DDR初始化固件且原厂明确要求特定工具链版本时才建议切换到原厂提供的交叉工具链。2.3 检查环境是否真的可用装完后不要急着编译先验证一次arm-linux-gnueabihf-gcc -v which arm-linux-gnueabihf-gcc echo $PATH如果第一个命令能打印出版本信息且which能找到编译器路径说明工具链可用。还有一个容易忽略的检查项确认你的Ubuntu是64位系统。32位系统上无法运行现代交叉编译器。如果你是在Windows下用WSL来编译需要注意一点WSL里编译U-Boot没问题但后续如果要通过串口/USB下载镜像WSL对硬件设备的访问是比较麻烦的通常需要借助usbipd之类的方案。建议纯编译工作在WSL里做涉及烧录和调试还是回到真正的Linux主机或虚拟机里。3. 以官方参考板为模板落地自己的board目录环境准备好之后就进入正题。这里我强烈建议“站在参考板的肩膀上”而不是从零写一个board.c。从零写一个能用的板级文件门槛很高涉及到时钟、DDR、串口等一大堆初始化逻辑新手没必要重复造轮子。3.1 参考板选型原则选参考板的标准有优先级排序优先选同一颗SoC、其次选同系SoC、再次选启动方式相近比如都是SD卡启动的板子。举个例子我的自研板用的是某款Cortex-A7双核SoCDDR3L颗粒SD卡启动。那么我就先在这个SoC对应的arch/arm/mach-xxx/目录下找官方支持的评估板然后看它的board/vendor/目录结构。如果源码树里压根没有同SoC的支持目录那就得先去把该SoC的mach目录补上这属于更基础的分支不在本篇文章范围。今天假设你的SoC已经被主线支持只是官方板目录里没有你的板。3.2 复制与改写board目录开一个终端进入U-Boot源码根目录cd u-boot # 创建新板目录名字建议用小写字母中划线不要带空格 mkdir -p board/dev/devboard-a7 # 参考官方板目录复制假设官方板叫evb-a7 cp -r board/dev/evb-a7/* board/dev/devboard-a7/复制完先做一轮清理把编译过程中生成的.o、.cmd等临时文件删干净否则后面可能把过期产物混进新板子里cd board/dev/devboard-a7 find . -name *.o -delete find . -name *.cmd -delete find . -name *.su -delete然后逐文件修改。先看Kconfigconfig TARGET_DEVBOARD_A7 bool Support DevBoard A7 if TARGET_DEVBOARD_A7 config SYS_BOARD string default devboard-a7 config SYS_VENDOR string default dev config SYS_SOC string default ax7 config SYS_CONFIG_NAME string default devboard-a7 endif这段配置的含义是向菜单里注册一个名为DEVBOARD_A7的目标板选项同时告诉编译系统这个板子的目录名、厂商名、SoC名、板级配置头文件名分别是什么。SYS_CONFIG_NAME尤其重要后续编译系统就是靠它去include/configs/下寻找devboard-a7.h的。接着修改Makefileobj-y : devboard-a7.o这句就是把devboard-a7.c加入编译。如果你的参考板目录里还有lowlevel_init.S、ddr.c之类的文件记得一并保留原名或同步改名并体现在Makefile里。最后是MAINTAINERS文件填上你自己的维护者信息。这里不需要真实人名格式参考官方风格即可。社区对维护者的要求是“出了问题有人能回应”不是“填个名就行了”。3.3 板级头文件与板级代码的关系在U-Boot的旧式配置体系中include/configs/board.h是板子宏定义的大本营。你可以看到大量CONFIG_SYS_...宏比如DDR基地址、环境变量偏移、U-Boot在内存中的链接地址等等。现在很多宏已经在往Kconfig迁移但板级头文件依然是有效且常见的手段。对第一阶段来说可以直接从参考板的头文件复制一份改成include/configs/devboard-a7.h然后暂时保留参考板的宏等后面适配DDR和启动介质时再逐个修改。很多朋友不理解“为什么板级C文件里没有太多代码”这是因为U-Boot把硬件初始化拆分得很开SoC相关的初始化在arch/arm/mach-xxx/目录DDR初始化可能通过dram_init()函数回调真正跟板子强相关的代码就集中在board_init()、board_mmc_init()这几个函数里。你能在devboard-a7.c里看到的大多数代码本质是承接SoC和板子的“接线”工作。4. 让Kconfig和configs认识你的新板目录建好、文件改完之后别急着编译还差最后一步登记。这一步做不对前面的活等于白干。4.1 从boards.cfg到Kconfig的登记制度老版本的U-Boot是通过boards.cfg文件来维护板卡列表的网上一些老教程会让你在这个文件里加一行。而现代U-Boot已经彻底Kconfig化你得在SoC对应的架构菜单里显式引入新板的Kconfig文件菜单里才会出现你的板子。具体操作是找到SoC对应的架构Kconfig。比如我用的SoC是ax7对应arch/arm/mach-ax7/Kconfig。在这个文件里加入一行source board/dev/Kconfig注意这行要放在合适的位置通常是该SoC的板卡选择区。加入之后执行make menuconfig在ARM architecture的菜单里应该能看到Support DevBoard A7这个选项。这一步可以验证前面的登记是否成功。4.2 编写defconfig与最小配置登记完成只是让U-Boot“认识”你真正决定怎么编译还是需要configs/devboard-a7_defconfig。创建一个新文件CONFIG_TARGET_DEVBOARD_A7y CONFIG_DEFAULT_DEVICE_TREEdevboard-a7这里CONFIG_TARGET_DEVBOARD_A7y会触发板级Kconfig里的所有依赖项CONFIG_DEFAULT_DEVICE_TREE指向后续要添加的设备树文件名。如果你这一阶段还不想引入设备树可以暂时不写设备树相关项甚至在板级Kconfig里取消CONFIG_OF_CONTROL但要注意参考板可能默认依赖设备树贸然关掉会导致编译失败。更稳妥的做法是从参考板的arch/arm/dts/目录里复制对应dts文件改名为devboard-a7.dts然后在arch/arm/dts/Makefile里把新dts加进目标列表。设备树内容可以先沿用等后续驱动适配再真正修改。这样既满足构建链又不会让第一阶段卡死。4.3 用savedefconfig生成极简配置很多新手习惯自己手写defconfig结果漏写一堆依赖项。其实更好的方法是先生成一个临时配置再回写精简defconfigmake devboard-a7_defconfig make menuconfig # 根据需求调整选项 make savedefconfig cp defconfig configs/devboard-a7_defconfigmake savedefconfig会根据当前Kconfig依赖关系自动把.config精简成最简defconfig。这个命令能省去大量手工整理时间也能避免因为缺了某个隐含依赖导致的“明明配置了却没用”问题。完成这一步后执行make devboard-a7_defconfig编译系统会读取configs/devboard-a7_defconfig生成.config再根据Kconfig生成autoconf.h。你可以打开include/generated/autoconf.h如果能看到CONFIG_TARGET_DEVBOARD_A7的宏定义说明整条登记链路已经打通。5. 第一次编译实战看log、修报错、拿到bin配置链路打通后正式编译这一阶段的目标非常朴素让U-Boot编译通过生成可用的u-boot.bin。专题中的第一期已经处理过U-Boot自身的编译框架所以你其实是有“这套源码能编过”的心理预期的剩下的问题只可能出在新板登记环节。5.1 编译命令与产物说明正式编译前先设置环境变量export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf-如果你是64位平台ARCH要对应用户平台配置工具链前缀也要同步改。然后make devboard-a7_defconfig make -j4 V1这里我特意建议-j4而不是-j$(nproc)。第一次编译时如果错误信息淹没在多线程输出里排查会非常费劲。V1能打印完整编译命令适合排错。等确认能编过之后再放开多线程。编译成功后重点看几个产物u-boot.bin裸机U-Boot镜像后续要打包进启动介质u-boot.map链接映射文件排查链接地址、符号分配时很有用spl/目录下的u-boot-spl.bin如果你启用了SPLSecondary Program Loader这里就是第一阶段引导程序u-boot.dtb如果启用了设备树控制会有设备树二进制。第一阶段不需要着急把镜像烧进板子先确认“能编过、产物齐全”就已经成功了一大半。5.2 新手最容易撞上的五个编译错误我整理了几个常见报错基本覆盖了大部分新手会踩的坑报错信息根因解决方式arm-linux-gnueabihf-gcc: command not found交叉工具链未安装或不在PATHapt install gcc-arm-linux-gnueabihf检查环境变量flex: command not found主机缺少flexapt install flexopenssl/xxx.h: No such file or directory缺少libssl-devapt install libssl-devError: unknown pseudo-op:.word编译器不是ARM工具链或者用32位工具链编64位代码检查CROSS_COMPILE和ARCH设置undefined reference to board_init板级Kconfig里的SYS_VENDOR/SYS_SOC和实际目录不匹配导致板级文件没被编译检查Kconfig中SYS_BOARD、SYS_VENDOR的default值第五个错误特别典型。U-Boot会自动根据CONFIG_SYS_BOARD和CONFIG_SYS_VENDOR拼出目录路径进而在对应Makefile里编译obj-y。如果你默认值写错了编译系统找不到板级文件函数定义就等于不存在。遇到链接错误时先回查Kconfig里的这几个字符串比在C代码里找函数体快得多。5.3 定位编译问题的排查思路编译报错时不要慌着粘贴到网上求答案。我自己的排查顺序是先看报错发生在哪个文件、哪个函数。检查这个文件是不是预期中的板级文件。很多时候是Kconfig登记没生效导致整个目录没被编译进去。打开include/generated/autoconf.h确认关键宏是否存在。宏没有就去查defconfig和Kconfig依赖。如果是汇编阶段的指令错误优先怀疑交叉工具链版本或ARCH设置而不是怀疑源码。用make clean重来一次排除旧产物干扰。还有个小技巧是在board/dev/devboard-a7/Makefile里临时加一行$(warning enter new board build)确认这个目录是否真的进入了编译流程。这种“打桩”手法在定位构建问题时非常有效。另外如果你的配置里启用了CONFIG_OF_CONTROL且还没添加设备树编译大概率会在dtb阶段失败。遇到这种情况先确认arch/arm/dts/Makefile里是否加入了devboard-a7.dtb或你的设备树文件名。这是“添加开发板(1)”阶段最容易忽略的一步我在做参考板复制时也会经常忘记。6. 第一阶段验收标准与后续规划第一次编译跑通后我建议给自己定一个验收清单避免“能编过”就误以为“板子已经能启动”。我的验收标准很简单执行make devboard-a7_defconfig make -j4能稳定通过u-boot.bin生成且板级目录、defconfig、Kconfig三者的名字保持一致。再进一步把编译产物用串口工具烧到SD卡能看到U-Boot串口输出哪怕停在“Unknown command”都没关系至少说明板级代码没有被编译系统丢弃。这时候你的board/dev/devboard-a7/目录已经成了独立的“私房定制”目录后续做DDR适配、网络驱动、MMC驱动时都可以在这里改不会污染官方代码。我个人强烈建议在添加开发板之前先git init并打一个tag每次能正常编译就提交一次。嵌入式源码调试最怕“改了一堆不知道哪里坏了”版本管理能帮你随时回到已知可用状态。还有一点想单独提醒U-Boot的配置体系是“多入口、共终点”的。defconfig不是唯一配置入口include/configs里的头文件、Kconfig里的default值也能改变编译结果。所以排查问题时别只盯着一处看要在autoconf.h里找最终答案。这篇先写到这里下一期会从设备树的引入开始把devboard-a7.dts、DDR初始化、环境变量分区逐步展开。对正在自己动手移植U-Boot的朋友来说这个阶段最重要的不是追求跑通复杂功能而是让U-Boot源码心甘情愿地接纳你的板子。等这条登记链路完全疏通后面的工作会顺畅很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑