
芯片上电后跑的是不是你想要的逻辑这个问题每个做FPGA的人迟早都要面对。不管是刚接触Vivado的学生还是已经在用Zynq做量产的老手“烧写”和“固化”这两个词几乎天天挂在嘴边但真要动手时坑一点也不少。我见过不少人拿着一个.bit文件想固化到Flash里折腾半天发现掉电还是丢程序也见过有人把BOOT.bin烧错分区板子直接变砖。这篇文章就把Vivado下从临时加载到真正固化的全流程讲清楚包括Zynq的BOOT.bin、MicroBlaze的bitelf合并、常见报错的定位思路尽量让你少走弯路。1. 烧写与固化的本质区别1.1 烧写是“临时加载”固化才是“永久保存”FPGA芯片本身是基于SRAM工艺的这意味着它的逻辑配置信息一旦断电就会全部丢失。你通过JTAG把.bit文件加载到芯片里只是改变了当前这块芯片的运行状态并没有写进任何非易失存储介质。我把这叫“临时烧写”速度快、适合调试但掉电就什么都没了。固化就不一样了。固化的本质是把配置数据写入板上的SPI Flash、eMMC或QSPI Flash这类非易失存储介质里。下次上电时FPGA芯片通过自身的启动流程主动从Flash读出配置数据完成自我加载。这也是“固化”二字真正的含义——把程序牢牢固定在板子上断电不丢上电自启。很多新手容易犯的错是以为点了“Program Device”就万事大吉。实际上Vivado的Hardware Manager里默认的Program操作只是加载到FPGA RAM里并不写Flash。想要固化必须选择“Add Configuration Memory Device”或者用命令行方式执行写Flash操作。1.2 SRAM工艺决定了启动流程理解固化为啥麻烦先要理解FPGA的启动机制。以7系列和UltraScale系列为例芯片上电后由内部的启动逻辑决定从哪个接口读取配置数据。常见模式有JTAG模式优先级最高调试时最常用Master SPI模式FPGA主动从SPI Flash读取Master BPI模式并行NOR Flash工业场景常见Slave SelectMAP / Slave SPI由外部主控引导加载Zynq的BootROM模式由PS侧主导读取BootROM头决定启动源这也是为什么固化前必须核对开发板上的启动模式跳线。比如很多板子默认跳线在JTAG模式你就算把Flash烧好了上电后芯片还是不会主动去读Flash。逻辑上不是错是模式没切回来。1.3 “看这一篇就够了”的目标范围我打算把内容控制在以下几条主线上纯FPGA比如Artix-7、Kintex-7的bit文件临时加载与SPI Flash固化Zynq-7000系列通过SDK/Vitis生成BOOT.bin并烧写QSPIMicroBlaze软核处理器对应的bit与elf合并方案以及我自己实际踩过报错的排查思路。这几个方向覆盖了绝大多数开发者的日常需求尤其是做板级调试和产品化的人。2. 正式动手前的准备版本、驱动与连接2.1 Vivado版本差异带来的坑烧写固化这件事不同版本Vivado的操作入口变动不大但小细节确实有差别。我自己的工作机从2018.3一路用到2022.1几个明显的变化值得提一下2019.1及以后版本Hardware Manager支持自动探测连接的板卡老版本经常要手动刷新2020.1开始硬件服务器hw_server的启动方式有变化网络远程调试时经常遇到版本不匹配Vitis一键烧写Flash的功能比SDK时代更统一但也更容易因为FSBL版本不对而失败关于安装网上很多“vivado安装教程”提到WinPcap安装失败的问题。Vivado的硬件管理器本身不依赖WinPcap真正依赖它的是老版本SDK里的一些网络调试功能。如果你只是做本地JTAG烧写WinPcap装不上可以先忽略不用为这个卡住安装流程。但要注意如果以后要用System Debugger连目标板做在线调试WinPcap缺失会导致连接失败到时候再补装即可。2.2 下载器驱动与识别问题Windows下最容易翻车的环节就是JTAG驱动。Xilinx平台电缆Platform Cable USB II在Win10/Win11下经常要手动安装驱动Digilent的下载器则要装Digilent Plugin。很多“识别不到器件”的问题不是Vivado坏了而是驱动没给到位。我的建议优先使用Vivado安装目录自带的drivers路径一般在C:\Xilinx\Vivado\版本\data\xicom\cable_drivers\nt64插上下载器后设备管理器里能看到“Xilinx USB Cable”且没有感叹号才算正常如果插拔后依然识别不到试着重启hw_server或者彻底重启Vivado比反复插拔有效2.3 连接和上电顺序连接JTAG时建议先接好下载器与板子再打开Vivado并启动Hardware Manager。上电顺序上我习惯先给板子上电再启动hw_server避免服务器起来时器件还没就绪导致扫描失败。还有一个容易被忽略的点板卡多个JTAG器件串联时Vivado会列出链上所有器件。你要确认自己选中的是目标FPGA别烧到链路上的其他芯片。我见过有人把固件写进了配置链上的CPLD结果Debug半天找不到原因。3. 临时烧写的完整操作与原理3.1 打开Hardware Manager并加载设备临时烧写的入口是Vivado左下角的“Open Hardware Manager”。连接好下载器之后点“Open Target”Vivado会通过hw_server扫描JTAG链。如果这里扫描不到大概率是驱动问题其次是硬件连接问题。还有少部分情况是板子供电不足JTAG链上电平不稳定。3.2 加载.bit文件的步骤在Hardware Manager界面里右键目标FPGA器件选择“Program Device”会弹出比特流文件选择框。默认情况下Vivado会带上综合实现后生成的.bit文件路径。这一操作的本质是通过JTAG把配置数据写入FPGA的配置SRAM然后触发芯片的配置逻辑完成加载。整个过程中Flash完全没有参与。3.3 临时烧写的实用场景临时烧写通常用于功能调试。比如你改了RTL代码想快速在板子上验证某个IP的行为直接烧.bit是最快的路径。调试时建议在Vivado里勾选“Enable JTAG DEBUG”相关的选项或者在约束文件里设置好ILA调试探针不然你烧进去之后没法用Vivado Logic Analyzer抓信号。4. 固化到SPI Flash的全流程4.1 生成配置文件.bin与.mcs的选择与计算要固化先得生成适合Flash存储的配置文件。常用的三种格式.bit原始配置比特流只用于JTAG调试不能直接烧Flash.bin二进制格式可以指定起始地址烧写到Flash.mcsIntel HEX格式包含地址信息适合直接整片烧写生成方式很简单在Vivado里点击“Generate Bitstream”之后继续点“Generate Memory Configuration File”里面可以选择格式和接口。这里有一个关键计算你的.bit文件大小决定了Flash容量需求。比如Artix-7 35T的bit文件大约3.6Mbit左右加上一些头信息选8Mbit的SPI Flash绰绰有余。但如果是Zynq的BOOT.bin里面包含了FSBL、bitstream和应用程序动辄几十MB那就得用至少64MB的QSPI Flash。4.2 用Hardware Manager烧写配置存储在Hardware Manager里右键FPGA选择“Add Configuration Memory Device”Vivado会问你目标Flash型号。选对型号后加载刚才生成的.mcs或.bin文件点击Program。整个烧写过程会先擦除FlashErase再写入数据Program最后做校验Verify。如果Flash里原有内容与新数据不一致校验阶段会直接报错这是正常的保护机制。关于SPI Flash型号匹配建议优先选择Vivado列表里有的型号。有些国产Flash芯片虽然兼容但ID不在列表里烧写时会提示device not supported。这时候可以尝试用“Generic SPI Flash”配置但速度可能不是最优。4.3 用命令行固化批量生产更高效当你有几十块板子要烧的时候GUI操作太慢了。我更推荐用命令行方式。在Vivado的Tcl Console里执行connect_hw_server open_hw_target set_property PROGRAM.FILE {./output.mcs} [current_hw_device] program_hw_devices [current_hw_device]这个方式适合写脚本循环处理多块板卡。配置那个环节如果对STM32、嵌入式开发比较熟你会发现这套逻辑和MCU的烧录器思路很像只是FPGA的配置协议更专一。4.4 固化后的验证断电重启才是王道每次固化完我都坚持做一件事断开JTAG、拔掉下载器、整板断电再重新上电观察FPGA是否自动加载程序。有几次我以为烧成功了结果显示正常但其实是JTAG还连着FPGA被JTAG链上的电平干扰。断开下载器后再上电才发现根本没固化成功。注意固化完成后必须将开发板启动模式从JTAG切换到对应的Master SPI/QSPI模式否则上电后不会自动加载。5. Zynq平台的烧写与固化细节5.1 Zynq的启动架构BOOT.bin从何而来Zynq和纯FPGA有本质区别芯片内部包含PSProcessing System和PLProgrammable Logic两部分。PL侧的配置数据仍然要加载但加载动作是由PS侧的BootROM配合FSBL完成的。所以Zynq固化不能直接烧.bit要生成一个BOOT.bin。BOOT.bin的典型组成FSBLFirst Stage Boot Loader初始化DDR、时钟等引导启动比特流PL配置数据可选应用程序运行在PS上的裸机程序或U-Boot/内核生成BOOT.bin的常规路径有两条一是用Vitis的“Create Boot Image”功能把FSBL、bitstream、elf打包二是用命令行工具bootgen。我个人在批量生成时都用bootgen脚本效率高很多。5.2 用Vitis/SDK固化QSPI Flash的流程以Vitis 2020.2为例流程大致是先在Vivado里完成PL工程并导出xsa文件在Vitis里基于xsa创建应用工程编译出elf创建Boot Image添加FSBL、bitstream、elf选择QSPI分区在Vitis的“Xilinx - Program Flash”里选择BOOT.bin烧写QSPI这里注意FSBL的选择Vitis会根据xsa自动关联FSBL但如果你修改了DDR型号或时钟配置FSBL没重新编译启动大概率会卡死在DDR初始化。5.3 QSPI分区与地址规划Zynq的QSPI Flash在BootROM眼里是一个顺序存储的启动设备但实际你可以把整个Flash分成多个区域。我建议的布局区域内容起始地址QSPI分区1BOOT.bin0x0分区2内核镜像或App0x200000分区3文件系统/配置参数0x800000烧写时用Program Flash指定BOOT.bin起点0x0即可其他分区可以后续通过应用程序自更新也可以临时用JTAG写入。注意地址对齐到Flash扇区边界不齐的话擦除会影响到相邻分区数据。5.4 Zynq启动失败怎么排查启动失败最常见的现象是串口没有任何输出或者只打印BootROM的头部信息就中断。我的排查顺序是检查启动模式跳线QSPI模式的BOOT_MODE引脚必须设正确检查BOOT.bin是否真的烧到了0x0检查FSBL能否正常初始化DDR串口打印卡在DDR初始化就换FSBL检查bitstream是否加密或与FSBL版本不匹配用SDK的“Program Flash”再烧一次并做Verify排除Flash读写异常6. MicroBlaze软核烧写的特殊处理6.1 纯bit加载与带程序的ELF加载如果MicroBlaze设计里没有额外运行程序直接烧.bit就行处理器只会停留在复位状态。但大多数MicroBlaze工程都有C程序在跑。这种情况下你要把.bit文件和.elf文件合并起来。按热搜词里提到的“2018.3 如何microblaze mmi bit elf文件烧写msc”这个问题其实困扰过不少人。6.2 利用mmi文件合并bit和elfMicroBlaze工程在Vivado里会生成一个MMI文件Memory Map Information它描述了BRAM在系统内存空间中的映射关系。利用这个信息可以将elf中的初始化数据合并进bit文件生成一个包含程序代码的配置比特流。操作路径是在Vitis或SDK里使用“Xilinx - Program FPGA”的方式它会自动读取MMI文件把elf初始化数据写到BRAM对应的bit流位置。另一个方式是先在Vivado里综合出带Debug Hub的bit再用SDK“Xilinx - Program FPGA”加载。如果不用MMI直接烧bit后处理器BRAM是空的程序当然跑不起来。6.3 固化带程序的MicroBlaze先临时烧写bit和elf确认程序能跑再把配置数据连同BRAM初始数据合并成完整bit流生成.mcs固化到Flash。顺序不能反否则容易把空程序固化进去。7. 常见报错与排查实录7.1 DRCRTSTAT-2报错这个词在热搜里反复出现。DRC RTSTAT-2一般是布线资源或跨时钟域约束的DRC错误会让Implement Design变红。解决思路打开Reports里的DRC报告定位到具体违规网络检查是否缺少set_clock_groups约束异步时钟没隔离检查是否用了BUFGMUX跨时钟切换这个资源本身有切换限制跑一下report_clock_interaction看有没有红色交互7.2 Implement Design变红先看log再重跑Implement变红通常伴随Critical Warning。遇到不要慌打开runme.log搜索ERROR和CRITICAL WARNING按时间顺序从第一个开始排查。常见原因时序收敛不了查看WNS和TNS针对关键路径优化资源超卖LUT/FF利用率超过100%重新裁剪逻辑布局布线失败检查是否有非法约束比如把IO约束到不支持的bank全局缓冲不足信号驱动能力不够参考BUFG规则7.3 串口烧写失败热搜里也有“串口烧写失败”。这个词在FPGA固化场景里一般指两种一是Zynq通过UART下载BOOT.bin失败二是纯FPGA用自定义串口引导时失败。UART启动模式本身存在速度慢、稳定性差的问题。串口烧写失败先查三样波特率是否匹配、启动引脚是否设成UART模式、串口连接是否经过电平转换芯片。如果板子上有CP2102或FT232Windows驱动不对也可能导致数据错乱。优先用FTDI公版驱动并且不要用USB延长线这会让UART时序变差。7.4 烧完之后板子没反应这是最让人头疼的场景。我排查顺序确认启动模式跳线必须与Flash类型匹配确认Flash型号Vivado配置列表里选的型号和板子实际型号是否一致确认.mcs生成时的接口设置SPI的width是x1还是x4flash的位宽要和配置一致确认复位电路FPGA上电后INIT_B引脚状态是否正常换一颗Flash试试我遇到过Flash本身出厂就有坏块的7.5 生成比特流失败如果卡在综合或实现阶段先备份工程然后勾选“Reset Run”重新跑一次。有时候是Vivado的增量编译缓存坏了。重新生成前检查IP核版本旧工程打开时IP核需要升级升级完再重新综合能消除大部分低级问题。8. 实操经验与关键心得最后分享几个我做烧写固化这几年的个人心得。第一点版本统一。工程文件、Vivado版本、下载器驱动、操作系统位数最好全部保持一致。我见过好几个问题最后追根溯源都是同事用2019.2烧的flash拿到2021.1的板子上调试hw_server版本不匹配导致器件列表空的。第二点养成固化后Verify的习惯。哪怕你用的是原厂Flash批量烧写时也建议把Verify打开。Flash是有寿命的擦写次数多了某些扇区会不稳定Verify能第一时间发现问题。第三点把常用烧写命令写成脚本。我自己的批次脚本大概长这样open_hw_manager connect_hw_server open_hw_target set_property PROGRAM.FILE {BOOT.bin} [current_hw_device] program_hw_devices [current_hw_device] disconnect_hw_server close_hw_manager每次换板子后只要改路径和波特率就行省心很多。第四点做好掉电测试。实验室里JTAG线可能长期连着这会让“固化成功”的假象掩盖问题。量产前的每块板子都应做断电冷启动测试确认真正脱离调试器后依然能启动。Vivado的烧写固化不算难但细节非常多。如果你照着这篇文章一步步走从临时加载到SPI固化再到Zynq的BOOT.bin应该能避开我当年踩过的绝大多数坑。真遇到新的怪问题先查电源、再查启动模式、最后查Flash型号这个顺序基本能解决八成故障。