完整指南与报错排查)
拿到一块Zynq板卡大多数人第一件事是点灯、跑个Hello World但真正让板子脱离JTAG和电脑独立工作必须把启动镜像“固化”到板载Flash里。我最近用Vitis给Zynq-7020配合的W25Q256FV做烧写先后碰到过芯片不识别、Flash下载时报“Target DLL has been cancelled”、烧写过程中提示无法与Flash芯片通信等一系列问题。这篇文章就是以W25Q256FV为对象把Zynq通过Vitis烧写QSPI Flash的完整流程、每个环节的原理、以及这些报错的真实排查链路讲清楚。适合正在做Zynq-7000系列产品或者刚接触Vitis烧写、卡在报错里出不来的同学参考。1. 为什么板卡上选W25Q256FV这颗Flash和Zynq的搭配逻辑先别急着打开Vitis得先搞清楚W25Q256FV到底是什么、在板子上承担什么职责。W25Q256FV是Winbond华邦的256Mbit SPI NOR Flash换算过来是32MB存储空间供电3.3V支持标准SPI、Dual SPI、Quad SPI以及QPI模式。它和后面经常见到的W25Q256JV、W25Q128JV是同一个家族指令集基本兼容只是工艺、频率和ID有些差异JEDEC ID通常是0xEF 0x40 0x19这也是区分Flash型号时最直接的依据。Zynq-7000系列的PS端自带一个QSPI控制器通过固定的MIO引脚外接SPI NOR Flash。也就是说只要在Vivado里把PS端QSPI外设打开硬件连好线软件侧就能通过FSBL、U-Boot或者裸机驱动访问这颗Flash。Zynq的上电启动流程是BootROM先读取QSPI里的启动头把FSBL加载到OCM片上内存运行FSBL再初始化DDR、配置PL端bitstream、把应用程序加载到DDR里执行。所以这块Flash实际上承担的是“启动介质”的角色一切脱离JTAG跑的裸机程序、Linux内核和根文件系统最终都要落到它上面。那么为什么很多Zynq开发板宁可选W25Q256FV也不用SD卡或eMMC我个人的理解是QSPI NOR在几个维度上比较适合Zynq这种场景引脚占用少6根信号线就够PCB布线压力小。启动时序简单Zynq BootROM原生支持QSPI启动不需要额外驱动。可靠性高NOR Flash的随机读取性能远好于NAND读写寿命一般标称10万次擦写做启动介质很稳妥。比SD卡稳不会出现接触不良、文件系统异常导致启动失败的问题。32MB容量足够放FSBL、bitstream和体积不大的应用程序一些Linux系统如果裁剪得当也能塞进去。要注意的是W25Q256FV不是按照字节随机写入的写入前必须先擦除擦除最小单位是4KB扇区读取则是按页256字节或连续读取。只要理解了“先擦后写、擦除粒度大、写地址要落到扇区内”这几个特性后面看烧写日志就不会一头雾水。还有一个很多人忽略的点这颗Flash的地址宽度是24位/32位可切换。256Mbit总容量是32MB而24位地址最多只能寻址16MB。上电默认是3字节地址模式要访问高16MB空间必须切换到4字节地址模式。这个特性直接关系到大镜像烧写和高地址分区后面细说。2. 烧写前的硬件核对清单与镜像类型选择很多人烧写失败不是软件操作问题而是硬件状态没对上。Vitis烧写Flash和普通JTAG调试有一个显著区别Program Flash流程会把FSBL下载到Zynq内部运行由FSBL来初始化QSPI控制器然后通过JTAG间接完成Flash的擦除、写入和校验。换句话说FSBL相当于一个“代理”所以硬件上必须保证PS能正常工作JTAG链路要通畅而且QSPI引脚的电平、片选、时钟都要正确。2.1 连线与电平先查原理图再动手Zynq-7000的QSPI控制器信号一般分配在MIO[6:0]这一组里包括SCLK、DI/DOIO0/IO1、IO2、IO3、以及片选具体哪一个MIO对应哪一个信号不同板卡可能略有差异强烈建议打开自己板卡原理图确认不要照抄网上的引脚编号。确认点主要有三个QSPI Flash的VCC是否和PS端的MIO bank共电源域一般都要3.3V如果Flash供电是1.8V而MIO bank是3.3V通信会不稳定甚至直接烧坏器件。片选上拉、HOLD引脚和WP引脚的接法是否合理HOLD和WP如果悬空会导致进入写保护或者暂停状态烧写时表现就是Flash ID能读、擦除却没反应。板上是否使用了电平转换或总线开关有些板子同时让QSPI和别的外设共享MIO需要通过跳线切换没有切过去的话信号被别的器件占用必然通信失败。如果你用的是杜邦线外接Flash模块来做实验我劝你趁早放弃。QSPI在几十MHz下对信号完整性还是有要求的杜邦线接触电阻大、寄生电感高在2MHz下也许能跑一旦提高QSPI时钟就会随机报错。板载贴片Flash是首选。2.2 启动模式拨码JTAG模式还是QSPI模式Zynq的启动模式由MIO上的启动模式引脚决定Vitis烧写时必须把板卡设置为JTAG启动模式。这样做的好处是BootROM不会尝试从QSPI或SD卡执行代码JTAG控制器能直接控制PSFSBL能干净地从OCM启动。有些板卡在QSPI启动模式下也能烧写但偶尔会发生启动代码和调试器抢总线的情况排查起来很绕所以不要给自己添乱。烧写完成后再把拨码切回QSPI启动复位板卡。如果这个顺序搞反经常出现“烧写显示成功但一上电根本没反应”的现象。2.3 镜像类型BOOT.BIN、BIN还是ELFVitis的Program Flash界面能接受多种文件但要注意它们的作用完全不同BOOT.BIN是最终用于启动的镜像包含启动头、FSBL、bitstream和应用程序通常烧写到Flash起始地址0x0。FSBL.elf是第一阶段启动加载器它既可以是BOOT.BIN的一部分也可以在烧写时作为“过渡代理”先被JTAG加载到OCM运行再操作Flash。.bit文件是PL配置数据可以单独烧写但单独烧写它不代表板子能启动因为还需要FSBL来引导。我见过不少初学者直接拿一个hello_world.elf去Program Flash然后抱怨启动不了。单个elf本身不是启动镜像必须通过Bootgen或者Vitis的Create Boot Image功能打包成BIN格式才行。所以烧写前脑袋里要有一张图烧BOOT.BIN是最终目标FSBL是烧写过程用的代理bitstream和应用程序只是BOOT.BIN的组成部分。3. Vitis中的实际烧写操作GUI和命令行两种走法这一章是操作主线。我以Vitis 2020.1及以上版本为例因为从2019.2开始Xilinx SDK逐渐被Vitis取代菜单名和界面有一些变化但这个流程在Vitis 2020~2023版本里基本通用。3.1 第一步在Vivado里导出包含QSPI配置的XSA烧写的前提是硬件工程要正确。打开Vivado的Block Design在Zynq PS配置界面里把QSPI外设打开选择Single QSPI或Dual QSPI并确认MIO分配和你板卡实际一致。QSPI的工作频率建议先按默认值等烧写稳定后再考虑提高速度。然后generate bitstream完成综合实现。生成完bitstream后菜单File - Export Hardware勾选Include bitstream导出XSA文件。这个XSA会带着QSPI外设配置和bitstream信息是后面Vitis工程的“硬件底座”。如果XSA里忘了包含bitstream有些烧写环节也能跑但Program Flash在需要配置PL时会找不到数据。提示Vivado和Vitis版本最好匹配比如Vivado/Vitis 2021.1配2021.1。跨大版本使用XSA经常出现兼容性问题最常见的现象就是Vitis里建平台时报错或者打开硬件工程后外设列表为空。3.2 第二步在Vitis里创建平台工程并生成FSBL打开Vitis设置好Workspace后用XSA创建Platform Project。平台工程里会自动包含FSBL相关的源文件和BSP。接着创建一个Application Project模板选择“Empty ApplicationC”即可因为我们只想要一个能编过的裸机hello_world或者干脆就用平台工程生成的FSBL作为烧写代理。你可能会问为什么明明只是烧写Flash还要创建一个应用工程原因在于Program Flash需要用到FSBL.elf而FSBL一般随着平台工程生成。在Vitis里构建平台工程构建选项里选择Release还是Debug都行重点是最后能在工程目录下找到fsbl.elf。如果你后续要生成BOOT.BIN还需要有一个应用elf所以顺手建一个hello_world应用用来验证链路也是常见做法。3.3 第三步确认JTAG连接和芯片识别烧写前一定先在Vivado Hardware Manager或Vitis的“Xilinx - Hardware Manager”里确认目标芯片已经被识别。打开Hardware Manager如果能看到xc7z020或者xc7z010说明JTAG链路是通的。如果这里都识别不到后面Program Flash大概率会报“Target DLL has been cancelled”或者“Cannot detect target”。这一步顺便可以读一眼Flash的ID。有些版本的硬件管理器能直接看到连接的SPI Flash型号如果显示0xEF4019说明W25Q256FV在链路上应答正常。注意硬件管理器看到的是JTAG链上的器件不一定代表QSPI通信正常但它至少能证明PS没有死锁、电源没问题。3.4 第四步执行Program Flash在Vitis菜单栏选择Xilinx - Program Flash或者直接点击工具栏里的“Program Flash”按钮弹出对话框后填写Image File选择BOOT.BIN或者你想烧写的.bin文件。FSBL File选择前面构建出的fsbl.elf。Flash Type选择qspi-x4-single这是最常用的Zynq QSPI四线模式。Offset通常填0x0。Cable / Target选择当前连接的JTAG目标。点Program后Vitis会先通过JTAG把FSBL加载到OCM然后由FSBL初始化QSPI执行擦除、写入、校验的流程。日志窗口会出现类似Erase Operation. Please wait...、Program Operation...、Verify Operation...的提示。整个烧写W25Q256FV的速度取决于镜像大小和QSPI时钟一般BOOT.BIN在几MB以内一两分钟内完成都算正常。除了GUI命令行方式适合批处理和脚本集成。在Vitis的Xilinx Shell或Vivado的xsct环境里可以调用program_flash工具program_flash -f BOOT.BIN -offset 0x0 -flash_type qspi-x4-single -fsbl fsbl.elf -cable type xilinx_tcf url TCP:127.0.0.1:3121如果你想在烧写后额外做校验可以加上-verify参数。命令行和GUI底层调用的其实是同一套东西GUI适合单板调试命令行适合量产或自动化流程。烧写完成后先不要急着断电。把启动模式拨码切换到QSPI启动按一下板卡复位键观察串口输出是否出现FSBL的启动日志或hello_world打印。如果这一步通过说明整个固化流程闭环了。4. 排查现场目标取消、Flash通信失败、芯片识别不到标题里的“常见问题解决”是重点我按实际排障频率从高到低讲。每次排障我都会坚持一个习惯先看完整日志再猜原因不要一上来就怀疑硬件。4.1 Error: Flash download failed - Target DLL has been cancelled这个报错很经典基本每个用Vitis/SDK烧写过的人都会遇到一次。它的英文原文是Flash download failed - Target DLL has been cancelled中文大概意思就是“Flash下载失败目标DLL被取消”。很多人看到DLL这个词就以为是Windows动态库损坏其实不是。实际排查中我发现这个报错绝大多数来自JTAG目标连接被中断而不是Flash本身的问题。日志里如果已经出现“Connecting to target”然后立刻跳到“Target DLL has been cancelled”优先检查下面几项JTAG连接线是否松动尤其是那种多根杜邦线拼出来的下载线接触不良是最常见原因。Vitis是否和Vivado Hardware Manager同时打开了同一个目标两个软件抢占JTAG资源导致连接被取消。关掉其中一个再试。hw_server进程是否异常。Vitis连接目标时依赖后台的hw_server这个进程如果崩溃或端口被占用就会中断连接。可以用任务管理器关掉所有hw_server进程重新打开Vitis再试。板卡电源是否稳定。有些板子JTAG和PS供电是分开的如果PS没上电JTAG虽然能看到芯片但一旦尝试加载FSBL到OCM就会因为CPU没运行而中止。目标连接地址设置问题。Vitis默认通过本地TCF端口连接如果之前手动设置过远端地址端口对不上同样会取消。还有一个容易忽略的点如果板卡处于QSPI启动模式而QSPI里恰好有一个异常镜像BootROM启动失败导致PS进入异常状态也可能让JTAG无法建立控制。这时候先把启动模式切成JTAG再重新连接。4.2 Warning: failed to communicate with the flash chip这个日志经常出现在Program Flash的早期阶段完整信息类似Warning: failed to communicate with the flash chip, read/write operations will fail。它的直接含义是FSBL的QSPI驱动已经尝试和Flash交互但Flash没有给出正确的响应或者响应超时。按我的经验主要原因有这些Flash Type选错。Vitis里的Flash Type要选对W25Q256FV一般用qspi-x4-single但你板子如果接的是1-bit SPI模式那就要改成qspi-x1-single。模式不匹配时Flash ID可能都读不出来。片选信号没拉低。检查板卡QSPI片选有没有连接到Zynq的QSPI CS引脚有些板子把CS做成了GPIO控制需要软件手动拉低而FSBL默认不操作这个GPIO。HOLD引脚被拉低。W25Q256FV的HOLD引脚通常是IO3如果被拉低Flash会暂停通信。检查原理图上HOLD有没有接上拉电阻。供电不稳或Flash没焊接好。不要忽略这个基础问题我曾遇到一块板子过回流焊后Flash虚焊导致能识别JTAG但始终无法和Flash通信。FSBL里的QSPI配置参数和实际Flash不匹配比如时钟分频、模式寄存器配置不对。遇到这种情况可以试试降低QSPI时钟频率在Vitis的BSP设置里把QSPI时钟调低排除高速信号问题。如果上述都排除了还有一个办法直接Program Flash界面里手动选择Flash型号。Vitis会弹出一个下拉列表里面有很多Spansion/Cypress/Micron的QSPI型号如果你的W25Q256FV没有在列表里可以手动添加或选择同容量、同类型的兼容型号。选择时要注意地址模式32MB容量的Flash需要支持4字节地址。4.3 Vitis下载调试时“不识别芯片”怎么解决这个热搜问题经常出现在刚装完Vitis的用户身上。现象是Hardware Manager里空白或者连接时提示“No hardware target detected”。排查思路按下面顺序来检查驱动。Vitis连JTAG通常依赖FTDI驱动或Digilent驱动。在Windows的设备管理器里如果看到箭头感叹号先重装驱动。检查线缆类型。Platform Cable USB II、Digilent JTAG-HS3这类线缆有不同的驱动要求确认你用的线缆被当前Vitis版本支持。检查板卡的JTAG供电。有的板卡JTAG口需要板子主电源供电只插USB下载线是不够的。检查多个JTAG器件的菊花链。如果板上有多个JTAG器件Zynq需要正确配置链的位置Vitis有时识别不到是因为链上某个器件占用了IDCODE。试着重启硬件服务器。Vitis菜单里有时候有Restart Hardware Server或者直接关掉所有Vivado/Vitis进程重新打开。还有一种情况在隔离转接板或仿真头上JTAG信号的电平和Zynq不匹配。Zynq的JTAG引脚属于MIO bank通常电平是1.8V或3.3V取决于板卡设计。如果下载线是3.3V标准而Zynq所在bank是1.8V必须用转换板。4.4 烧写期间Flash扇区擦除失败Program Flash是“擦除-写入-校验”三步如果擦除阶段失败日志往往会指向某个扇区地址无法擦除比如出现Erase timeout或者反复重试。这里要注意W25Q256FV的擦除命令分为4KB扇区擦除、32KB块擦除、64KB块擦除和整片擦除。Vitis工具会根据镜像大小和Flash布局自动选择擦除粒度但个别情况下工具对这款Flash的支持不够完善会把擦除命令发错。这种情况的应急方案有三个改小镜像尺寸避开跨块边界的地址看是否固定在某一个地址失败。手动切到qspi-x1-single用最基础的单线SPI模式重试排除四线模式下IO2/IO3虚焊或配置问题。先用Vivado Hardware Manager里的Flash编程功能把整片擦除再回到Vitis写。这种做法相当于绕过程序自动擦除。还有一个点W25Q256FV有块保护Block Protection机制状态寄存器里的BP位如果被置位扇区会变成只读。FSBL或历史程序有可能修改过状态寄存器。遇到擦除不了的时候可以先用Flash编程指令把状态寄存器恢复成非保护状态。Vitis界面里有些版本提供“Erase Entire Device”选项实际执行时通常也会顺带清保护位。4.5 烧写成功但上电不启动或启动一半卡住这个现象比上面几种更隐蔽。烧写流程全程没报错校验也通过但把启动模式切到QSPI后板卡没有任何反应或者只打印几行FSBL日志就卡住。先查启动模式拨码是否真的切到了QSPI有的板卡拨码丝印和实际逻辑是反的我之前就被坑过。再查BOOT.BIN的组成是否正确最稳妥的做法是在Vivado/Vitis的Create Boot Image界面里重新生成一次确保启动头、FSBL、bitstream、app.elf的顺序和地址偏移正确。还有一个经常被忽略的点如果你的镜像超过16MB而FSBL或者BootROM没有正确进入4字节地址模式那高16MB的内容虽然被“写进去”了但启动时根本读不到。W25Q256FV上电默认3字节地址模式Zynq的QSPI驱动虽然能配置4字节模式但不是所有版本的FSBL都默认开启。遇到大镜像启动失败先确认FSBL的QSPI驱动配置里地址宽度是否正确或者干脆把镜像控制在16MB以内验证。如果启动只卡在FSBL阶段优先怀疑DDR初始化失败。Zynq的FSBL一大功能就是初始化DDR控制器如果DDR颗粒型号、时序参数和实际硬件不匹配FSBL会在DDR测试或者加载阶段卡死。这个和Flash本身没有关系但很多人不知道会误以为Flash没烧好。5. 固化后的启动验证、DDR问题与批量生产建议5.1 烧写完成后如何“证明”真的好了很多工程师的习惯是烧完马上断电切拨码其实更好的顺序是烧写完成后保持JTAG连接先读回Flash内容做个校验再切到QSPI启动模式。Vitis Program Flash默认带校验但如果你用的是命令行且没加-verify参数就要额外做一次读回验证。读回验证有两个层面在Vitis里重新执行Program Flash但不勾选擦除只做Verify或者使用Hardware Manager的Readback功能把Flash内容读成二进制文件和原始BOOT.BIN做对比。在U-Boot或Linux环境下执行sf probe sf read把Flash某段内容读到DDR再用md5sum和原文件比对。我个人更推荐第二种因为它在真实启动环境下验证了Flash可读性。如果U-Boot环境下能正常sf probe识别到W25Q256FV基本说明Flash通信、地址模式、ID识别都正常。5.2 Zynq-7020用JTAG固化Flash时必须使用DDR吗这问题被问得非常多。答案是不必须但在标准Vitis流程里DDR往往会被初始化一次于是很多人产生了“固化Flash必须依赖DDR”的错觉。Program Flash的机制是先把FSBL加载到Zynq的OCM里运行FSBL再去控制QSPI。FSBL是由Vivado硬件工程生成的如果硬件工程里启用了DDR控制器FSBL启动时就会初始化DDR。所以你会看到烧写过程中DDR其实已经被初始化了一遍但这不是“烧写Flash”本身的要求而是FSBL顺带做的动作。如果你的板卡没有DDR、DDR焊接有问题、或者DDR颗粒型号和Vivado配置不一致标准流程就会在FSBL阶段失败表现为Target DLL被打断、或者FSBL日志卡在DDR初始化相关位置。解决办法不是“必须有DDR”而是让FSBL跳过DDR初始化或者使用一个只针对QSPI的FSBL版本。在Vitis的BSP里可以调整FSBL源码把DDR初始化相关代码注释掉重新编译FSBL再用这个FSBL去Program Flash。这样即使板上没有DDR也能完成QSPI烧写。对于Zynq-7020这种片内只有256KB OCM的芯片如果你想在完全没有DDR的情况下跑应用程序程序必须链接在OCM里。固化这类小镜像时只要FSBL不初始化DDRQSPI烧写是可以顺利完成的。5.3 镜像布局建议不要一股脑塞在0地址很多人的BOOT.BIN就是把FSBL、bitstream、app.elf依次排开全部烧在Flash偏移0x0。这样不是不行但会给后续升级带来麻烦。更常见的做法是在BIF文件里显式指定每个分区的偏移地址the_ROM_image: { [bootloader] fsbl.elf [offset0x100000] system.bit [offset0x500000] app.elf }这样FSBL在0x0bitstream在1MB处应用程序在5MB处。后续如果只更新app就不需要把整个Flash擦掉重写可以按分区擦除和烧写速度更快风险也更小。实际项目中QSPI Flash不只有启动镜像还可以划出几个区域用来存参数、日志、或者做OTA备份。W25Q256FV有32MB空间合理规划分区能让开发和量产都轻松很多。我的建议是至少预留两个镜像槽位一个当前运行镜像一个备份镜像升级时先写备份区校验成功后再切换避免升级中途断电导致板子变砖。5.4 烧写速度优化和量产烧写开发阶段烧写慢一点无所谓的但到了量产阶段几十块板子每块等两三分钟生产效率就很受影响。烧写速度主要受QSPI时钟频率影响Vitis默认的FSBL里QSPI时钟通常比较保守可以适当提高。改FSBL的BSP设置把QSPI时钟从几十MHz提高到100MHz以上但前提是板子布线质量要过关否则会擦写失败或校验不一致。量产烧写还要考虑另一个问题不要用JTAG一板一板慢慢烧。如果产品量级达到几百上千建议优先考虑以下方案制作烧写底板用Vivado Hardware Manager配合批量烧写脚本通过多路JTAG转接板同时烧写多块板。如果产品有网口或USB口先把一个Bootloader烧进Flash后续通过Bootloader配合串口或网口升级应用程序这也是最常见的量产流程。出厂前把W25Q256FV先通过编程器烧好镜像再贴片到PCB上适合镜像内容固定、不需要现场烧写的场景。我个人在实际项目里的习惯是开发阶段用Vitis GUI烧方便观察日志小批量试产用命令行脚本烧稳定可追溯真正量产让产线用专用烧录底板。三种方式并不冲突重要的是把镜像内容、分区布局、烧写版本号都管理好否则半年后连自己都分不清板上烧的是哪一版。回到这篇文章的主题Vitis给Zynq烧写W25Q256FV本身并不神秘本质就是FSBL当代理、QSPI控制器操作NOR Flash。把硬件连接、启动模式、镜像格式、FSBL可用性这几个要素理清楚绝大多数报错都能在十分钟内定位。如果以后你在烧写时再遇到“Target DLL has been cancelled”先别急着重装Vitis按第4章的链路排查一遍大概率是连接问题或FSBL启动问题。烧写已经到了最后一步多花几分钟做一次完整的启动验证能省下后面一整天的调试时间。