
简介在 Zynq SoC 平台下基于 Linux v2.13.6 的调试宏典型应用示例面向嵌入式驱动开发与系统调试人员解决 ARMFPGA 异构架构中硬件交互复杂、问题定位难等常见场景。压缩包内共 2 个文件其中 C 文件用于实现 Zynq 硬件接口访问C 文件提供与 Bebob/Yamaha 设备相关的驱动调试代码整体大小仅 2KB结构紧凑便于直接阅读核心逻辑。该压缩包发布后已有 191 人学习下载常用于参考调试宏在真实驱动代码中的定义方式与触发条件。通过分析这两个文件可以掌握 DEBUG_printf、assert 等调试宏在 Zynq Linux 中的布局思路理解如何在不改动整体代码结构的前提下输出关键状态信息并能在驱动与硬件协作异常时快速划定排查范围。示例中还包含了条件编译和断言逻辑的具体用法对中断处理、寄存器状态检查等调试场景具有直接借鉴意义。1. 拿到 zynq.rar_V2你面对的不是一个压缩包而是一条启动链一个zynq.rar_V2工程包在很多人看来就是一堆elf、bit、dts文件的随意堆放但在做过 Zynq Linux 移植的人眼里这其实是一个完整启动链路的快照从片内 BootROM 读 FSBLFSBL 初始化 DDR 和时钟跳转 U-Boot再由 U-Boot 把内核和设备树加载进内存。版本号 V2 通常意味着硬件工程迭代过一轮外设地址、DDR 容量或者 PL 侧 bitstream 有变化。这篇文章就顺着这个压缩包常见的样子把解包、构建 BOOT.bin、制作 SD 卡启动盘和典型报错一次讲透适合要接手 Zynq Linux 工程、或者准备从零跑起一块 Zynq-7020 开发板的人。2. Zynq Linux 启动链路与压缩包里 V2 工程的目录结构2.1 先从 BootROM 到 Linux 的链路搞清“FSBL 为什么绕不开”Zynq-7000 的启动不是一个“引导 Linux”动作而是多级加载。上电后片内 BootROM 根据 MIO[4:6] 的电平决定从 QSPI、SD、NAND 还是 JTAG 读第一段镜像。这一段就是 FSBLFirst Stage Boot Loader它做三件事初始化 PS 侧 DDR、PLL 和 MIO如果 PL 侧有逻辑把 bitstream 配置进 FPGA然后跳转 U-Boot。U-Boot 作为第二级负责从文件系统或裸分区读内核镜像最终才进入 Linux。这个链路上最关键也最容易出问题的环节就是 FSBL——FLASH 操作时报a valid fsbl file is required for flash operation本质上就是工具没找到这个第一级引导文件。所以拿到zynq.rar_V2后第一件事不是急着编译内核而是先确认包里的fsbl.elf是否存在、是否与当前硬件导出版本匹配。V2 工程的“V2”经常就体现在这里硬件工程更新后FSBL 必须重新编译否则 DDR 初始化参数和时钟配置还是旧版内核起一半就会挂。2.2 解压 zynq.rar_V2识别硬件导出、镜像与设备树三类文件在 Linux 主机上解压.rar一般优先用 7z 或 unrar文件名带空格或中文时注意乱码问题sudo apt install p7zip-full unrar 7z x zynq.rar_V2.rar -o ./zynq_v2 cd zynq_v2 ls -lR | head -n 80如果解压出来文件名是乱码常见原因是压缩包在 Windows 下用 GBK 编码创建可以这样处理ls | convmv -f GBK -t UTF-8 --notest解压后重点找三类文件。第一类是硬件导出Vivado 工程里的.xsa2020.1 之后或.hdf之前它包含 PS 配置、地址空间和 PL bitstream 的描述。第二类是镜像文件fsbl.elf、u-boot.elf、image.ub、BOOT.bin。第三类是设备树源码.dts或编译好的.dtb。常见的目录布局大致是zynq_v2/ ├── hardware/ │ ├── zynq_top.xsa │ ├── zynq_top.bd │ └── constraints/ ├── software/ │ ├── fsbl.elf │ ├── u-boot.elf │ └── image.ub ├── boot/ │ ├── BOOT.bin │ └── boot.scr └── device_tree/ └── system.dts这个结构不是标准但大多数工程逃不出这个骨架。image.ub是 FIT 格式统一镜像把内核、设备树、ramdisk 封在一个文件里U-Boot 用它就不用分别加载内核和 dtb这也是 Zynq Linux 常见做法中比裸uImage 独立 dtb 更稳的方案。2.3 两版工程对比V2 到底改了硬件还是只改了设备树拿到 V2 版本经验是先做差异分析再动手。如果手里有 V1 的包直接对比diff -rq zynq_v1/src zynq_v2/src md5sum zynq_v2/hardware/zynq_top.xsa zynq_v1/hardware/zynq_top.xsa差异通常集中在三处.xsa变了说明 Vivado 工程动过可能是 DDR 型号、UART 引脚或 PL 逻辑升级设备树.dts变了说明外设映射调整fsbl.elf变了说明与新的.xsa配套。这里有个容易误用的地方有人拿到新压缩包后直接沿用旧 FSBL省掉重编这一步结果 U-Boot 能跑但 Linux 挂在Unhandled exception。判断依据很简单FSBL 里嵌了硬件配置的二进制描述.xsa一变它就必须重编。3. 生成 BOOT.bin 与 image.ubPetaLinux 和 boot.bif 两条路3.1 选型PetaLinux 管全套还是 bootgen 手工出镜像构建 Zynq Linux 镜像业界主流有两条路。一条是用 PetaLinux它对 Zynq 的封装很成熟从内核配置、设备树编译到打包一条命令全搞定另一条是手工方式用 Xilinx 提供的交叉编译工具链编内核再用bootgen和dtc自己拼装镜像。前者适合快速跑通和版本迭代后者适合需要深度定制内核、或者想让构建过程完全可控的场景。我一般给的判断标准是如果压缩包里给了完整的.xsa且你装好了 PetaLinux走第一条路效率高如果包内只有fsbl.elf、u-boot.elf和image.ub这些现成产物直接手工生成BOOT.bin就够了没必要建一套完整 PetaLinux 工程。注意 PetaLinux 和 Vivado 版本需要配套比如热词里提到的 PetaLinux 2025.1 对应 Vivado 2025.1拿 2024.2 的 PetaLinux 去打开 2025.1 的.xsa会直接拒绝导入。3.2 PetaLinux 最小流程从硬件导出到 BOOT.bin、boot.scr、image.ub假设.xsa路径是/home/user/zynq_v2/hardware/zynq_top.xsa用 PetaLinux 构建的最小命令流程如下source /opt/petalinux/2025.1/settings.sh petalinux-create --type project --template zynq --name linux_v2 cd linux_v2 petalinux-config --get-hw-description../zynq_v2/hardware/ petalinux-build petalinux-package --boot --fsbl --fpga --u-boot --force -o BOOT.bin petalinux-package --image --format iu --kernel --dtb -o image.ubpetalinux-config这一步会把.xsa里的硬件配置导成内核配置和设备树源文件模板选zynq而不是zynqMP这是 Zynq-7000 和 Ultrascale 的分水岭。petalinux-build编译整个系统产物在images/linux/。petalinux-package --boot是把 FSBL、bitstream、U-Boot 三个组件合成BOOT.bin--fsbl不跟路径时会自动用当前工程的 FSBL--fpga同理--force覆盖旧文件。--image --format iu这行是把内核和设备树打成 FIT 格式的image.ub。如果你在images/linux/下已经能看到image.ub这步可以省略。PetaLinux 还支持生成boot.scr在 U-Boot 源码目录放一个boot.cmd内容是setenv bootargs consolettyPS0,115200 root/dev/mmcblk0p2 rw rootwait load mmc 0:1 0x20800000 image.ub bootm 0x20800000用mkimage把它编译成脚本镜像mkimage -T script -C none -n Boot Script -d boot.cmd boot.scr这里load mmc 0:1 0x20800000的含义是从 SD 卡第一个分区把image.ub加载到内存地址0x20800000这个地址必须在 DDR 范围内且避开内核解压区域Zynq-7020 的 DDR 从0x00100000开始0x20800000是一个常见且安全的选择。bootm解析 FIT 镜像自动匹配里面的内核和 dtb。3.3 手工 boot.bif理解 FSBL、bitstream 与 U-Boot 的链接顺序不走 PetaLinux 时生成BOOT.bin靠的是bootgen加一个 bif 描述文件。新建boot.bifthe_ROM_image: { [bootloader]zynq_fsbl.elf system.bit u-boot.elf }然后执行bootgen -image boot.bif -o i BOOT.bin -w onbif 文件就是告诉 bootgen“按这个顺序把镜像拼起来”。顺序有讲究[bootloader]标记的 FSBL 必须放在第一个这是 BootROM 的唯一入口system.bit是 PL 的 bitstream如果你的设计里 PL 侧没有逻辑或者 U-Boot 里已经带了 FPGA 加载功能这一行可以去掉但 FSBL 里就不包含配置 FPGA 的步骤u-boot.elf最后它会作为第二阶段镜像被 FSBL 跳转。-w on的意思是覆盖输出文件不加的话第二次运行会报文件已存在。一个踩坑点如果zynq_fsbl.elf是从 V1 工程里直接拷贝来的而system.bit是 V2 新综合出来的BOOT.bin能生成但在板子上从 SD 启动时 U-Boot 可能直接收不到 CPU 寄存器的正确初始化。症状是串口完全无输出或者输出乱码。这种问题排查起来非常耗时所以务必确认fsbl.elf是用同一个.xsa编译的。3.4 编译设备树dtc 与 FIT 打包兜底方案设备树是 Zynq Linux 里“硬件描述即代码”的体现。PetaLinux 把.dts生成在components/plnx_workspace/device-tree/下手工方式下需要自己维护。常见的处理方式是修改 dts 后重新编译dtc -I dts -O dtb -o system.dtb system.dts如果最终要用image.ub还需要把内核和 dtb 打包成 FIT。写一个fit.its/dts-v1/; / { descriptions Zynq V2 FIT image; images { kernel1 { data /incbin/(uImage); arch arm; os linux; type kernel; compression none; }; fdt1 { data /incbin/(system.dtb); type flat_dt; arch arm; }; }; configurations { conf1 { kernel kernel1; fdt fdt1; }; }; };执行打包mkimage -f fit.its image.ub这里有个容易忽略的细节/incbin/路径相对于.its文件所在目录所以建议把uImage和system.dtb放在同一目录下再执行。FIT 镜像的好处是 U-Boot 的bootm可以根据configurations自动匹配 dtb不用再手写fdt addr系列命令。4. 制作 SD 卡启动盘分区、拷贝与启动模式设置4.1 fdisk 分区FAT32 放 BOOT.binext4 放根文件系统SD 卡是 Zynq Linux 最常见的启动介质它的分区要求很简单第一个分区必须 FAT32放BOOT.bin、image.ub和boot.scr因为 BootROM 和 U-Boot 都认 FAT第二个分区用 ext4放根文件系统。操作前先确认设备名lsblk sudo fdisk /dev/sdb在 fdisk 交互界面里依次执行d # 删除旧分区重复执行直到清空 n p 1 # 新建主分区1 1G # 给 FAT32 分区 1GB t c # 把分区1类型改为 FAT32 (W95 FAT32, 0x0c) n p 2 # 新建主分区2剩余空间 w # 写入然后格式化sudo mkfs.vfat -F 32 -n BOOT /dev/sdb1 sudo mkfs.ext4 -L rootfs /dev/sdb2分区类型0x0c和0x0b的区别在于是否支持 LBA 寻址U-Boot 和 BootROM 都兼容但0x0c更稳妥。第一分区大小不建议小于 256MB因为image.ub动辄十几 MB加上内核调试版本和多个 dtb 备份空间紧张时排错会很痛苦。4.2 boot.scr 与 uEnv.txtU-Boot 通过 env 决定加载什么格式化后挂载并拷贝文件mkdir -p /mnt/sd_boot /mnt/sd_root sudo mount /dev/sdb1 /mnt/sd_boot sudo mount /dev/sdb2 /mnt/sd_root sudo cp BOOT.bin image.ub boot.scr /mnt/sd_boot/ sudo cp -a rootfs/* /mnt/sd_root/ sync sudo umount /mnt/sd_boot /mnt/sd_root拷贝后检查一下BOOT.bin的完整性md5sum /mnt/sd_boot/BOOT.bin有时 U-Boot 不会自动执行boot.scr而是读取环境变量。这时可以在 FAT 分区放一个uEnv.txtuenvcmdload mmc 0:1 0x20800000 image.ub bootm 0x20800000uEnv.txt是 U-Boot 在启动脚本之前的最后一道检查它的优先级高于boot.scr。常见做法是两者都放boot.scr是主脚本uEnv.txt里只做变量覆盖不要写重复的加载逻辑否则启动时执行两次bootm会报Bad Linux ARM zImage magic。4.3 拨码检查与启动验证MIO[4:6] 和 dmesg 看哪里断插卡前先确认开发板的启动模式。Zynq-7000 的启动引脚是 MIO[4:6]SD 启动对应的电平组合在大多数参考设计里是MIO41, MIO50, MIO60也就是拨码开关里 MIO4 拨到 ONMIO5、MIO6 拨到 OFF。不同板卡的丝印标注不完全一样比如有些板子写的是“SD Boot”而不是具体 MIO 编号以板卡手册为准。上电后接串口波特率 1152008N1。先看是否出现 U-Boot 版本信息再看内核启动日志。如果卡在 FSBL 阶段串口只有 Xilinx 的 logo如果 U-Boot 跑起来了但找不到image.ub会打印Bad FAT entry或Failed to load image.ub。进入 Linux 后用这几条确认系统状态uname -a cat /proc/cmdline dmesg | grep -i mmc dmesg | grep -i eth/proc/cmdline能看到root/dev/mmcblk0p2 rw rootwait说明 bootargs 生效dmesg | grep -i mmc里应该有 SD 卡的分区信息比如mmcblk0: p1 p2。如果分区信息没打印检查dmesg | grep -i mmc0看控制器有没有 probe 成功。5. 从 FSBL 报错到 eth0 不通Zynq Linux 上线前 3 个必查点Vitis 里做 Flash 烧写时a valid fsbl file is required for flash operation是最常见的挡路报错。原因是 Vitis 的 Program Flash 功能必须用 FSBL 来初始化 DDR 和时钟而默认工程的 bootloader 列表是空的。解决方式是在 Vitis 里新建一个 FSBL 工程File - New - Application Project硬件平台选当前.xsa模板选Zynq FSBL编译后在烧写界面指定这个fsbl.elf。还有一种情况是命令行烧写时忘了加-fsbl参数program_flash -f BOOT.bin -fsbl zynq_fsbl.elf -offset 0 -flash_type qspi-x4-offset 0表示从 QSPI Flash 起始地址烧写-flash_type要跟板卡实际 Flash 型号匹配qspi-x4是四线 SPI 模式如果板子上是qspi-x1布局烧写会成功但启动时乱码。第二个必查点是 U-Boot 环境变量里的ethaddr。Zynq 的 GEM 控制器没有内置 MAC 地址完全依赖外部 EEPROM 或环境变量没设置时 U-Boot 会警告ZYNQ GEM: MAC address not found进入 Linux 后eth0可能直接起不来。常见做法是进 U-Boot 命令行设置并保存setenv ethaddr 00:0a:35:00:01:02 saveenv第三个技巧是动态修改设备树。编译好的image.ub里 dtb 是固定的但调试时经常要临时改外设状态。U-Boot 里可以直接操作 device treefdt addr 0x20800000 fdt set /ethernete000b000 local-mac-address [00 0a 35 00 01 03] bootm 0x20800000但注意fdt addr的地址必须和load的地址一致也就是image.ub加载到0x20800000后fdt addr要指向同一个地址这是初学者最容易犯的错。如果每次都手动改太麻烦把这个命令写进boot.scr里放在bootm之前执行就能实现不改设备树源码、不改内核镜像的临时硬件调整。本文还有配套的精品资源点击获取