☰
ZYNQ7020 Linux最小系统搭建教程:从Vivado到SD卡启动
2026/9/29 15:59:47 网站建设 项目流程

我第一次接触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卡第一个分区读取启动镜像。第二阶段是FSBL(First Stage Boot Loader),它负责最基础的硬件初始化,包括MIO引脚配置、DDR控制器初始化、时钟PLL设置,然后把后续启动代码加载到内存。第三阶段是U-Boot,这个大家比较熟,它的任务是初始化串口(做打印输出)、从SD卡的ext4分区读取内核镜像,并把它加载到DDR中。第四阶段是内核启动,内核完成挂载根文件系统、启动init进程,最终给你出现一个可以登录的shell。

这个流程听起来好像很常规,但它是整个ZYNQ Linux开发的地基——所有后面要做的驱动开发、应用调试,都是建立在这条启动链路上的。我最初犯的一个错误就是急着先看内核驱动的编写,结果连最小系统都没跑通,出了bug根本不知道是该改设备树、还是在U-Boot里调参数,整半天都找不到问题在哪。

1.2 为什么选择Vivado加Vitis的组合方案

如果你查过旧版资料,会发现很多教程还在用Vivado搭配老的SDK(Software 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 Project,Browse你的.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 Project,Platform选择刚创建的平台工程,然后在下拉模板里分别选“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-Boot:

the_ROM_image: { [bootloader] ./zynq_fsbl.elf ./u-boot.elf }

所以严谨来说,ZYNQ-7020由于处理器架构是双核A9,没有单独的PMU处理器,因此直接用FSBL+U-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,设备树地址是0x2000000,ramdisk地址是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 ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- make xilinx_zynq_defconfig make -j8 UIMAGE_LOADADDR=0x8000 uImage

这里面有几个细节。第一,交叉编译工具链需要提前安装,Ubuntu下直接apt install gcc-arm-linux-gnueabihf就行,注意是gnueabihf不是gnueabi,因为ZYNQ-7020的A9内核带硬件浮点单元,用hf版工具链能发挥浮点性能,否则内核启动会慢很多。第二,UIMAGE_LOADADDR=0x8000是内核源码在DDR中的解压地址,这个值必须与U-Boot的配置一致,否则内核无法启动。第三,编译完成后生成的uImage只是一个内核载体,还需要和设备树一起打包成image.ub。

5.2 设备树覆盖的修改逻辑

设备树(Device Tree)的作用是告诉内核“你所在的硬件平台有哪些设备、地址是多少、中断怎么连”。Xilinx内核里已经为许多官方板卡写好了设备树源文件,你可以在arch/arm/boot/dts/目录下找到zynq-zc702.dts等文件。

如果你能用官方板卡,直接用它即可。如果是第三方板卡,通常厂家会提供适配好的设备树源文件,你有两个选择:一是把厂家提供的dts文件直接编译进image.ub,二是先用官方dts把系统跑起来,然后通过修改dts源码适配自己板子。后者能帮你更深入理解设备树的作用。

最常改的设备树节点是内存节点。打开dts文件,找到类似下面的内容:

memory@0 { 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。具体步骤:

  1. 用读卡器把SD卡接到电脑,先用fdisk /dev/sdX删除所有分区。
  2. 创建第一个分区,大小为500MB左右,类型设置为W95 FAT32 (LBA),文件系统格式化为FAT32。
  3. 创建第二个分区,大小占据剩余空间,格式化为ext4。
  4. 把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,会看到类似:

bootcmd=run uenvboot; run sdboot

run 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分区后重新解压rootfs
Vitis 下载调试时不识别芯片JTAG驱动未正确安装 / 硬件连接松动重新安装digilent驱动;检查JTAG线连接;确认电源供电充足
波特率不正确导致乱码串口波特率与U-Boot配置不一致统一设置为115200 8N1

6.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这条启动链路的全貌看清楚。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询