
简介围绕飞腾FT2004/FT2000C平台uboot移植、合成与下载的PDF实战文档面向嵌入式Linux驱动、BSP开发及bootloader调试的初中级工程师也可供需要完成板卡适配与固件烧录任务的技术人员参考。压缩包仅含1个PDF文件大小约5.03MB以图文并茂的实操记录形式呈现便于离线查阅与对照复现。内容覆盖GCC-ARM V8.2交叉编译环境搭建、飞腾官方uboot源码定制FT2004C.h、FT2004C_defconfig及PEU1 X16配置、编译脚本编写、image_fix打包工具配置PCIE与DDR控制器、串口调试以及GZUT_EZP_XPro烧录flash等完整链路所有截图与测试结果均来自实测。目前已有504人学习可作为FT2004系列uboot移植的流程参考与排错依据帮助读者理解从源码修改到可启动镜像落地的关键节点。1. FT2004 板子上那条从源码到点亮的链路拿到一块 FT2004 的板子能用的资料往往只有一页原理图、一个调试串口和几个名字各异的二进制文件bl31.bin、u-boot.bin、fip.bin、.itb谁先谁后、各自烧到哪个地址没有一份能直接照抄的说明。FT2004 是 ARMv8-A 的四核处理器上电后由片内 BootROM 起步经固件层PBF 或 ARM Trusted Firmware 的 BL31把控制权交给 U-BootU-Boot 再去拉内核。这条链路上任何一环的加载地址、对齐方式或者设备树节点写错串口就是一行字都不吐。所谓移植是让 U-Boot 认识这块板的 DDR、串口、时钟、存储和网口所谓合成是把 BL31、U-Boot 和 DTB 按固件约定的偏移拼成一个可烧录镜像所谓下载是通过串口、网口或者烧录器把镜像送进 QSPI Flash、eMMC、SD 卡或直接加载到内存里跑起来。这三件事在 FT2004 上互相咬合单独做对一件没有意义。下面按准备工具链、板级移植、镜像合成、下载固化、调优排错的顺序把每一步能复现的命令和参数写清楚。2. FT2004 的 U-Boot 源码与 aarch64 交叉工具链怎么准备动手改代码之前先把编译环境和源码布局理顺。FT2004 上跑的都是 64 位 ARM 代码宿主机上装一套能用的 aarch64 工具链是底线。这一步做不扎实后面所有报错都会指向错误的方向。2.1 交叉工具链的选型与自检常见做法是用gcc-aarch64-linux-gnuDebian/Ubuntu 下直接apt install gcc-aarch64-linux-gnu也可以用厂商 SDK 里自带的裸机工具链。两者都能编 U-Boot区别在于前者带 glibc 头文件后者更干净编出来的镜像体积略小。BL31 和 PBF 一般用同一套工具链即可除非固件方明确要求某个版本。装完先自检别等到编译到一半才发现路径不对# 确认工具链可用输出里要有 aarch64 前缀 aarch64-linux-gnu-gcc -v # 写进环境变量注意结尾那个短横线不能丢 export ARCHarm export CROSS_COMPILEaarch64-linux-gnu- export PATH$PATH:/opt/toolchain/bin # 快速验证能否产出目标文件 aarch64-linux-gnu-gcc -c -o /tmp/t.o -xc - EOF int main(void){return 0;} EOFARCH在 U-Boot 里填arm而不是arm64这是很多人第一次编 64 位 U-Boot 时踩的坑CROSS_COMPILE结尾的短横线是 U-Boot Makefile 拼接工具名前缀用的漏掉会报aarch64-linux-gnu-gcc: not found。验证用的那段代码只是确认工具链能出目标文件输出物在 /tmp 下不影响仓库。SDK 目录建议按职责分开避免 U-Boot 的编译产物和固件产物混在一起sdk/ ├── boot/ │ ├── pbf/ # 片内固件通常是二进制形式交付 │ ├── atf/ # BL31 源码产物 bl31.bin │ └── uboot/ # 移植主战场 ├── images/ # 合成后的可烧录镜像 └── tools/ # mkimage、fiptool、打包脚本2.2 编译前先跑通一次原厂配置在改任何一行代码之前先确认这套源码本身能编出产物。这一步的价值在于把「源码问题」和「我的改动问题」分开。用厂商提供的板级配置名跑一遍配置名以源码configs/目录下实际存在的为准cd sdk/boot/uboot make distclean make ft2004_defconfig # 板级配置名按 defconfig 实际文件名替换 make -j$(nproc) 21 | tee build.logdistclean清掉上一次编译留下的.config和中间文件避免旧配置残留-j$(nproc)按 CPU 核数并行编译tee build.log把日志同时落到文件便于后面grep -i error定位。如果这一步就失败先解决工具链和源码完整性问题别急着动设备树。编译通过后make menuconfig可以进去核对几个关键项CONFIG_SYS_TEXT_BASEU-Boot 自身被加载的地址、CONFIG_BAUDRATE调试串口波特率、CONFIG_DEFAULT_DEVICE_TREE默认设备树文件名、CONFIG_SYS_LOAD_ADDRtftpboot这类命令的默认加载地址。这四个值的取值范围必须落在板子的 DDR 可用区间内抄别的板子的值十有八九起不来。2.3 产物清单与各自用途一次完整编译会吐出好几个文件名字相近用途完全不同。合成镜像时拿错文件是常见事故先对照这张表产物文件内容用途u-boot.bin纯二进制不含设备树单独烧写或作为 fip 的 nt-fwu-boot-nodtb.bin去掉 DTB 的 U-Boot与外部 DTB 拼 FIT 时用u-boot.dtbU-Boot 自己用的设备树拼 FIT、确认节点是否生效u-boot.itb打开 FIT 后自动生成的组合镜像部分平台可直接烧写u-bootELF 格式带符号表用 gdb 或 addr2line 定位崩溃System.map符号地址表分析启动卡死的现场地址判断某个配置项有没有真正生效最直接的办法是去.config里查而不是看menuconfig界面上的勾。看出u-boot.dtb的修改是否生效用fdtdump u-boot.dtb | grep -A3 uart之类的命令确认节点确实编进去了比反复烧板子快得多。提示u-boot.bin和设备树是两份独立数据烧写时如果只更新了其中一份会出现「驱动认得到、地址却不对」的诡异现象改动 dts 后一定要两份一起更新。3. 板级移植让 FT2004 跑出第一行串口输出移植的第一目标不是让 U-Boot 完整启动而是让串口先吐出第一行字。只要有一行输出就说明 DDR、时钟和串口三件事里至少有两条是对的后面的调试才有反馈渠道。这一章围绕设备树、板级配置和外设驱动三块展开。3.1 设备树里必须先对上的几个节点FT2004 平台的 U-Boot 使用设备树描述硬件根节点的compatible会和板级文件的匹配表比对对不上就走不到初始化流程。控制台输出靠chosen节点里的stdout-path指定别名则让命令行能用serial0这种短名字引用。/ { model FT2004 EVB; compatible phytium,ft2004, phytium,e2000; chosen { stdout-path uart0; /* 决定控制台往哪个串口打印 */ }; aliases { serial0 uart0; /* 让命令行可以用 serial0 引用 */ }; }; uart0 { status okay; clock-frequency 100000000; /* 必须与实际输入时钟一致否则波特率全错 */ };compatible的第一项要和板级文件的of_match表一致clock-frequency写错会出现「有输出但全是乱码」因为分频算出来的波特率偏离了 115200stdout-path指向的节点status必须是okay标成disabled就没输出。检查设备树有没有编对用dtc -I dtb -O dts u-boot.dtb反编译出来比对着看。3.2 defconfig 与板级开关的对应关系板级配置项在configs/下的 defconfig 里每一条都影响最终镜像行为。改的时候按用途分组别一股脑往里加。配置项含义常见错误CONFIG_TARGET_xxx选择板级目录和初始化代码名字和board/下目录不一致CONFIG_DEFAULT_DEVICE_TREE默认加载的 dts 名字与实际 dts 文件名差一个后缀CONFIG_SYS_TEXT_BASEU-Boot 重定位前的运行地址落在 DDR 未初始化区间CONFIG_SYS_LOAD_ADDR下载命令的默认落点与内核加载区重叠CONFIG_BAUDRATE调试串口波特率与上位机终端不一致修改后执行make olddefconfig让新增项补齐默认值再重新编译。想看某个配置是否真的被选中直接grep CONFIG_TARGET_xxx .config比在图形界面里翻菜单可靠。3.3 从有输出到能敲命令串口与存储的衔接串口有输出只是起点接下来要让命令行可用、让存储设备能被识别。U-Boot 进命令行前会初始化board_init、dram_init、misc_init_r等钩子DDR 容量报错会导致后续内存操作全部失败典型的症状是bdinfo里 DRAM 容量显示为 0 或明显偏小。# 板上执行确认 DDR 和启动参数 FT2004 bdinfo FT2004 dm tree FT2004 mmc list FT2004 sf probe 0bdinfo看 DDR 起始地址和容量dm tree看驱动模型里各设备有没有绑定成功mmc list和sf probe 0分别验证 eMMC/SD 和 SPI Flash 能不能被探测到。sf probe失败通常是 SPI 控制器节点的reg或片选引脚配错先回到设备树核对别急着怀疑硬件。注意DDR 初始化参数时序、容量、位宽属于最不该凭经验猜的部分务必用固件方给出的配置或参数表写错会表现为随机的偶发死机极难定位。4. 把 BL31、U-Boot 与 DTB 合成为可烧录镜像合成这一步决定了板子能不能从存储介质里被正确加载。核心是三件事各部分放在镜像的什么偏移、用什么工具拼、拼完怎么校验。FT2004 平台上常见的合成方式有 FIT 镜像和 fip 两种选哪种取决于固件层怎么解析。4.1 合成前的镜像布局动手拼之前先把布局画清楚。固件层加载时按固定偏移去取 BL31、U-Boot 和 DTB偏移对不上就加载到一片空白。下面是一种常见的布局示意具体数值必须以所用固件的说明为准偏移内容说明0x0引导头/预留区部分平台留出头部空间0x1000bl31.bin通常要求 4KB 对齐0x40000u-boot.bin与 BL31 之间留足空间0x100000u-boot.dtb或直接编进 FIT预留空间宁可留大不留小BL31 后续升级体积变大时不用重排整个布局。对齐要求来自固件层的解析逻辑常见的对齐粒度是 4KB 或 64KB。4.2 用 mkimage 合成 FIT 镜像FIT 的好处是把多份镜像和它们的加载地址、入口地址写在一份 its 描述文件里加载时由 U-Boot 自己解析不需要固件层知道固定偏移。/dts-v1/; / { description FT2004 firmware; #address-cells 1; images { bl31 { description ARM Trusted Firmware BL31; data /incbin/(bl31.bin); /* 相对 its 文件的路径 */ type firmware; os arm-trusted-firmware; arch arm64; compression none; load 0x80000000; /* 按实际内存布局填写 */ entry 0x80000000; }; uboot { description U-Boot; data /incbin/(u-boot-nodtb.bin); type firmware; os u-boot; arch arm64; compression none; load 0x80200000; entry 0x80200000; }; fdt { description FT2004 device tree; data /incbin/(u-boot.dtb); type flat_dt; arch arm64; compression none; }; }; configurations { default conf; conf { description FT2004 boot; firmware bl31; /* 先被加载并跳转的镜像 */ loadables uboot; /* 由前一级转交控制权 */ fdt fdt; }; }; };生成与检查mkimage -f ft2004.its firmware.itb mkimage -l firmware.itb # 列出各镜像的地址与校验信息firmware字段指定第一级被加载的镜像loadables是后续交接的对象两者顺序颠倒会直接卡在第一级load/entry必须落在有效 DDR 区间内且不能互相覆盖mkimage -l能列出每份子镜像的哈希和地址合成后必查一次比烧到板子上再猜要省事。4.3 fip 方式与直接拼接的差异如果固件层不解析 FIT就要用 fip 或者裸拼。fiptool 是 ATF 自带工具优点是会自动校验各段对齐fiptool create \ --soc-fw bl31.bin \ --nt-fw u-boot.bin \ fip.bin # 不打包、只按偏移直接拼时用 dd 预留空间再覆盖写入 dd if/dev/zero offw.bin bs1M count16 dd ifbl31.bin offw.bin bs1K seek4 convnotrunc dd ifu-boot.bin offw.bin bs1K seek256 convnotruncseek的单位由bs决定上面用bs1Kseek4就是偏移 4KB写错单位是最常见的低级错误convnotrunc保证不把已写入的内容截断。合成完做一次完整性校验sha256sum fw.bin cmp -n $(stat -c%s bl31.bin) bl31.bin (dd iffw.bin bs1K skip4 count64 2/dev/null)cmp确认镜像里对应区间的内容和源文件逐字节一致通过之后再进入烧写环节。5. 下载与烧写从串口 XMODEM 到 QSPI/eMMC镜像合成了接下来要把它送进板子。FT2004 平台上常用的路径有三条串口传文件到内存、网口 TFTP 到内存、以及把内存里的内容固化进 Flash。三条路径适用场景不同速度差得很远。5.1 内存下载XMODEM、YMODEM 与 TFTP串口下载不依赖网络只要串口通就能用代价是慢。U-Boot 侧执行接收命令宿主机侧用sx/sb或 kermit 发送# 宿主机把文件通过串口发给板子设备名按实际替换 sx -b firmware.itb /dev/ttyUSB0 /dev/ttyUSB0 # 或者用 kermit参数更可控 kermit -i -l /dev/ttyUSB0 -b 115200 -s firmware.itbFT2004 loadx ${loadaddr} # XMODEM 接收配合宿主机 sx FT2004 loady ${loadaddr} # YMODEM 接收配合 sb速度更快 FT2004 md ${loadaddr} 0x20 # 看接收到的头几个字确认不是空的loadx用 128 字节块loady用 1KB 块同样波特率下后者快将近八倍。传输完一定要用md看内存内容镜像没传完整时前几个字往往就是全 0 或全 F。有网络的情况下走 TFTP 更快FT2004 setenv ipaddr 192.168.1.100 FT2004 setenv serverip 192.168.1.10 FT2004 tftpboot ${loadaddr} firmware.itbipaddr是板子自己的地址serverip是宿主机上 TFTP 服务的地址两者必须在同一网段。tftpboot成功后filesize环境变量会自动更新为收到的字节数后面的写 Flash 命令直接引用它比自己算长度可靠。5.2 固化到 QSPI Flash / eMMC / SD内存里跑通之后才轮到固化。三种介质的命令和坑各不相同FT2004 sf probe 0 # 探测 SPI Flash FT2004 sf erase 0x0 0x200000 # 擦除 2MB地址和长度都是十六进制 FT2004 sf write ${loadaddr} 0x0 ${filesize} # 从内存写入 FlashFT2004 mmc dev 0 # 选中设备 0 FT2004 mmc write ${loadaddr} 0x0 0x1000 # 第三个参数是块数不是字节数sf erase的第二个参数是长度单位字节mmc write的第三个参数是从哪个块开始写多少块一块 512 字节写 2MB 要传0x1000而不是0x200000。这两个命令的参数单位不一致是烧写失败中最常见的来源之一。写之前务必先擦除Flash 只能把 1 写成 0不擦除直接写会得到一堆不可预期的数据。方式典型速度依赖适用场景串口 XMODEM/YMODEM慢只需串口首板点亮、网络未通TFTP快网口和 TFTP 服务日常迭代烧录器最快专用工具量产、批量刷写5.3 上电验证与环境变量落盘烧完不要立刻断电先在当前会话里验证能不能从烧写位置正常加载FT2004 sf probe 0; sf read ${loadaddr} 0x0 0x200000 FT2004 bootm ${loadaddr}sf read把刚写进去的内容读回内存bootm尝试启动能走到内核或 U-Boot 二次加载界面说明写入位置和长度都对。随后把启动命令固化下来FT2004 setenv bootcmd sf probe 0; sf read ${loadaddr} 0x0 0x200000; bootm ${loadaddr} FT2004 saveenvbootcmd是上电后自动执行的命令序列saveenv把它写进环境变量存储区。如果saveenv报Cannot save environment先确认环境变量分区在配置里指向了可写介质而不是默认的nowhere。6. 把 FT2004 的 U-Boot 调稳的几个技巧前几章走完板子基本能启动了剩下的问题往往更零碎网口时通时不通、偶发卡死、换个批次的核心板行为不一样。这一章集中处理这类疑难。6.1 网口不通的排查顺序千兆网口在移植阶段是最容易出问题的外设同类平台上的经验基本通用。按这个顺序查能覆盖大部分情况症状优先排查点mii info读不到 PHYPHY 地址、MDIO 时钟、复位 GPIO 时序能链路但 ping 不通RGMII 收发延时、时钟方向、PHY 模式配置大包丢、小包通DMA 描述符数量与缓冲区对齐FT2004 mii info # 逐个地址扫 PHY FT2004 mii read 0 0 # 读寄存器 0确认 PHY 是否响应PHY 复位时序是最容易被忽略的一环复位脚拉低后需要保持一段时间再释放很多板子因为释放太快导致 PHY 内部寄存器还没准备好表现为「第一次上电通、热重启不通」。这类问题在设备树里加一个复位延时节点往往就能解决。6.2 启动卡死的定位手段串口完全没有输出时先把早期调试串口打开让打印从 U-Boot 重定位之前就开始CONFIG_DEBUG_UARTy CONFIG_DEBUG_UART_BASE0x... CONFIG_DEBUG_UART_CLOCK100000000 CONFIG_DEBUG_UART_ANNOUNCEy这四个配置项让debug_uart_init在 SPL 或早期阶段就初始化串口输出的是不走驱动模型的裸打印能在 DDR 初始化之前就给出反馈。如果这样还是没有任何字符问题就在 DDR 初始化或更前面的固件层不在 U-Boot 本身此时应该换一个已知能启动的镜像回来对比。进了命令行之后bootstage report能列出各阶段耗时卡在哪一段一目了然dm tree看驱动绑定情况bdinfo看内存布局。偶发死机这类问题把System.map里的地址和串口打印出的 PC 值对照用addr2line -e u-boot -f -i 地址反查函数名比反复改代码试要快得多。本文还有配套的精品资源点击获取