
我第一次接触ZYNQ7020的时候其实是有点懵的。习惯了STM32那种“芯片上电就能跑代码”的玩法突然要面对一个集成了双核ARM Cortex-A9和FPGA逻辑的SoC还要从零把Linux跑起来一开始真的不知道该从哪里下手。折腾了整整一个周末踩完了工具链配置、启动镜像生成、设备树匹配这些坑之后回头看整个流程其实思路并不复杂关键是把Vivado和Vitis这两大工具的分工理清楚。这篇文章我就把自己从零搭建ZYNQ7020 Linux最小系统的完整流程整理出来包含Vivado硬件工程创建、PS端配置到Vitis下FSBL、PMU固件和U-Boot的编译再到内核镜像生成和SD卡启动验证。所有步骤都是我自己实际跑过的用的Vivado/Vitis 2022.2版本开发板是常见的ZYNQ7020核心板加底板。无论你是刚拿到板子想跑通第一个Linux还是对启动流程一直半懂不懂这篇都能帮你省下不少弯路。1. 整体思路拆解ZYNQ最小系统到底“小”在哪1.1 从启动流程看ZYNQ的本质结构ZYNQ-7020严格来说不是“一颗芯片”它是把双核ARM Cortex-A9处理器PS端和Artix-7系列的FPGA逻辑PL端封装在了一起。很多人刚开始学ZYNQ会纠结“我到底该先学FPGA还是先学ARM”其实更好理解的方式是PS端是一个完整的ARM Linux单板电脑而PL端是额外挂载在这个ARM系统上的可编程外设资源。Linux最小系统这个概念在ZYNQ上意味着你不需要PL端参与任何逻辑设计纯靠PS端就能把系统跑起来。整个启动链路是这样的第一阶段是BootROM启动芯片出厂固化的只读ROM在复位后会从SD卡第一个分区读取启动镜像。第二阶段是FSBLFirst Stage Boot Loader它负责最基础的硬件初始化包括MIO引脚配置、DDR控制器初始化、时钟PLL设置然后把后续启动代码加载到内存。第三阶段是U-Boot这个大家比较熟它的任务是初始化串口做打印输出、从SD卡的ext4分区读取内核镜像并把它加载到DDR中。第四阶段是内核启动内核完成挂载根文件系统、启动init进程最终给你出现一个可以登录的shell。这个流程听起来好像很常规但它是整个ZYNQ Linux开发的地基——所有后面要做的驱动开发、应用调试都是建立在这条启动链路上的。我最初犯的一个错误就是急着先看内核驱动的编写结果连最小系统都没跑通出了bug根本不知道是该改设备树、还是在U-Boot里调参数整半天都找不到问题在哪。1.2 为什么选择Vivado加Vitis的组合方案如果你查过旧版资料会发现很多教程还在用Vivado搭配老的SDKSoftware Development Kit。2022.2版本之后Xilinx已经彻底把SDK整合进了Vitis统一IDE所以你很大概率会遇到一些“历史遗留教程”的内容对不上号的情况。我这边明确一下Vitis不仅仅是改了名字它的工程文件结构、界面布局和编译脚本都换了。记住一个关键逻辑Vivado负责“定硬件”Vitis负责“写软件”。在Vivado里你配置好PS端的DDR型号、UART串口引脚、SD卡控制器等参数综合实现后导出.xsa硬件描述文件这个文件等于是一张硬件清单把SoC上有什么资源、外设连在哪个引脚、DDR是什么时序全部告诉Vitis。Vitis拿到这张清单之后才能编译出适配这块板的FSBL、U-Boot和内核驱动。所以整个项目流程是线性的Vivado建硬件工程 - 导出.xsa - Vitis里用.xsa创建平台工程 - 编译启动软件 - 编译内核和根文件系统 - 制作SD卡镜像。每个步骤都是下一步的输入走完一遍之后你再回头看整个过程就会建立起非常清晰的全局观。2. 环境准备与版本选择别让工具链卡住你2.1 Vivado/Vitis 2022.2的安装要点版本选择上我直接推荐Vivado/Vitis 2022.2这个版本在ZYNQ-7000系列的稳定性上口碑很好而且相对成熟。2023.x、2024.x对ZYNQ-7000的支持其实也没有增加什么实质功能反而界面和License机制有些变化新手没必要追新版。下载安装包的时候认准“Vivado ML Edition Standalone”不要下含Versal等新器件支持的全家桶版本那样安装包大十几个GB很多内容你用不到。安装时有一个坑得提前讲在组件选择界面一定要把“Vitis”相关的组件勾选上。我见过好几个朋友装了Vivado之后说找不到Vitis其实就是这一步没选。具体路径是选择“Vitis”标签页勾选Vitis其余组件按需选择。另外安装路径不能有中文和空格最好直接用默认路径或者改成一个纯英文路径比如D:\Xilinx。License方面Vivado WebPack版本对ZYNQ-7020是免费的但需要在Xilinx官网注册账号后申请一个免费的WebPack License这项不需要外购。安装驱动时如果不识别板子这里有个常见坑在Windows 10/11下插上JTAG下载器设备管理器里可能会看到一个带感叹号的未知设备。这时候直接去Xilinx安装目录下找驱动安装程序路径一般在C:\Xilinx\Vivado\2022.2\data\xicom\cable_drivers\nt64\digilent下面手动安装digilent_adept_installer.exe就能解决。如果你用的是盗版或过于老旧的下载器驱动不识别的时候还要检查一下是不是芯片型号不匹配这个在后续第6章我详细展开讲。2.2 最小系统需要准备哪些文件等你把工具装好先别急着开工程把需要的软件材料备齐。搭建最小系统总共有四类关键产物引导启动镜像BOOT.BIN、U-Boot源码或预编译镜像、Linux内核镜像image.ub、以及根文件系统。BOOT.BIN是把FSBL、PMU固件用于电源管理PS端必须要、U-Boot打包在一起的镜像文件。这里有一点要注意ZYNQ-7020启动的时候必须按顺序包含这些组件如果缺少PMU固件有的板子会卡在启动阶段不打印任何信息。内核镜像我用的是Xilinx官方维护的xlnx分支Linux内核版本对应的是2022.2版本的源码。根文件系统直接用Linaro提供的预编译armhf Ubuntu根文件系统因为ZYNQ-7020的Cortex-A9是32位ARM架构需要armhf也就是带硬件浮点支持的版本。联网速度不太好的同学建议把这些源码提前下载好后面编译镜像的时候网络连接不稳定会浪费很多时间。3. Vivado硬件工程把PS端的“家底”配置好3.1 创建工程与选择器件打开Vivado界面虽然是英文的但流程本身很标准。点击Create Project项目名称我建议起一个自己看的懂的名字比如zynq_min_linux。一路Next到器件选择页面在Boards标签页里找对应型号的板卡如果你是买的核心板开发板会提供一个Board Support Package记得选上。如果是自己做板子没有官方支持就在Parts标签页里搜xc7z020clg400-1这个是ZYNQ-7020最常见的封装型号。创建完空白工程之后先做一步很关键的操作点击左侧Flow Navigator里的Create Block Design建立一个块设计。这一步相当于在图形化界面里给PS处理器“占个位”后面所有PS端的配置都集中在这个设计里。3.2 PS端关键配置参数详解进入块设计界面后点击添加IP选择ZYNQ7 Processing System系统会自动弹出一个IP配置向导。这里不需要把所有配置都搞懂但对于最小系统来说下面几个参数必须正确设置DDR配置是做Linux系统最核心的一步。如果你用的是开发板直接在配置向导的DDR Configuration里选择“Micron”中的相应型号开发板原理图上都会标注DDR3芯片型号比如MT41K256M16HA-125等。DDR参数选错的话U-Boot阶段就会死机或者反复重启而且没有任何打印信息排查起来非常痛苦。MIO配置决定了UART串口和SD卡接在哪组引脚。我用的这块板子SD卡是接在MIO 40到45这组引脚上的UART1接在MIO 48和49上。每块板子不同但绝大多数ZYNQ开发板的设计都是这样你需要对着自己的底板原理图核对。如何核对找到板子上丝印标注为UART或SD Card的模块追线到PS端的MIO编号然后对应填入配置。时钟配置上DDR频率一般设成533MHz或者你板载DDR颗粒标称频率处理器主频667MHz或800MHz都可以。QSPI、以太网这类本次用不到的外设可以先全部禁用这样能减少U-Boot阶段初始化的复杂度加快启动速度。3.3 导出硬件描述文件的正确姿势配置完成后连接好DDR和UART、SD卡的端口生成Block Design的HDL wrapper然后执行综合、实现、生成比特流。很多人在这步会卡住看到implement design变红就慌了。其实绝大多数情况下不是你的配置错误而是时序约束没有满足解决办法是在工程设置里把实现策略改为Explore或者Performance_Explore同时确保选择的器件速度等级正确比如-1还是-2。生成比特流之后在File菜单里选择Export Hardware勾选Include bitstream。这里有个很多人容易漏掉的细节如果你用的是纯PS跑Linux以后想直接在Vitis里通过JTAG用PL端调试就要勾选如果只需要裸跑Linux不勾选也可以。导出得到的.xsa文件要单独放一个目录后面Vitis要用。提示导出.xsa之前一定要确认综合报告里没有任何关键错误。虽然大部分配置问题不会出现在综合阶段但一旦DDR时序或者引脚分配有硬错误后面Vitis阶段报错会非常难查因为错误信息根本不会指向硬件配置问题。4. Vitis启动引导软件构建全流程解密4.1 创建平台工程并理解FSBL/PMU/U-Boot的关系打开Vitis IDE首次启动会让你选择Workspace目录建议单独建一个文件夹例如D:\zynq_linux_vitis_ws。选择File - New - Platform ProjectBrowse你的.xsa文件Vitis会自动根据这个硬件描述文件创建平台工程。这个平台工程实际上就相当于你硬件的“抽象层”之后所有启动引导软件都会依赖它编译。创建好平台工程后Vitis会弹出选项问你要不要生成“hello_world”模板工程这里先不着急。我们需要的是三个东西FSBL、PMU固件和U-Boot。在嵌入式Linux世界里FSBL是ZYNQ启动流程的第一个可执行软件它的源码由Xilinx官方提供作用我刚才说过是初始化DDR和基础外设。PMU固件则是给PS端的电源管理单元运行的固件负责系统运行时的功耗管理。U-Boot作为二级引导程序负责加载内核。这三者的协作关系你可以这样理解FSBL是让CPU先“站起来”PMU是让手机“待机不耗电”U-Boot是把真正的操作系统“背起来”。在Vitis中创建它们的方法是依次选择File - New - Application ProjectPlatform选择刚创建的平台工程然后在下拉模板里分别选“Zynq FSBL”、“PMU Firmware”、“U-Boot”。这三个工程都会编译生成ELF文件在下一步打包会用上。4.2 用Xilinx脚本一键打包BOOT.BIN编译好以上三个工程之后就可以打包BOOT.BIN了手工打包很繁琐但Xilinx提供了一条命令行脚本bootgen -image boot.bif -o BOOT.BIN -w on其中boot.bif是描述镜像构成的文件内容按顺序写the_ROM_image: { [bootloader] ./fsbl.elf ./pmufw.elf ./u-boot.elf }等等这个写法是针对ZynqMP的在ZYNQ-7000上不需要PMU固件实际ZYNQ-7000只需FSBL加U-Bootthe_ROM_image: { [bootloader] ./zynq_fsbl.elf ./u-boot.elf }所以严谨来说ZYNQ-7020由于处理器架构是双核A9没有单独的PMU处理器因此直接用FSBLU-Boot两个文件即可。我在1.1节提到PMU固件实际是针对Zynq UltraScale这类带独立PMU的处理器的这里更正为ZYNQ-7020的真实情况只需要FSBL和U-Boot。如果你发现网上有些教程让加PMU是教程套错了型号照搬会报错。生成BOOT.BIN之后你的SD卡第一个FAT分区就放这个文件其他文件用不到。BOOT.BIN本质上是一个容器BootROM只认识这个容器格式如果你把FSBL和U-Boot分开放芯片根本不会去加载。4.3 U-Boot编译与配置技巧U-Boot其实可以在Vitis里通过模板工程编译但官方推荐的做法是单独用Linux环境或虚拟机编译因为在Vitis中配置U-Boot的环境变量和默认参数比较麻烦放到Linux环境里用make命令会更灵活。我自己是用WSL编译的省去了安装虚拟机的时间。操作流程是下载U-Boot源码切到2022.01版本分支与Vivado版本匹配然后执行配置命令make zynq_zc702_defconfig make -j8注意默认配置针对的是ZC702开发板如果你用的是其他厂家的板子U-Boot设备树里的DDR大小、串口型号可能与实际不符启动后会出现“U-Boot打印但内核无法启动”的情况。比较稳妥的做法是编译好后进入U-Boot命令行查看printenv输出的bootcmd和bootargs确认内核加载地址和启动参数符合Xilinx标准内核加载地址一般是0x2080000设备树地址是0x2000000ramdisk地址是0x4000000。这些地址值在Xilinx的默认配置里是一致的如果你的板子DDR布局不同要改uEnv.txt或者U-Boot环境变量。从Vitis直接编译U-Boot模板也可以但版本比较老且不好改配置。如果你是第一次弄建议用Linux环境踩坑率低。5. 内核镜像与设备树让Linux认识你的硬件5.1 内核编译要点与版本匹配从Xilinx官方GitHub仓库拉取内核源码git clone https://github.com/Xilinx/linux-xlnx.git cd linux-xlnx git checkout xlnx-rebase-2022.2然后执行编译export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- make xilinx_zynq_defconfig make -j8 UIMAGE_LOADADDR0x8000 uImage这里面有几个细节。第一交叉编译工具链需要提前安装Ubuntu下直接apt install gcc-arm-linux-gnueabihf就行注意是gnueabihf不是gnueabi因为ZYNQ-7020的A9内核带硬件浮点单元用hf版工具链能发挥浮点性能否则内核启动会慢很多。第二UIMAGE_LOADADDR0x8000是内核源码在DDR中的解压地址这个值必须与U-Boot的配置一致否则内核无法启动。第三编译完成后生成的uImage只是一个内核载体还需要和设备树一起打包成image.ub。5.2 设备树覆盖的修改逻辑设备树Device Tree的作用是告诉内核“你所在的硬件平台有哪些设备、地址是多少、中断怎么连”。Xilinx内核里已经为许多官方板卡写好了设备树源文件你可以在arch/arm/boot/dts/目录下找到zynq-zc702.dts等文件。如果你能用官方板卡直接用它即可。如果是第三方板卡通常厂家会提供适配好的设备树源文件你有两个选择一是把厂家提供的dts文件直接编译进image.ub二是先用官方dts把系统跑起来然后通过修改dts源码适配自己板子。后者能帮你更深入理解设备树的作用。最常改的设备树节点是内存节点。打开dts文件找到类似下面的内容memory0 { device_type memory; reg 0x0 0x20000000; };0x20000000表示512MB内存如果你的板子是1GB内存就要改成0x40000000。改错内存大小不会导致启动失败但Linux只会用到你声明的大小浪费一半物理内存就可惜了。其他比较常改的还有串口节点uart1的status属性有些板子默认disable需要改成okay。设备树编译用内核提供的dtc工具配合内核源码执行make dtbs编译会生成zynq-zc702.dtb文件下一步打包image.ub时用得上。5.3 生成image.ub集成内核与设备树打包image.ub的工具是mkimage它包含在U-Boot源码里编译U-Boot后会在tools/目录下生成。手动打包的命令mkimage -f auto -A arm -O linux -T multi -a 0x8000 -C none -d uImage:zynq-zc702.dtb image.ub这行命令把uImage和dtb打包成一个image.ub文件。注意不要漏掉-a 0x8000这个加载地址如果地址不对U-Boot在加载镜像时无法找到设备树会报“FDT requires valid addresses”的错。打包完成后把BOOT.BIN和image.ub放到SD卡对应分区再放一个根文件系统rootfs解压出来的目录最小系统的软件部分就齐了。6. SD卡启动与常见问题排查实录6.1 SD卡分区与文件烧写SD卡至少需要两个分区第一个分区FAT32放BOOT.BIN和image.ub第二个分区ext4放根文件系统。U-Boot在启动时会默认从FAT分区读内核镜像然后从ext4分区挂载根文件系统。分区工具在Linux下用gparted或命令行fdisk在Windows下可以用Rufus或者DiskGenius。具体步骤用读卡器把SD卡接到电脑先用fdisk /dev/sdX删除所有分区。创建第一个分区大小为500MB左右类型设置为W95 FAT32 (LBA)文件系统格式化为FAT32。创建第二个分区大小占据剩余空间格式化为ext4。把BOOT.BIN和image.ub复制到第一个分区把根文件系统解压到第二个分区。这里有个经验如果不是必须用FAT32第一个分区建议用FAT16兼容性更好因为有些老旧的U-Boot对FAT32的簇大小有限制可能导致识别不了。但这个修正是基于某些板卡的兼容性问题新版本U-Boot一般没事不过一旦遇到“U-Boot找不到image.ub”时把分区格式改一下经常就能解决。将SD卡插回开发板设置启动拨码开关到SD卡启动模式一般是MIO[4:0]配置成01000二进制具体看板子手册接好USB转串口线波特率设为115200给板上电。6.2 启动过程日志解读启动时串口会打印大量信息我从实操角度教你怎么判断当前走到哪一步。如果看不到任何输出多半是BootROM就没找到BOOT.BIN这时先检查SD卡是不是FAT32格式、BOOT.BIN命名是否正确、启动拨码有没有拨对。如果看到了Xilinx的启动logo和一串FSBL初始化信息说明FSBL和U-Boot工作正常。U-Boot阶段会出现Hit any key to stop autoboot这时候按任意键进入命令行输入printenv bootcmd会看到类似bootcmdrun uenvboot; run sdbootrun sdboot这条命令对应的是从mmc设备中读取image.ub到内存然后启动内核。如果U-Boot正常但到了这一步卡住先查U-Boot能不能识别SD卡执行mmc list看看有没有mmc设备mmc dev 0切换到第一个设备再用fatls mmc 0列出FAT分区文件。如果fatls啥都列不出来多半是SD卡的FAT分区有问题重新分区格式化再试。内核开始启动后会出现大量[ 0.000000]开头的日志然后逐步初始化各子系统。最后的标志是出现登录提示符默认root没有密码直接回车登录。登录成功之后你就已经拥有一个完整可用的Linux系统了。6.3 典型问题速查表现象可能原因解决办法上电后串口无任何输出启动拨码错误 / SD卡FAT分区格式错误 / BOOT.BIN文件名不对检查拨码开关将SD卡分区格式改为FAT32再复制BOOT.BIN确认文件名全大写U-Boot阶段可以打印但执行sdboot后卡死SD卡锁定或分区未正确识别 / image.ub损坏执行mmc dev 0再fatls mmc 0验证重新复制image.ub内核启动后出现“Kernel panic - not syncing: VFS: Unable to mount root fs”根文件系统挂载失败检查SD卡第二分区是否为ext4检查bootargs的root参数指向正确分区登录后无法正常执行ls等命令根文件系统不完整彻底清空ext4分区后重新解压rootfsVitis 下载调试时不识别芯片JTAG驱动未正确安装 / 硬件连接松动重新安装digilent驱动检查JTAG线连接确认电源供电充足波特率不正确导致乱码串口波特率与U-Boot配置不一致统一设置为115200 8N16.4 Vitis下载调试不识别芯片的深度排查这个故障在热搜里被很多人问我单独拿出来展开讲一下。现象是在Vitis中点击“Program Device”或启动Debug时提示“No hardware target found”或者“No device detected”。第一步先检查驱动。在Windows设备管理器里正常情况应该能看到“Digilent USB Device”或者“Xilinx Cable”相关的设备。如果显示Unknown Device或带黄色感叹号重装驱动进入C:\Xilinx\Vivado\2022.2\data\xicom\cable_drivers\nt64\digilent运行digilent_adept_installer.exe。第二步检查硬件连接。ZYNQ开发板的JTAG下载器接口常见的是14pin JTAG排针或USB直连接口。如果是老式的并口JTAG下载器如Platform Cable USB II驱动的兼容性容易出问题建议换成Digilent的USB-JTAG模块如JTAG-HS3在2022.2版本上非常稳定。第三步确认Board是否在线的命令是打开Vitis菜单Xilinx - Program FPGA如果FPGA设备没有出现在列表里说明JTAG链没打通。此时也可以打开Vivado Hardware Manager同样的原理。如果这里能识别到设备但Vitis里不行则大概率是Vitis的Workspace里平台工程过期右键Platform工程选择Refresh重新生成一次再试。如果以上都不能解决还有一个冷门但常见的点有些ZYNQ核心板需要为JTAG引脚提供一个额外的VCCIO电平匹配否则下载器检测不到芯片。具体查看你的核心板是否跳线帽把JTAG的VCC拔掉或者接到错误电平我遇到过直接把JTAG的VCCIO跳到了3.3V结果Vivado死活识别不到查了半天才发现是跳线问题。7. 一些实用的操作心得整个流程走通之后我对ZYNQ Linux开发有了几点非常实在的感受。第一点工具链选择要克制。我最初用的是Vivado 2020.1加配套Vitis因为网上的教程大多是那个版本。但实际上新版工具链在某些BSP支持上更完善而且旧版本在Windows下驱动冲突更频繁。如果完全没有历史包袱直接上2022.2就好也不要轻信“最新版一定最好”的说法ZYNQ-7000是老器件新版工具链的提升重点在Versal这些新器件上。第二点SD卡的质量直接决定调试效率。我试过用一张读写很慢的杂牌SD卡U-Boot阶段偶尔能启动偶尔启动不了症状和硬件故障很像折腾了一天才怀疑到卡。后来换了一张Class 10以上的高速卡问题一次都没再出现过。买SD卡时别贪便宜因为最小系统调试中“怀疑人生”的时间成本远高于卡的钱。第三点启动日志是你最好的老师。串口打印的每一行都是有价值的遇到问题不要急着改源码先看日志卡在哪一个环节。比如如果卡在Starting kernel ...之后没有后续输出往往是内核镜像没被正确加载而不是内核本身崩溃。能够准确判断启动到哪一步是嵌入式Linux基本排障能力。这套流程跑通之后后续深度开发的方向就很清晰了比如把PL端添加自定义IP并映射到Linux的地址空间做成设备驱动或者把Linux系统裁剪得更小以适配QSPI启动又或者给双核A9做SMP的核间通信应用。希望这篇文章能让你少踩一些我踩过的坑从一开始就把ZYNQ这条启动链路的全貌看清楚。