玩转U-Boot bdinfo:嵌入式bsp开发者的定制、扩展与裁剪实战指南 电子说
作为嵌入式开发者,U-Boot 是我们调试、适配板卡的核心工具,而 bdinfo 命令更是板级信息调试的“利器”——它能直观打印内存布局、Flash 信息、网络配置、时钟频率等核心参数。但原厂的 bdinfo.c 往往是“大而全”的通用实现,适配自研板卡时,要么冗余打印拖慢调试效率,要么缺少我们需要的自定义硬件信息。
今天就结合bdinfo.c 的核心逻辑,手把手教你做功能裁剪、扩展、私有定制,让bdinfo 完全贴合自研板卡的调试需求!

一、先理清:bdinfo.c 核心价值与定制思路
bdinfo.c 的核心是实现bdinfo 命令,通过封装各类打印函数(print_num/print_mhz/print_eth 等),基于bd_t(板级信息结构体)和全局gd 指针,按不同架构(ARM/RISC-V/PPC 等)差异化打印硬件参数。
我们的定制思路围绕 3 个核心需求:
•裁剪:删掉自研板卡用不到的打印逻辑,减小 U-Boot 镜像体积;
•扩展:新增自研板卡的自定义硬件信息打印(如硬件版本、PMIC 状态);
•定制:修改输出格式,适配自研调试工具(如 JSON 格式、固定分隔符)。
二、实战 1:功能裁剪——精简冗余打印,减小镜像体积
自研板卡往往是“极简设计”:比如只有单网口、无外置 Flash、不需要多 DRAM Bank 打印,这些冗余代码不仅增加镜像体积,还会在调试时输出无关信息,干扰判断。
适用场景
•自研 ARM 板卡只有 eth0,不需要 eth1-eth5 打印;
•板卡无 Flash 芯片,无需 Flash 起始地址/大小打印;
•不需要 LCD/VIDEO 相关的帧缓冲(FB base)打印。
裁剪步骤(以 ARM 架构为例)
步骤 1:精简以太网打印
原厂代码会遍历 eth0-eth5,但我们只有单网口,直接修改 print_eth_ip_addr 函数,只保留 eth0:
static inline void print_eth_ip_addr(void){// 只保留eth0,删除eth1-eth5的条件编译print_eth(0);printf("IP addr = %sn", env_get("ipaddr"));}
步骤 2:删除 Flash 相关打印
如果板卡无 Flash,直接注释/删除 print_bi_flash 函数的调用(以 ARM 架构的 do_bdinfo 为例):
static int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[]){bd_t *bd = gd->bd;print_num("arch_number",bd->bi_arch_number);print_bi_boot_params(bd);print_bi_dram(bd);// 【裁剪】删除Flash打印(板卡无Flash)// print_bi_flash(bd);// ... 原有逻辑保留// ... 其余逻辑不变}
步骤 3:关闭 LCD/VIDEO 相关打印
如果板卡无显示模块,删除帧缓冲打印:
// 【裁剪】注释掉FB base打印// print_num("FB base ", gd->fb_base);
裁剪核心原则
•优先用条件编译宏(如CONFIG_MY_BOARD_NO_FLASH)封装裁剪逻辑,便于后续开关:
print_bi_flash(bd);
•只删除“打印调用”,不删除底层函数(如 print_bi_flash),避免影响其他架构/板卡复用。
三、实战 2:功能扩展——新增自定义板级信息打印
这是最常用的定制场景:比如打印自研板卡的硬件版本、PMIC 电压、传感器 ID、自定义保留内存区域等。
适用场景
自研 ARM 板卡需要打印:
1.硬件版本号(存储在gd 全局变量的自定义字段);
2.PMIC(电源管理芯片)的工作电压;
3.自研的安全分区内存地址。
扩展步骤
步骤 1:定义自定义辅助打印函数
在bdinfo.c 中新增适配自定义信息的打印函数(复用原厂的格式化风格,保持一致性):
// 新增:打印硬件版本__maybe_unusedstatic void print_board_version(const char *name, u32 version){// 格式对齐原厂:左对齐12字符,后接版本号(十进制)printf("%-12s= V%02d.%02dn", name, (version>>8)&0xFF, version&0xFF);}// 新增:打印PMIC电压(单位:mV)__maybe_unusedstatic void print_pmic_voltage(const char *name, u32 voltage_mv){printf("%-12s= %d mVn", name, voltage_mv);}// 新增:打印自定义保留内存__maybe_unusedstatic void print_custom_reserve_mem(const char *name, ulong start, ulong size){print_num(name, start);print_num("-> size", size);}
步骤 2:在对应架构的 do_bdinfo 中添加调用
以 ARM 架构为例,在 do_bdinfo 函数中新增自定义打印(建议放在原有打印逻辑的末尾,便于查看):
static int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[]){bd_t *bd = gd->bd;// ... 原有打印逻辑(arch_number、DRAM、eth等)保留// 【扩展】新增自定义硬件信息打印// 1. 打印硬件版本(假设gd->arch.board_version是自定义字段)print_board_version("Board Ver", gd->arch.board_version);// 2. 打印PMIC核心电压(模拟读取PMIC寄存器)u32 core_volt = read_pmic_reg(PMIC_CORE_VOLT_REG); // 自研PMIC读取函数print_pmic_voltage("PMIC Core", core_volt);// 3. 打印安全分区内存print_custom_reserve_mem("Secure Area", 0x90000000, 0x100000);print_baudrate();// ... 其余原有逻辑保留return 0;}
步骤 3:用条件编译封装扩展逻辑
为了便于开关自定义打印,新增全局宏CONFIG_MY_BOARD_CUSTOM_BDINFO:
// 自定义打印逻辑print_board_version("Board Ver", gd->arch.board_version);print_pmic_voltage("PMIC Core", core_volt);print_custom_reserve_mem("Secure Area", 0x90000000, 0x100000);
然后在板卡的configs/my_board_defconfig 中添加:
CONFIG_MY_BOARD_CUSTOM_BDINFO=y
四、实战 3:私有定制——适配自研调试工具
原厂bdinfo 输出是“纯文本”,如果需要对接自研的调试解析工具(如自动解析参数的脚本),可以定制输出格式(如 JSON、固定分隔符)。
适用场景
让bdinfo 输出 JSON 格式,便于上位机脚本解析内存、波特率、自定义硬件版本等信息。
定制步骤
步骤 1:修改核心打印函数为 JSON 格式
以print_num 为例,修改为 JSON 键值对格式:
__maybe_unusedstatic void print_num(const char *name, ulong value){// 原厂格式:printf("%-12s= 0x%08lXn", name, value);// 定制为JSON格式(注意逗号分隔,最后一个字段无逗号)printf(" "%s": "0x%08lX",n", name, value);}// 同步修改自定义打印函数为JSON格式__maybe_unusedstatic void print_board_version(const char *name, u32 version){printf(" "%s": "V%02d.%02d"n", name, (version>>8)&0xFF, version&0xFF);}
步骤 2:包裹整体输出为 JSON 结构
在do_bdinfo 函数开头/结尾添加 JSON 首尾标识:
static int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[]){bd_t *bd = gd->bd;// JSON开头printf("{n");print_num("arch_number",bd->bi_arch_number);print_bi_boot_params(bd);print_bi_dram(bd);// ... 其余打印逻辑(含自定义)print_board_version("Board Ver", gd->arch.board_version);// JSON结尾printf("}n");return 0;}
定制后输出效果
{"arch_number": "0x00000000","boot_params": "0x80000100","DRAM bank": "0x00000000","-> start": "0x80000000","-> size": "0x10000000","Board Ver": "V01.02"}
五、定制扩展的最佳实践
1.优先用条件编译,避免硬改:所有定制逻辑都用CONFIG_MY_BOARD_XXX 宏封装,便于不同板卡复用、开关;
2.复用原厂函数风格:新增打印函数时,对齐原厂的格式化规则(如%-12s 左对齐),保持输出可读性;
3.利用 __weak 函数扩展:如果需要修改架构级的核心逻辑(如 PPC 的 board_detail),优先用__weak 重写,而非直接修改原厂函数:
// 自研板卡重写board_detailvoid board_detail(void){print_num("Custom Param", 0x12345678);}
4.编译测试验证:
○编译:make my_board_defconfig && make,确保无编译错误;
○烧录:将新 U-Boot 烧录到板卡;
○验证:执行bdinfo 命令,检查打印内容是否符合预期。
六、总结
bdinfo.c 是 U-Boot 板级调试的“窗口”,通过裁剪冗余代码、扩展自定义信息、定制输出格式,既能减小 U-Boot 镜像体积,又能让调试信息精准匹配自研板卡的需求。
核心要点回顾:
1.裁剪:聚焦自研板卡的硬件特性,删除无关打印,用宏控制开关;
2.扩展:复用原厂打印风格,新增自定义辅助函数,在对应架构的do_bdinfo 中调用;
3.定制:适配调试工具的输出格式(如 JSON),兼顾可读性和自动化解析。
掌握这些技巧,让bdinfo 从“通用工具”变成贴合你自研板卡的“专属调试助手”!
全部0条评论
快来发表一下你的评论吧 !