1. 为什么程序固化这件事值得单独拿出来讲很多人第一次接触 FPGA 开发流程通常是这样的写好 Verilog 代码跑完综合和实现生成比特流然后用下载器把比特流直接灌进 FPGA 里板子上的灯亮了、串口有输出了项目就算跑通了。但等你断电再重新上电发现板子跟没烧过一样什么反应都没有。这时候才意识到一个问题比特流是烧到 FPGA 的 SRAM 里的掉电就丢。这就是程序固化要解决的事情。所谓固化就是把比特流写进板载的 Flash 芯片里让 FPGA 每次上电后自动从 Flash 读取配置数据完成自我加载。做完这一步你的板子才真正变成一个上电即工作的独立设备而不是每次都要连着电脑、开着 Vivado 才能跑的开发原型。我见过不少初学者在这一步卡住原因五花八门有人生成的 MCS 文件格式不对有人 Flash 型号选错了有人烧录时提示 cannot load flash device description还有人烧完之后 FPGA 根本不从 Flash 启动。这些问题看起来零散其实背后都指向几个固定的知识点。这篇内容就是围绕 Vivado 2023.2 这个版本把程序固化到 Flash 的完整流程拆成三步讲清楚同时把那些高频报错的排查思路一并整理出来。适合谁看如果你已经能用 Vivado 跑通一个基本的 FPGA 工程比特流也能正常生成但对怎么让板子脱机运行这件事还没搞明白那这篇就是写给你的。如果你已经固化过几次但总在某个环节翻车也可以对照着排查。在展开之前先把几个核心概念对齐一下不然后面容易懵。比特流BitstreamVivado 综合实现之后生成的.bit文件描述 FPGA 内部逻辑资源和连线关系的配置数据。它可以直接通过 JTAG 下载到 FPGA 的配置存储器里但掉电丢失。MCS 文件一种 ASCII 格式的 PROM 配置文件本质上是把.bit文件转换成适合烧录到 Flash 的格式。Vivado 里通过 Create Configuration Memory File 生成扩展名通常是.mcs。Flash 烧录器认的就是这个文件。Flash 芯片板子上那颗用来存储配置数据的非易失存储器常见的有 SPI Flash如 W25Q 系列、M25P 系列和 BPI Flash。绝大多数入门和中端 FPGA 开发板用的是 SPI Flash容量从 4Mb 到 128Mb 不等。配置模式FPGA 上电后从哪里加载配置数据由模式引脚M[2:0]决定。Master SPI 模式是最常见的FPGA 作为主机主动从 SPI Flash 读数据。固化能不能成功跟这个模式设置对不对有直接关系。把这四个概念理清楚后面的操作就不会觉得是在照着点按钮了。2. 第一步生成正确的 MCS 固化文件2.1 从比特流到 MCS 的转换逻辑Vivado 生成的.bit文件不能直接丢给 Flash 烧录器因为.bit文件里包含了大量 FPGA 配置命令和头部信息格式跟 Flash 需要的存储格式不一样。MCS 文件做的事情是把比特流数据按字节重新组织加上地址信息和校验变成一种通用的 PROM 格式。在 Vivado 2023.2 里生成 MCS 有两种路径一种是在 GUI 里通过菜单操作另一种是用 Tcl 命令。两种方式本质一样但 Tcl 方式更适合脚本化、可复现的流程。先说 GUI 路径。在 Flow Navigator 里找到 Program and Debug展开后点击 Generate Bitstream 先把比特流跑出来。比特流生成成功后在同一个区域会看到 Create Configuration Memory File 的选项。点进去之后会弹出一个配置窗口需要填几个关键参数。这里有个细节很多人会忽略如果你在生成比特流的时候没有勾选 Bin File 或者没有指定正确的比特流路径Create Configuration Memory File 可能会找不到源文件。所以稳妥的做法是先确认.bit文件已经生成在工程目录下再去做 MCS 转换。2.2 关键参数怎么填才不出错配置窗口里有几个参数需要认真对待填错了后面烧录必出问题。Format选 MCS。虽然也有 BIN 格式可选但 MCS 兼容性更好大多数烧录工具都认。Memory Part这是最容易出错的地方。你需要选择板子上实际使用的 Flash 型号。比如常见的 W25Q128FV、W25Q64FV、M25P16 等。如果列表里找不到你的型号可以选一个容量和接口兼容的替代型号但要注意页大小和扇区大小是否一致。提示如果你不确定板子上 Flash 的具体型号可以看开发板的原理图或者丝印。丝印上通常会印着类似 W25Q128 这样的字样。实在找不到用flash id命令在烧录工具里读一下也能识别出来。Interface选 SPIx1、SPIx2 或 SPIx4取决于你的 Flash 支持的接口宽度。大多数入门板子用 SPIx1 就够了。如果你选了 SPIx4 但 Flash 不支持烧录会失败。Load Bitstream Files勾选这个选项然后指定.bit文件的路径。Start Address默认 0x00000000 就行除非你有特殊的分区需求。Data File指定输出的 MCS 文件路径和名称。填完这些点 OKVivado 就会在指定目录生成.mcs文件。生成过程中可以在 Tcl Console 里看到进度信息如果报错会直接显示出来。2.3 用 Tcl 命令批量生成 MCS如果你经常需要重新生成 MCS或者想把这一步集成到自动化流程里用 Tcl 命令会更高效。在 Vivado 的 Tcl Console 里输入write_cfgmem -format MCS -interface SPIx1 -size 16 -loadbit up 0x00000000 ./project.runs/impl_1/top.bit -file ./output/top.mcs这条命令的参数含义-format MCS输出格式为 MCS。-interface SPIx1SPI 单线接口。-size 16Flash 容量为 16MB注意单位是 MB不是 Mb这里容易搞混。-loadbit up 0x00000000 ...从地址 0 开始加载比特流文件。-file输出文件路径。注意-size参数的单位是 MB但 Flash 型号通常标的是 Mb。比如 W25Q128 是 128Mb换算成 MB 就是 16MB。如果你填错了生成的 MCS 文件大小会不对烧录时可能报地址越界。生成完 MCS 之后建议用文本编辑器打开看一眼文件头。正常的 MCS 文件开头应该是类似:020000040000FA这样的记录如果看到一堆乱码或者文件大小为 0说明生成过程出了问题。2.4 生成 MCS 时的常见报错报错一ERROR: [Writecfgmem 20-1] Bitstream file not found这个错误说明-loadbit参数里指定的.bit文件路径不对。检查一下路径是否正确特别是相对路径的基准目录。Vivado 的 Tcl Console 默认工作目录是工程根目录但如果你在别的目录下执行命令路径就要相应调整。报错二ERROR: [Writecfgmem 20-3] Invalid size value-size参数填的值不在支持范围内。Vivado 支持的 size 值通常是 1、2、4、8、16、32、64、128 这些。如果你填了个 12 或者 24就会报这个错。选一个大于等于实际 Flash 容量的值就行。报错三生成的 MCS 文件特别大远超 Flash 容量这种情况通常是因为-size填太大了或者比特流文件本身异常。先确认比特流文件大小是否正常一般几十 KB 到几 MB再检查-size参数。3. 第二步把 MCS 烧进 Flash 的完整操作3.1 硬件连接与模式引脚确认在烧录之前有两件事必须先确认否则后面全是无用功。第一件事是下载器连接。Vivado 2023.2 支持常见的 Xilinx 下载器Platform Cable USB、Digilent HS2/HS3 等。把下载器接到板子的 JTAG 接口上然后打开 Vivado 的 Hardware Manager点击 Open Target → Auto Connect。如果连接成功会在 Hardware 窗口里看到 FPGA 器件和对应的 Flash 器件。如果 Auto Connect 失败先检查驱动。Windows 设备管理器里应该能看到下载器对应的 USB 设备如果没有或者有黄色感叹号说明驱动没装好。Vivado 安装目录下的data\xicom\cable_drivers\nt64里有驱动安装程序手动装一下。第二件事是配置模式引脚。FPGA 上电后从哪个接口加载配置数据由模式引脚决定。对于 Master SPI 模式M[2:0] 通常设置为 001。如果你用的是开发板板子上一般有跳线帽或者拨码开关来设置模式默认出厂设置通常就是 Master SPI。但如果你自己画的板子就要仔细检查这几个引脚的电平。提示有些板子为了兼容 JTAG 调试和 Flash 启动两种模式会用一个跳线来选择。烧录时可能需要把跳线拨到特定位置。具体看板子的原理图。3.2 在 Hardware Manager 里添加 Flash 器件连接上 FPGA 之后在 Hardware 窗口里右键点击 FPGA 器件选择 Add Configuration Memory Device。这时候会弹出一个对话框让你选择 Flash 型号。这里就是很多人卡住的地方如果列表里找不到你的 Flash 型号或者选了型号之后报 cannot load flash device description怎么办先说找不到型号的情况。Vivado 自带的 Flash 型号库虽然很全但不可能覆盖市面上所有芯片。如果你用的是比较冷门的型号可以选一个参数兼容的替代型号。关键参数是容量、页大小、扇区大小、SPI 模式。只要这几个对得上烧录通常没问题。再说 cannot load flash device description 这个报错。这个错误的意思是 Vivado 找不到对应 Flash 的描述文件。描述文件通常放在 Vivado 安装目录的data\xicom\cfgmem\下面扩展名是.xml。如果文件缺失或者路径不对就会报这个错。解决办法有几个一是确认 Vivado 安装完整没有杀毒软件误删文件二是手动指定描述文件路径三是换一个型号试试。我遇到过几次这个报错最后发现是杀毒软件把某个 XML 文件隔离了恢复之后就好了。3.3 烧录操作与进度观察选好 Flash 型号之后右键点击 Flash 器件选择 Program Configuration Memory Device。弹出的窗口里需要指定 MCS 文件路径然后点 OK 开始烧录。烧录过程中Vivado 会在下方显示进度条和日志信息。正常情况下烧录一个 16MB 的 Flash 大概需要几十秒到几分钟取决于下载器速度和 Flash 写入速度。烧录成功的标志是日志里出现 Program/Verify Operation successful 或者类似的提示。如果中途报错日志里会有详细的错误信息根据错误类型去排查。烧录完成后先断电再重新上电。这时候 FPGA 应该会自动从 Flash 加载配置板子上的功能应该正常运行。如果上电后没反应说明固化没成功需要进一步排查。3.4 烧录失败的典型报错与排查报错一ERROR: Flash download failed - Target DLL has been cancelled这个报错出现频率极高。原因通常是下载器和 Flash 之间的通信出了问题。排查步骤确认下载器连接牢固JTAG 线没有松动。确认 FPGA 已经上电且电源电压正常。确认 Flash 型号选择正确。尝试降低 JTAG 时钟频率。在 Hardware Manager 的设置里把 JTAG 频率从默认的 15MHz 降到 6MHz 或更低。如果以上都不行尝试用 Erase 功能先擦除 Flash再重新烧录。报错二WARNING: Failed to communicate with the Flash chip, read/write operations will be disabled这个警告说明 Vivado 无法跟 Flash 芯片建立通信。可能的原因包括Flash 芯片没有供电。有些板子的 Flash 供电是独立的需要确认电源正常。SPI 引脚连接有问题。检查 FPGA 和 Flash 之间的 SPI 信号线CS、CLK、MOSI、MISO是否连通。Flash 芯片损坏。这种情况比较少见但如果以上都排除了可以考虑换一颗 Flash 试试。报错三烧录成功但 FPGA 不启动这种情况最让人头疼因为烧录过程没报错但结果不对。排查思路确认配置模式引脚设置正确。如果模式引脚设成了 JTAG 模式FPGA 上电后不会从 Flash 加载。确认 MCS 文件里的比特流是正确版本的。有时候工程改了但 MCS 没重新生成烧进去的是旧版本。确认 Flash 的起始地址正确。如果比特流不是从 0x00000000 开始存储的FPGA 可能找不到配置数据。检查 FPGA 的 DONE 引脚。上电后 DONE 引脚应该拉高表示配置完成。如果一直是低电平说明配置过程失败了。4. 第三步验证固化结果与脱机运行4.1 断电重启验证法最直接的验证方式就是断电再上电。把板子的电源拔掉等几秒钟再插上。观察板子的行为如果 FPGA 正常从 Flash 加载配置DONE 灯会亮起板子上的功能会正常运行。如果 DONE 灯不亮或者闪烁说明配置失败。如果板子完全没反应检查电源和模式引脚。这个验证方法虽然简单但要注意一点有些板子的 DONE 灯和电源灯是同一个或者 DONE 灯的位置不明显。如果你不确定哪个灯是 DONE可以看原理图确认。4.2 用 JTAG 回读验证如果断电重启后板子没反应但又想确认 Flash 里到底有没有数据可以用 JTAG 回读的方式验证。在 Hardware Manager 里右键点击 Flash 器件选择 Read Configuration Memory Device。Vivado 会把 Flash 里的内容读出来保存为一个文件。你可以把这个文件和原始 MCS 文件做对比看看是否一致。如果回读出来的数据跟原始 MCS 不一致说明烧录过程有问题需要重新烧录。如果一致但 FPGA 还是不启动那问题就出在配置模式或者硬件连接上。4.3 脱机运行时的注意事项固化成功之后板子就可以脱机运行了。但有几个细节需要注意第一Flash 的寿命。SPI Flash 的擦写次数通常在 10 万次左右虽然看起来很多但如果你频繁地重新烧录也会消耗寿命。建议在开发阶段用 JTAG 直接下载比特流调试只在最终版本才固化到 Flash。第二配置时间。FPGA 从 Flash 加载配置需要一定时间通常在几十毫秒到几百毫秒之间。如果你的系统对启动时间有要求需要把这个时间考虑进去。第三多版本管理。如果你的项目有多个版本需要切换可以考虑在 Flash 里划分多个分区每个分区存一个版本的比特流通过模式引脚或者外部信号来选择从哪个分区加载。这属于进阶用法入门阶段先掌握单版本固化就行。第四Flash 加密。有些项目对安全性有要求需要对 Flash 里的比特流进行加密。Xilinx 的 FPGA 支持比特流加密功能但需要额外的密钥管理和配置步骤。这个后面可以单独展开讲。5. 那些年我踩过的固化坑5.1 Flash 型号选错导致的诡异现象有一次我帮朋友调试一块板子固化之后 FPGA 死活不启动。烧录过程没报错回读数据也正常但就是不上电。折腾了半天最后发现是 Flash 型号选错了。板子上用的是 W25Q128JV但我在 Vivado 里选的是 W25Q128FV。这两个型号容量一样但页大小和扇区结构有细微差别。Vivado 按照 FV 的参数去写 JV 的芯片虽然能写进去但数据布局不对FPGA 读出来就是乱的。这个坑的教训是Flash 型号一定要精确匹配不能只看容量。如果实在找不到完全一致的型号至少要确认页大小、扇区大小、SPI 模式这三个参数一致。5.2 比特流版本不对导致的假成功还有一次我改了一版代码重新生成了比特流但忘记重新生成 MCS 文件。烧录的时候用的还是旧版本的 MCS结果板子上跑的还是旧功能。当时我还以为是代码没生效查了半天才发现是 MCS 没更新。这个坑的教训是每次修改代码后比特流和 MCS 都要重新生成。可以在 Vivado 里把这两个步骤串成一个自动化流程避免遗漏。5.3 JTAG 时钟太快导致的通信失败有一次用一块国产下载器烧录 Flash一直报 Target DLL has been cancelled。换了下载器、换了线、换了板子都不行。最后试着把 JTAG 时钟从 15MHz 降到 3MHz一次就成功了。这个坑的教训是不是所有下载器都能跑高时钟。国产下载器或者质量一般的线材在高频下容易丢包。遇到通信类报错先降时钟试试往往能解决大部分问题。5.4 模式引脚被忽略导致的烧了没用这个坑最隐蔽。板子上有一个跳线帽用来选择启动模式默认是 JTAG 模式。我烧录完 Flash 之后没有把跳线帽拨到 SPI 模式结果 FPGA 上电后还是从 JTAG 加载自然不启动。这个坑的教训是固化之前先确认模式引脚。如果是自己画的板子更要仔细检查 M[2:0] 的电平设置。6. 几个提升效率的实操技巧6.1 把 MCS 生成和烧录串成脚本如果你经常需要重复固化流程可以写一个 Tcl 脚本把生成 MCS 和烧录 Flash 的步骤串起来。这样每次只需要跑一遍脚本不用手动点菜单。# 生成 MCS write_cfgmem -format MCS -interface SPIx1 -size 16 \ -loadbit up 0x00000000 ./project.runs/impl_1/top.bit \ -file ./output/top.mcs # 连接硬件 open_hw_manager connect_hw_server open_hw_target # 烧录 Flash current_hw_device [lindex [get_hw_devices] 0] refresh_hw_device -update_hw_probes false [current_hw_device] create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts {w25q128fv-spi-x1_x2_x4}] 0] set_property PROGRAM.BLANK_CHECK 0 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]] set_property PROGRAM.ERASE 1 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]] set_property PROGRAM.CFG_PROGRAM 1 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]] set_property PROGRAM.VERIFY 1 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]] set_property PROGRAM.CHECKSUM 0 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]] program_hw_cfgmem -hw_cfgmem [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]这个脚本可以根据你的实际工程路径和 Flash 型号做调整。跑通一次之后后面就省事了。6.2 用 Flash ID 确认芯片型号如果你不确定板子上 Flash 的具体型号可以在 Hardware Manager 里读 Flash ID。具体操作是连接 FPGA 后在 Tcl Console 里输入puts [get_property REGISTER.CONFIG_MODE [current_hw_device]]或者用烧录工具自带的 Read Flash ID 功能。读出来的 ID 可以跟 Flash 数据手册对照确认具体型号。6.3 保留一份黄金 MCS每次固化成功后把对应的 MCS 文件和比特流文件备份到一个固定目录标注好版本号和日期。这样如果后面出了问题可以快速回退到已知可用的版本。6.4 注意 Vivado 版本的兼容性Vivado 2023.2 生成的 MCS 文件在旧版本的 Vivado 里可能不兼容。如果你需要在多台电脑上操作尽量保持 Vivado 版本一致。另外不同版本的 Vivado 对 Flash 型号库的支持也有差异新版本通常支持更多型号。7. 关于固化这件事的一些个人体会FPGA 程序固化这个操作步骤本身并不复杂三步就能搞定生成 MCS、烧录 Flash、验证启动。但真正让人头疼的是各种报错和异常情况。我自己的经验是大部分固化失败都不是操作问题而是信息不对称——不知道 Flash 型号、不知道模式引脚怎么设、不知道下载器时钟该调多少。所以我的建议是在动手固化之前先把这几件事查清楚板子的 Flash 型号是什么、配置模式引脚怎么设置、下载器支持的最高时钟是多少。这些信息通常在开发板的原理图或者用户手册里能找到。如果找不到就去问板子的卖家或者查芯片数据手册。另外固化失败的时候不要慌按照生成 MCS → 烧录 → 验证这个流程逐步排查。先确认 MCS 文件没问题再确认烧录过程没报错最后确认 FPGA 启动模式正确。大部分问题都能通过这个思路定位到。最后说一个我自己的习惯每次固化成功后我都会在板子上贴一个小标签写上固化的日期和版本号。这样过一段时间回头看能快速知道板子上跑的是哪个版本。这个习惯看起来不起眼但在管理多个项目的时候特别有用。