uboot移植常见问题
U-Boot 的移植过程可以分为两个部分一是 U-Boot 的编译二是 U-Boot 的启动流程分析。目录一、U-Boot 编译1、总体编译链路2、第一层defconfig和Kconfig生成完整配置3、第二层Makefile.build选择并编译各目录源码4、第三层链接生成U-Boot ELF文件5、第四层生成设备树并封装i.MX启动镜像二、uboot的启动流程1、启动主线2、树状时序流程3、入口、初始栈与board_init_f3.1. Boot ROM和DCD3.2. _start不是head.o3.3 _main建立初始运行环境3.4. 早期UART分三层3.5. board_init_f()的重点4、重定位与board_init_r4.1. 为什么要重定位4.2. relocate_code()做什么4.3. board_init_r()的重点5、设备树、DM、MMC和环境5.1. U-Boot镜像中有设备树5.2. DM的bind和probe5.3. MMC控制器probe和卡枚举5.4. 默认环境和保存环境6、main_loop、bootcmd与Linux6.1. 自动启动6.2. 当前关键环境变量6.3. 默认MMC启动6.4. bootz跳入Linux三、uboot移植常见的问题1、总体思路2、移植前首先确定什么2.1. 硬件确认表2.2. DDR是第一优先级2.3. UART必须与PCB一致2.4. MMC编号不能想当然2.5. 选择参考板2.6. 当前工程基线3、通常需要修改哪些文件3.1. defconfig3.2. 板级Kconfig和Makefile3.3. 板级C文件3.4. imximage.cfg3.5. 板级配置头文件3.6. U-Boot控制设备树3.7. 不应优先修改的通用文件4、推荐移植顺序基础验证命令5、上板后按现象排查5.1. 完全没有串口输出5.2. 串口乱码5.3. 重定位附近停止5.4. DM中没有设备或probe失败5.5. MMC识别失败5.6. 环境修改不生效5.7. 网口不能使用5.8. U-Boot正常但Linux失败一、U-Boot 编译编译U-Boot时首先应该关注defconfig、各级Kconfig和Makefile。1、执行make mx6ull_14x14_evk_defconfig后scripts/kconfig/conf先递归解析根Kconfig以及其中source进来的各级Kconfig再读取板级defconfig作为初始配置根据select/default/depends on补齐依赖最终生成完整的.config。随后Kconfig系统把.config转换成Make可以读取的include/config/auto.conf。2、编译各目录时scripts/Makefile.build先包含auto.conf再包含例如drivers/net/Makefile其中obj-$(CONFIG_FEC_MXC) fec_mxc.o会根据配置展开为obj-y从而决定是否编译该驱动。各目录的.c/.S先编译成.o再聚合成built-in.o。3、最后链接器使用u-boot.lds把start.o和各目录的built-in.o链接成U-Boot ELF文件u-boot不是Linux的vmlinux。其中vectors.S先生成vectors.o并进入arch/arm/lib/built-in.o最终由链接脚本的*(.vectors)规则将其.vectors段放到代码最前面并通过ENTRY(_start)指定_start为U-Boot入口随后才排列start.o和其他代码。4、通过DTC生成U-Boot控制设备树dts/dt.dtb两者拼成u-boot-dtb.bin最后使用mkimage和imximage.cfg封装成本板最终镜像u-boot-dtb.imx。1、总体编译链路mx6ull_14x14_evk_defconfig 各级Kconfig ↓ scripts/kconfig/conf .config ↓ silentoldconfig include/config/auto.conf include/configs/mx6ullevk.h ↓ scripts/Makefile.autoconf include/autoconf.mk auto.conf autoconf.mk ↓ scripts/Makefile.build 各目录Makefile中的obj-y ↓ .c/.S → .o ↓ ld -r built-in.o ↓ u-boot.lds u-bootELF ↓ objcopy u-boot-nodtb.bin imx6ull-14x14-evk.dts ↓ DTC dts/dt.dtb u-boot-nodtb.bin dts/dt.dtb ↓ 拼接 u-boot-dtb.bin ↓ mkimage imximage.cfg u-boot-dtb.imx2、第一层defconfig和Kconfig生成完整配置执行make mx6ull_14x14_evk_defconfig顶层Makefile的%config规则会递归进入scripts/kconfig构建并运行主机端配置工具conf。主要过程是conf_parse(Kconfig) │ // 解析根Kconfig │ └─ 递归解析所有source进来的Kconfig // 建立symbol、菜单、默认值和依赖关系 conf_read(configs/mx6ull_14x14_evk_defconfig) │ // 读取板级defconfig中明确给出的y/n/string值 │ // 这些值只是用户种子配置不是最终结果 │ └─ 计算select/default/depends on/choice // 补齐依赖、默认值以及未在defconfig中出现的选项 ↓ conf_write() // 输出完整.config需要注意defconfig不是直接复制成.config。例如某个选项被设置为y时它可能通过select带出其他选项未满足depends on时配置也可能无法生效。随后配置系统将.config转换成Make可以直接包含的.config ↓ silentoldconfig include/config/auto.conf这份U-Boot 2017.03仍处于新旧配置机制并存阶段还存在一条旧式板级配置通道include/configs/mx6ullevk.h ↓ C预处理并提取CONFIG宏 include/autoconf.mk因此本树正式编译时会同时使用include/config/auto.conf include/autoconf.mk例如CONFIG_CMD_NETy主要来自defconfig/Kconfig进入auto.conf。CONFIG_FEC_MXC在mx6ullevk.h中定义进入旧式autoconf.mk。3、第二层Makefile.build选择并编译各目录源码顶层Makefile会使用类似下面的方式递归构建目录$(MAKE) $(build)drivers/net其中$(build)近似展开为-f scripts/Makefile.build objdrivers/netscripts/Makefile.build的主要工作是1. 包含include/config/auto.conf 2. 包含include/autoconf.mk 3. 包含目标目录的Kbuild没有Kbuild时包含Makefile 4. 解析obj-y、obj-$(CONFIG_*)和subdir-y 5. 编译.c/.S文件 6. 聚合该目录的built-in.o 7. 递归处理选中的子目录以FEC网卡驱动为例drivers/net/Makefile中有obj-$(CONFIG_FEC_MXC) fec_mxc.o当配置为CONFIG_FEC_MXCyMake展开结果相当于obj-y fec_mxc.o因此形成drivers/net/fec_mxc.c ↓ 编译 drivers/net/fec_mxc.o ↓ ld -r聚合 drivers/net/built-in.o如果CONFIG_FEC_MXCn或没有定义左边会变成obj-n或obj-不会进入最终的obj-y依赖图因此该驱动不会被编译进U-Boot。built-in.o不是最终程序也不是单独驱动文件它是一个可重定位目标文件用来汇总某个目录中所有被选中的.o方便顶层统一链接。4、第三层链接生成U-Boot ELF文件各目录完成编译后顶层链接器使用u-boot.lds将下面这些内容链接起来arch/arm/cpu/armv7/start.o 各目录的built-in.o 平台运行库最终得到u-boot // U-Boot的ELF文件 // 不是Linux内核的vmlinuxARM链接脚本中的关键部分为ENTRY(_start) .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) *(.text*) }对应关系是arch/arm/lib/vectors.S ↓ 汇编 arch/arm/lib/vectors.o ↓ 聚合 arch/arm/lib/built-in.o虽然vectors.o已经位于arch/arm/lib/built-in.o中但最终链接时u-boot.lds会把其中的.vectors输入段单独抽取并放到.text最前面然后排列start.o和其他.text代码。最终代码开头大致是_start: // vectors.S b reset ↓ reset: // arch/arm/cpu/armv7/start.S ↓ 其他U-Boot代码因此ENTRY(_start)指定ELF入口符号。*(.vectors)保证ARM异常向量段位于代码前部。CPUDIR/start.o (.text*)把CPU启动代码紧随其后。.vectors的最终位置由u-boot.lds保证不是每个目录生成built-in.o时统一保证的。5、第四层生成设备树并封装i.MX启动镜像链接得到u-bootELF后通过objcopy去掉ELF符号表等信息生成裸二进制u-bootELF ↓ objcopy u-boot-nodtb.bin板级defconfig指定CONFIG_DEFAULT_DEVICE_TREEimx6ull-14x14-evk因此DTC会将U-Boot控制设备树编译为arch/arm/dts/imx6ull-14x14-evk.dts ↓ CPP DTC dts/dt.dtb启用CONFIG_OF_SEPARATE后顶层Makefile执行u-boot-nodtb.bin dts/dt.dtb ↓ 二进制拼接 u-boot-dtb.bin最后tools/mkimage使用板级imximage.cfg进行i.MX镜像封装u-boot-dtb.bin board/freescale/mx6ullevk/imximage.cfg ↓ tools/mkimage -T imximage u-boot-dtb.imximximage.cfg提供i.MX启动镜像所需的重要信息包括IVT和Boot Data相关布局。启动介质类型。DCD寄存器配置。DDR控制器初始化参数。U-Boot入口地址。最终的u-boot-dtb.imx才是本板默认烧写和启动使用的镜像。这里要区分两份设备树imx6ull-14x14-evk.dtb // 编进u-boot-dtb.imx供U-Boot自己的Driver Model使用 100ask_imx6ull-14x14.dtb // U-Boot启动Linux时从存储设备加载并传给Linux二、uboot的启动流程1、启动主线当前工程基线SoCi.MX6ULL 14x14 DDR512 MiB0x800000000x9fffffff 配置mx6ull_14x14_evk_defconfig 镜像u-boot-dtb.imx 默认路径非SPLMMC启动失败后网络启动完整主线Boot ROM ↓ 执行DCD、初始化DDR、装载U-Boot _start → reset → _main ↓ board_init_f // 重定位前初始化并规划DDR布局 ↓ relocate_code // 复制U-Boot并修正地址 ↓ board_init_r // DM、MMC、环境、控制台、网络 ↓ main_loop // 执行bootcmd或进入命令行 ↓ 加载zImage和Linux DTB ↓ bootz → boot_jump_linux2、树状时序流程i.MX6ULL上电/复位 │ ├─ Boot ROM │ // 根据Boot拨码或熔丝选择启动介质 │ ├─ 解析u-boot-dtb.imx │ // 读取IVT、Boot Data、DCD和入口地址 │ ├─ 执行imximage.cfg中的DCD │ // 初始化DDR IOMUX和MMDC控制器 │ ├─ 将U-Boot装入DDR并跳到入口 │ └─ _start() [arch/arm/lib/vectors.S] // ARM异常向量入口第一条指令跳到reset │ └─ reset() [arch/arm/cpu/armv7/start.S] // 设置CPU、CP15、cache/TLB和低级运行状态 │ └─ _main() [arch/arm/lib/crt0.S] // 建立初始C运行环境 │ ├─ sp CONFIG_SYS_INIT_SP_ADDR │ // 初始栈位于片内OCRAM │ ├─ board_init_f_alloc_reserve() │ // 预留早期malloc和global_data │ ├─ r9 gd │ // ARM使用r9保存gd指针 │ ├─ board_init_f_init_reserve() │ // 初始化gd │ └─ board_init_f(0) [common/board_f.c] // 重定位前初始化与内存规划 │ ├─ fdtdec_setup() │ // 找到U-Boot控制设备树 │ ├─ arch_cpu_init()/initf_dm() │ // CPU基础初始化和早期DM │ ├─ board_early_init_f() │ // mx6ullevk.c配置UART1引脚 │ ├─ serial_init()/console_init_f() │ // 初始化早期串口和控制台 │ ├─ dram_init() │ // 检测并记录gd-ram_size │ └─ reserve_*()/setup_reloc() // 从DDR顶部预留U-Boot、malloc、FDT、gd、栈等 // 计算gd-relocaddr和新栈 │ └─ 返回_main() │ ├─ 切换新的DDR栈和gd ├─ relocate_code() │ // 复制U-Boot修正.rel.dyn ├─ relocate_vectors() │ // 切换异常向量 ├─ clear_bss │ // 清零BSS └─ board_init_r(gd, relocaddr) // 重定位后完整初始化 │ ├─ initr_caches()/initr_malloc() │ // cache和正式malloc区 ├─ initr_dm() │ // 扫描完整U-Boot控制设备树 ├─ board_init() │ // 板级boot参数和外设配置 ├─ initr_mmc() │ // probe USDHC主控制器 ├─ initr_env() │ // 读取MMC环境失败则用默认环境 ├─ console_init_r()/interrupt_init() │ // 完整控制台和中断 ├─ board_late_init() │ // 设置板型和运行时环境 ├─ initr_net() │ // 初始化FEC、MDIO和PHY └─ main_loop() // 自动启动或进入命令行 │ ├─ bootdelay_process() │ // 获取bootdelay和bootcmd ├─ autoboot_command() │ // 未被按键打断时执行bootcmd └─ cli_loop() // 启动失败或被打断后进入命令行3、入口、初始栈与board_init_f3.1. Boot ROM和DCDBoot ROM不属于U-Boot源码它负责选择启动介质。解析i.MX镜像。执行DCD初始化DDR。装载U-Boot并跳到入口。因此DCD负责首次初始化DDR dram_init()主要检测并记录容量DCD错误可能导致完全没有串口输出。3.2._start不是head.ovectors.S → vectors.o → arch/arm/lib/built-in.o start.S → start.o链接脚本用ENTRY(_start)指定入口并把.vectors放在前面_start: b resetvectors.S生成的是vectors.o单独的head-y对象是start.o。3.3_main建立初始运行环境_main位于arch/arm/lib/crt0.SOCRAM初始栈 ↓ 预留并初始化gd ↓ r9保存gd ↓ board_init_f(0)gd保存DDR容量、重定位地址、新栈、FDT、控制台和bd等全局状态。3.4. 早期UART分三层board_early_init_f() // UART1 PAD复用 ↓ serial_init() // 配置MXC UART寄存器和波特率 ↓ console_init_f() // 启用U-Boot早期控制台三者必须与PCB使用的UART控制器一致。3.5.board_init_f()的重点board_init_f()不是简单初始化栈它还会初始化CPU、定时器、早期DM和UART。找到U-Boot控制FDT。调用dram_init()获取DDR容量。从DDR顶部向下预留运行区。计算重定位地址、新栈和新gd。board_init_f()负责规划 relocate_code()负责真正复制4、重定位与board_init_r4.1. 为什么要重定位当前初始链接地址CONFIG_SYS_TEXT_BASE 0x87800000Boot ROM只知道固定装载地址不知道实际DDR容量以及MMU、LCD、malloc、FDT、gd和栈需要多少空间。board_init_f()检测实际内存后才能在DDR高端动态安排最终运行位置。4.2.relocate_code()做什么复制U-Boot到gd-relocaddr ↓ 处理.rel.dyn重定位项 ↓ 切换异常向量 ↓ 清零BSS ↓ 进入board_init_r()这样可把低地址留给Linux内核和DTB例如zImage0x80800000 Linux DTB0x83000000 initrd0x838000004.3.board_init_r()的重点malloc/cache ↓ 完整DM扫描 ↓ board_init ↓ MMC控制器 ↓ 环境变量 ↓ 完整控制台和中断 ↓ board_late_init ↓ FEC/PHY ↓ main_loopmx6ullevk.c提供运行时板级hook函数作用board_early_init_f()早期UART pinmuxdram_init()检测DDR容量board_init()boot参数和板级外设board_late_init()启动模式和运行时环境board_phy_config()PHY特殊设置5、设备树、DM、MMC和环境5.1. U-Boot镜像中有设备树U-Boot DTS ↓ DTC dts/dt.dtb ↓ 与u-boot-nodtb.bin拼接 u-boot-dtb.bin ↓ i.MX封装 u-boot-dtb.imx需要区分U-Boot控制DTB // 封装在U-Boot镜像中供DM使用 Linux DTB // 从MMC/TFTP加载bootz时传给Linux5.2. DM的bind和probeinitr_dm() ↓ 扫描status有效的节点 compatible匹配driver.of_match ↓ device_bind()创建udevice并加入UCLASS后续子系统才触发probeinitr_mmc() ↓ 遍历UCLASS_MMC device_probe() ↓ fsl_esdhc_probe()所以bind成功 ≠ probe成功 ≠ SD/eMMC卡枚举成功5.3. MMC控制器probe和卡枚举fsl_esdhc_probe() // 读取reg、bus-width、CD、电源和时钟 // 建立struct mmc mmc_init() // 发送CMD0、CMD8、ACMD41/CMD1读取CID/CSD本板initr_env()读取MMC环境时通常会触发完整卡枚举。5.4. 默认环境和保存环境mx6ullevk.h中的CONFIG_BOOTCOMMAND CONFIG_EXTRA_ENV_SETTINGS在编译时形成default_environment[]运行时MMC环境CRC有效 → 导入MMC环境覆盖编译默认值 MMC环境无效 → 使用default_environment[]只有执行saveenv才会把RAM中的环境写入MMC。当前环境位置设备mmc 1 硬件分区user area 偏移896 KiB 大小8 KiB6、main_loop、bootcmd与Linux6.1. 自动启动main_loop() │ ├─ bootdelay_process() │ // 读取bootdelay和bootcmd ├─ 未按键中断 │ └─ autoboot_command(bootcmd) └─ 被中断或命令返回 └─ cli_loop()6.2. 当前关键环境变量mmcdev1 mmcpart2 bootdir/boot imagezImage fdt_file100ask_imx6ull-14x14.dtb loadaddr0x80800000 fdt_addr0x83000000 consolettymxc06.3. 默认MMC启动bootcmd │ ├─ mmc dev 1; mmc rescan ├─ 尝试加载mmc 1:2中的boot.scr │ └─ 找到则source执行由脚本接管启动 │ └─ 没有boot.scr ├─ 加载/boot/zImage到0x80800000 ├─ run mmcargs │ // 生成console和root/dev/mmcblk1p2等bootargs ├─ 加载Linux DTB到0x83000000 └─ bootz ${loadaddr} - ${fdt_addr}内核加载失败时执行网络启动DHCP/TFTP加载zImage和Linux DTB ↓ 设置NFS root bootargs ↓ bootz6.4.bootz跳入Linux检查zImage和DTB ↓ boot_prep_linux() // 修正FDT、chosen、bootargs和内存信息 ↓ boot_jump_linux() // 跳到内核入口ARM 32-bit关键寄存器r0 0 r1 machid r2 Linux DTB地址 PC zImage入口进入Linux后U-Boot不再运行Linux会重新初始化自己的设备和驱动。三、uboot移植常见的问题1、总体思路U-Boot移植不是先改驱动而是确认目标板硬件 ↓ 选择最接近的参考板 ↓ 列出硬件差异 ↓ 修改板级配置、DCD、DTS和代码 ↓ 先跑通DDR、UART和命令行 ↓ 再验证MMC、环境、网络和Linux排查时始终寻找“第一个失败的阶段”。2、移植前首先确定什么第一步应拿到原理图、DDR资料、SoC手册、启动介质布局和烧写说明。2.1. 硬件确认表硬件必须确认配置错误的常见表现SoCi.MX6UL/ULL、14x14/9x9、版本PAD、寄存器或时钟不匹配DDR型号、容量、位宽、颗粒、时序、走线无输出、重定位死机、运行不稳定启动介质SD/eMMC/NAND/QSPI、Boot拨码、烧写偏移Boot ROM找不到镜像UART控制器、TX/RX PAD、时钟、波特率、电平无输出或乱码MMCUSDHC编号、4/8位、CD、供电、RESET控制器或卡识别失败网口FEC1/2、RMII/MII、PHY地址、复位、50 MHz找不到PHY或没有Link电源PMIC、电源轨、时序、使能GPIO外设无响应或随机复位其他I2C/SPI/USB/LCD的控制器、引脚和地址probe失败或总线无波形2.2. DDR是第一优先级i.MX6ULL默认非SPL启动路径中Boot ROM ↓ 执行imximage.cfg中的DCD 初始化DDR ↓ 装载并运行U-Boot需要区分imximage.cfg中的DCD // 真正初始化DDR控制器和IOMUX dram_init() // U-Boot运行后检测并记录gd-ram_size修改PHYS_SDRAM_SIZE只能改变U-Boot认为的容量不能修复错误的DDR时序。DDR型号、位宽、频率或走线变化时应重新生成并验证DCD。2.3. UART必须与PCB一致早期串口需要同时匹配PCB连接 board_early_init_f()中的pinmux CONFIG_MXC_UART_BASE UART时钟 串口工具设置consolettymxc0主要传给Linux不会替代U-Boot早期UART的引脚和控制器配置。2.4. MMC编号不能想当然需要分别确认USDHC1/USDHC2 // SoC控制器编号 U-Boot mmc 0/mmc 1 // U-Boot aliases和枚举编号 /dev/mmcblk0/1 // Linux枚举编号三者可能对应但不能脱离两套设备树直接推断。2.5. 选择参考板参考板优先级相同SoC和封装 ↓ 相同DDR类型、位宽和拓扑 ↓ 相同启动介质 ↓ 相同UART、USDHC、FEC实例 ↓ 相同PHY和PMIC建议先建立差异表项目参考板目标板修改位置DDR512 MiB256 MiBDCD、容量和内存布局UARTUART1UART4pinmux、基地址、consoleSD卡USDHC1USDHC2DTS、aliases、环境和bootcmd网口FEC2/PHY1FEC1/PHY0DTS、复位、时钟和PHY地址2.6. 当前工程基线SoCi.MX6ULL 14x14 DDR512 MiB 早期UARTUART1 U-Boot控制DTSimx6ull-14x14-evk.dts mmc0USDHC1GPIO1_19低有效CD mmc1USDHC2non-removable 默认环境mmc 1用户区偏移896 KiB大小8 KiB 默认启动mmc 1:2中的/boot/zImage和Linux DTB 网口FEC2、RMII、PHY地址1 最终镜像u-boot-dtb.imx 参考烧写偏移SD卡起始后1 KiB3、通常需要修改哪些文件3.1. defconfig路径configs/myboard_defconfig作用选择SoC和目标板。选择命令和驱动。指定U-Boot控制设备树。指定i.MX镜像配置文件。示例CONFIG_TARGET_MYBOARDy CONFIG_DEFAULT_DEVICE_TREEmyboard CONFIG_SYS_EXTRA_OPTIONSIMX_CONFIGboard/myvendor/myboard/imximage.cfg CONFIG_OF_CONTROLy CONFIG_DM_MMCy CONFIG_DM_ETHy执行defconfig后应检查最终.config因为defconfig只是配置种子Kconfig还会处理select/default/depends on。3.2. 板级Kconfig和Makefile路径board/myvendor/myboard/Kconfig board/myvendor/myboard/MakefileKconfig重点SYS_BOARDmyboard SYS_VENDORmyvendor SYS_CONFIG_NAMEmyboard它们决定板级目录和include/configs/myboard.h。Makefile决定编译哪些板级文件obj-y : myboard.o文件存在但没有进入obj-y就不会进入最终U-Boot。3.3. 板级C文件路径board/myvendor/myboard/myboard.c常见hookboard_early_init_f() // 早期UART等pinmux dram_init() // 检测并记录DDR容量 board_init() // boot参数地址及板级控制器初始化 board_late_init() // 板型、启动来源和运行时环境变量 board_phy_config() // PHY特殊寄存器配置具体职责取决于当前版本和DM/legacy路径不要把所有设备初始化都硬编码到板级C文件。3.4.imximage.cfg路径board/myvendor/myboard/imximage.cfg主要包含i.MX Boot ROM镜像配置。DCD寄存器写入。DDR IOMUX和MMDC参数。DCD错误常见表现为完全无输出、随机死机或重定位失败。DDR变化时必须重新验证DCD。3.5. 板级配置头文件路径include/configs/myboard.hU-Boot 2017.03仍有大量传统配置宏重点检查PHYS_SDRAM_SIZE CONFIG_MXC_UART_BASE CONFIG_SYS_MMC_ENV_DEV CONFIG_SYS_MMC_ENV_PART CONFIG_ENV_OFFSET/CONFIG_ENV_SIZE CONFIG_MMCROOT CONFIG_FEC_ENET_DEV CONFIG_FEC_MXC_PHYADDR CONFIG_ETHPRIME CONFIG_BOOTCOMMAND CONFIG_EXTRA_ENV_SETTINGS其中硬件和环境后端宏 // 编译期参数 CONFIG_BOOTCOMMAND/CONFIG_EXTRA_ENV_SETTINGS // 形成default_environment[]默认环境模板MMC中已有的有效环境会覆盖新编译的默认环境。3.6. U-Boot控制设备树路径arch/arm/dts/myboard.dts重点检查compatible、reg、status pinctrl bus-width、cd-gpios、non-removable regulator phy-mode、phy-handle、reset-gpios clocks和aliases必须区分U-Boot控制DTB // 封装进u-boot-dtb.imx供U-Boot DM使用 Linux DTB // U-Boot从文件系统加载bootz时传给Linux修改Linux DTS不会改变U-Boot阶段的DM行为修改U-Boot DTS后必须重新生成并烧写镜像。3.7. 不应优先修改的通用文件除非确认驱动本身缺少支持否则不要首先修改drivers/mmc/fsl_esdhc.c drivers/net/fec_mxc.c drivers/core/ common/board_f.c、board_r.c start.S、crt0.S板级差异应优先放在defconfig、板级目录、配置头、DTS和DCD中。4、推荐移植顺序1. 验证原始参考板配置和工具链 ↓ 2. Boot ROM能识别镜像 ↓ 3. DCD正确初始化DDR ↓ 4. UART稳定输出 ↓ 5. board_init_f和重定位成功 ↓ 6. 进入board_init_r和命令行 ↓ 7. MMC识别、分区和读文件 ↓ 8. 环境变量读取和保存 ↓ 9. PHY、Link、ping和TFTP ↓ 10. 加载zImage和Linux DTB ↓ 11. 启动Linux和根文件系统关键原则先跑通“DDR UART 命令行”再逐个增加外设。基础验证命令进入命令行后先执行version bdinfo printenvMMC只读验证dm tree mmc list mmc dev 0 mmc info mmc part网络验证mii info mdio list ping ${serverip} dhcp tftp ${loadaddr} zImageLinux启动建议先拆开bootcmdmmc dev ${mmcdev} run loadimage run loadfdt run mmcargs printenv bootargs bootz ${loadaddr} - ${fdt_addr}5、上板后按现象排查5.1. 完全没有串口输出供电/复位 ↓ Boot拨码和烧写偏移 ↓ IVT/Boot Data ↓ DCD和DDR ↓ UART控制器、时钟和pinmux ↓ 串口接线、电平和波特率无输出不一定是UARTDCD失败可能让CPU根本走不到串口初始化。5.2. 串口乱码检查波特率、8N1、流控、UART时钟、晶振、电平和控制器选择。5.3. 重定位附近停止重点检查DCD、DDR稳定性、容量、gd-ram_size、gd-relocaddr和顶部保留区。OCRAM阶段正常、开始大量使用DDR后失败 → 优先怀疑DDR和内存布局5.4. DM中没有设备或probe失败dm tree中没有节点 → CONFIG、U-Boot DTB、status、compatible、驱动是否链接 节点存在但probe failed → reg、aliases、时钟、pinctrl、GPIO、regulator和硬件节点能bind只证明compatible匹配并创建了udevice不代表硬件初始化成功。5.5. MMC识别失败现象所在层次优先检查No MMC device available控制器未注册CONFIG、DT bind/probe、驱动probe failed: -22控制器probereg、aliases和USDHC时钟MMC: no card present卡检测CD GPIO、极性、卡座Card did not respond...卡协议供电、RESET、CLK/CMD/DAT0识别后读写错误数据传输bus-width、DAT线、频率、信号质量mmc info成功但文件失败上层存储分区、文件系统、路径初始枚举使用低速1-bit模式早期超时时先查供电、CLK、CMD和DAT0。5.6. 环境修改不生效default_environment[] // 编译进镜像的默认模板 MMC中的有效环境 // 运行时覆盖默认模板检查CONFIG_SYS_MMC_ENV_DEV/PART、CONFIG_ENV_OFFSET/SIZE和CRC。必要时env default -a printenv saveenv保存前必须确认环境区域不会覆盖其他数据。5.7. 网口不能使用PHY供电和复位 ↓ RMII 50 MHz时钟 ↓ MDC/MDIO读取PHY ID ↓ 自动协商和Link ↓ ping ↓ DHCP/TFTP检查FEC实例、PHY地址、复位GPIO、参考时钟、phy-handle、MAC地址和ethprime。5.8. U-Boot正常但Linux失败现象排查方向找不到zImage/DTBMMC、分区、路径和文件名bootz失败镜像格式和加载地址Starting kernel...后无输出Linux DTB、console、内核支持无法挂载rootfsroot、mmcblk编号、文件系统和根分区不要把U-Boot控制DTB当作Linux DTB传给内核。