1. 从bit到mcs:为什么FPGA掉电就“失忆”
很多刚接触Xilinx FPGA开发的朋友都有过这样的困惑:明明用ISE烧录了bit文件,开发板跑得好好的,一断电再上电,程序就没了,FPGA又变回一张白纸。这不是你的板子坏了,而是FPGA的配置存储机制决定的。
FPGA内部用来存放配置数据的单元是易失性的SRAM结构,掉电之后数据全部丢失。bit文件本质上就是一份“配置说明书”,它告诉FPGA内部的查找表、布线开关、IOB该怎么连接。JTAG下载bit的过程,就是把这本说明书临时塞进SRAM里,所以断电即失效。要想让FPGA上电自动加载程序,就必须把配置数据存到非易失性存储器里,最常见的就是板载的SPI Flash。
而ISE工具链里,把bit转成能烧进Flash的格式,用的就是iMPACT生成的mcs文件。mcs是Intel HEX格式的一种变体,专门用于存储配置数据,它带有地址信息,iMPACT按地址把数据一段段写进Flash。整个链路是:bit文件 → 通过iMPACT转换成mcs → 通过JTAG把mcs烧进Flash → FPGA上电后从Flash读取配置。
这套流程在ISE 14.7上依然是最经典的做法,虽然Vivado已经普及,但大量老项目、教学板卡、工业设备还在用ISE,尤其是Spartan-6、Virtex-5这些器件。我手上就有好几块Spartan-6的板子,客户要求必须用ISE维护,所以这套流程我反复走过很多遍,踩过的坑也不少。下面把完整流程和关键细节拆开讲清楚。
2. 生成mcs之前,bit文件必须先过这几关
2.1 bit文件的来源与常见误区
mcs是从bit文件转出来的,所以bit文件本身的质量直接决定固化后能不能正常启动。很多人以为只要ISE综合实现通过了,生成的bit就没问题,其实不然。bit文件生成过程中有几个关键设置,如果没注意,固化后会出现“JTAG下载能跑,Flash启动不跑”的诡异现象。
第一个是配置时钟频率。在ISE的Generate Programming File属性里,有个-g ConfigRate选项,默认是6,对应大约3MHz的配置时钟。如果Flash的读取速度跟不上,或者板子走线较长,这个值偏大就会导致配置失败。我遇到过一块板子,JTAG下载一切正常,固化后死活不启动,后来把ConfigRate从6改成3,问题消失。原因是JTAG下载时时钟由下载器控制,而Flash启动时时钟由FPGA内部振荡器产生,两者路径不同,裕量也不一样。
第二个是未使用引脚的处理。在ISE的Generate Programming File属性里,-g UnusedPin选项控制未使用IO的状态,默认是Pull-down。如果板子上某些未使用引脚外部有上拉,固化启动时可能出现冲突。这个选项要根据实际硬件来定,不能无脑用默认值。
第三个是bit文件的压缩。ISE支持生成压缩bit,能减小mcs体积,但压缩后的配置数据需要FPGA内部解压,会增加启动时间。对于启动时间敏感的应用,建议不压缩。
2.2 确认bit文件与器件型号匹配
这一步听起来废话,但实际中真有人搞错。ISE工程里选的器件型号必须和板子上的FPGA完全一致,包括速度等级和封装。比如xc6slx9-2tqg144和xc6slx9-3tqg144,速度等级不同,bit文件不通用。烧进去之后iMPACT可能不报错,但FPGA启动后行为异常。
确认方法很简单:在ISE里打开工程,看Project Summary里的Device信息,和板子丝印上的型号对一遍。另外,bit文件头部也包含器件ID,iMPACT在加载bit时会校验,如果型号不匹配会直接报错,所以这一步一般不会出大问题,但速度等级这种细节容易被忽略。
2.3 bit文件命名与路径的坑
ISE默认生成的bit文件名和工程顶层模块名一致,路径在工程目录下。这里有个小坑:如果工程路径包含中文或空格,iMPACT加载bit时可能报错。我建议把bit文件复制到一个纯英文、无空格的短路径下,比如D:\fpga\flash,后续操作都在这个目录里做,省得路径问题干扰。
另外,如果你用的是ISE的iMPACT GUI,它默认会去工程目录找bit,但有时候工程重新编译后bit文件被覆盖,iMPACT还引用旧路径,导致烧进去的是旧版本。养成习惯:每次生成mcs前,手动确认bit文件的修改时间,确保是最新编译出来的。
3. iMPACT生成mcs:GUI操作与批处理两条路
3.1 GUI方式:一步步点,但要知道每步在干什么
打开iMPACT,第一件事是新建工程。菜单File → New Project,弹出向导。这里有个选择:是配置单个器件还是多个?对于固化Flash,我们选Configure single FPGA,然后选择SPI Flash或BPI Flash。大多数板子用的是SPI Flash,比如M25P16、W25Q64这类。
接下来iMPACT会让你添加bit文件。选中之前准备好的bit,然后它会自动识别器件型号。这时候注意看iMPACT识别的器件和你的FPGA是否一致,不一致要手动改。
关键步骤来了:右键点击Flash器件,选择Add SPI/BPI Flash,然后选Flash型号。如果列表里没有你的Flash型号,可以选一个容量相同或更大的兼容型号,或者手动输入Flash的ID。我遇到过一块板子用的是国产Flash,iMPACT列表里没有,最后选了M25P16,因为两者指令集兼容,烧进去也能用。
然后就是生成mcs:右键Flash器件,选Generate File,格式选MCS,输出文件名自己定。iMPACT会弹出一个窗口让你设置mcs的起始地址和结束地址。对于SPI Flash,起始地址一般是0,结束地址根据bit文件大小自动填充。这里有个细节:mcs的地址范围要覆盖整个bit文件,且不能超过Flash容量。如果bit文件压缩后是2Mbit,Flash是16Mbit,那结束地址填0x1FFFF就够了,不用填满。
生成完mcs,iMPACT会提示成功。这时候你可以用文本编辑器打开mcs看一眼,开头是:020000040000FA这样的记录,这是Intel HEX的扩展线性地址记录,正常。
3.2 批处理方式:适合反复烧录和自动化
GUI点一次两次还行,如果经常要改代码重新固化,每次都点一遍太累。iMPACT支持批处理模式,用命令行脚本一次搞定。我一般会写一个.cmd文件,内容大致如下:
setMode -bscan setCable -p auto addDevice -p 1 -file "D:\fpga\flash\top.bit" addDevice -p 2 -file "D:\fpga\flash\flash.mcs" program -p 2 -e -v quit这个脚本的逻辑是:先设置边界扫描模式,自动识别下载线,然后添加FPGA器件和Flash器件,最后对Flash执行擦除、编程、校验。-e是擦除,-v是校验,这两个参数建议都加上,擦除保证没有残留数据,校验保证烧进去的数据和mcs一致。
运行方式:在命令行里执行impact -batch flash.cmd。注意iMPACT的路径要加到系统环境变量里,或者用绝对路径调用。ISE 14.7安装后,iMPACT在Xilinx\14.7\ISE_DS\ISE\bin\nt64目录下。
批处理方式还有一个好处:可以集成到Makefile或CI流程里。比如你每次编译完bit,自动调用iMPACT生成mcs并烧录,省去人工操作。不过要注意,批处理模式下如果下载线没插好或Flash型号识别错误,脚本会直接报错退出,不会像GUI那样弹窗提示,所以第一次用建议先用GUI确认硬件连接正常。
3.3 mcs生成后的验证
mcs生成后,别急着烧,先做两件事。第一,检查mcs文件大小。一个未压缩的bit文件,mcs大小大约是bit的2.5倍左右,因为HEX格式每个字节用两个ASCII字符表示,再加上地址和校验。如果mcs大小异常小,可能是生成过程中断了。第二,用iMPACT的Verify功能,把mcs和bit对比一下,确认转换无误。
我个人的习惯是,生成mcs后立刻用impact -batch跑一次校验,确认mcs能被iMPACT正确解析。这一步花不了几秒钟,但能避免烧录到一半发现文件损坏的尴尬。
4. 把mcs烧进Flash:JTAG链路与Flash识别的实战细节
4.1 硬件连接:JTAG链路上的器件顺序
烧录Flash时,JTAG链路上通常有两个器件:FPGA和Flash。但Flash本身没有JTAG接口,它是通过FPGA的边界扫描单元间接访问的。所以iMPACT里看到的器件顺序是:第一个是FPGA,第二个是Flash(逻辑上挂在FPGA后面)。
实际连接时,下载线接板子的JTAG口,板子上的FPGA和Flash之间的SPI连线必须正常。如果SPI连线有问题,iMPACT能识别到Flash,但读写会失败。我遇到过一块板子,Flash的CS信号被外部电路拉死,iMPACT识别Flash正常,但一编程就报failed to communicate with the flash chip。后来查原理图发现CS上有个电阻下拉,导致FPGA无法拉低CS。所以硬件排查时,SPI的四根线(CS、CLK、MOSI、MISO)都要确认。
4.2 iMPACT识别Flash失败的常见原因
iMPACT识别Flash时,会通过FPGA发送Read ID指令,读取Flash的制造商ID和设备ID。如果读不到,就会报cannot load flash device description或failed to communicate with the flash chip。常见原因有这几个:
- Flash型号选错:iMPACT里选的Flash型号和实际板子上的不一致,导致ID不匹配。解决办法是查Flash datasheet,找到Manufacturer ID和Device ID,在iMPACT里手动添加。
- SPI时钟太快:iMPACT默认的SPI时钟可能偏高,某些Flash支持不了。可以在iMPACT的Flash属性里把时钟降下来,比如从10MHz降到1MHz。
- FPGA未配置:烧录Flash前,FPGA必须先配置一个能正常工作的bit,因为iMPACT是通过FPGA的边界扫描逻辑去访问Flash的。如果FPGA里没有配置,或者配置了一个IO状态不对的bit,Flash访问就会失败。所以流程上,先下载一个正常的bit到FPGA,再烧Flash。
- 电源不稳:Flash编程时电流较大,如果板子供电不足,会出现随机失败。用示波器看Flash的VCC,编程瞬间不能有明显跌落。
4.3 烧录过程中的参数设置
在iMPACT里对Flash执行编程时,有几个选项要留意:
- Erase:建议勾选。Flash编程前必须擦除,否则旧数据可能残留。擦除是按扇区进行的,SPI Flash的扇区大小一般是64KB或4KB,擦除整个芯片需要几秒钟。
- Program:把mcs数据写入Flash。
- Verify:烧录后校验,确保数据一致。这个选项强烈建议勾选,虽然多花一点时间,但能保证可靠性。
- Blank Check:擦除后检查是否全空,一般不用勾,因为Erase已经保证了。
烧录时间取决于Flash容量和mcs大小。16Mbit的Flash,烧一个2Mbit的mcs,大约需要10到20秒。如果超过一分钟还没完成,可能是SPI时钟太慢或Flash有问题。
4.4 烧录完成后的启动验证
烧录完成后,iMPACT会提示Program Succeeded。这时候别急着拔下载线,先做启动验证:断开下载线,给板子断电,再上电。观察FPGA的配置指示灯(如果有DONE信号灯),正常情况下DONE灯会在上电后几百毫秒内亮起。如果DONE灯不亮,说明FPGA没有从Flash成功加载配置。
如果DONE灯不亮,排查顺序是:先确认Flash里的数据是否正确(重新用iMPACT读回Flash内容,和mcs对比),再确认FPGA的配置模式引脚(M[2:0])是否设置为SPI模式。Spartan-6的M[2:0]引脚决定启动模式,001是SPI模式,如果设成000就是JTAG模式,上电不会从Flash加载。这个引脚通常在板子上有跳线或电阻配置,查原理图确认。
5. 那些年我踩过的Flash固化坑
5.1 “JTAG能跑,Flash不跑”的典型排查链路
这个现象太常见了,我至少遇到过五六次。排查思路可以按以下顺序走:
第一步,确认bit文件本身没问题。用JTAG下载同一个bit,功能正常,说明bit逻辑没问题。
第二步,确认mcs生成正确。用iMPACT的Verify功能对比mcs和bit,确认转换无误。
第三步,确认Flash烧录成功。用iMPACT的Readback功能,把Flash内容读回来,和mcs对比。如果读回来的数据和mcs不一致,说明烧录过程有问题,可能是Flash型号选错或SPI时钟太快。
第四步,确认FPGA配置模式。查M[2:0]引脚,确认是SPI模式。有些板子用拨码开关选择启动模式,拨错了就启动不了。
第五步,确认ConfigRate。如果前面都正常,但启动仍然失败,把ConfigRate从6降到3或2,重新生成bit和mcs,再烧一次。这个参数对启动成功率影响很大,尤其是老Flash或长走线的情况。
第六步,检查Flash的WP(写保护)引脚。有些板子把WP引脚接地或接VCC,导致Flash被写保护,烧录时看似成功,实际数据没写进去。用万用表量一下WP引脚电平。
5.2 Flash容量与mcs地址范围的匹配问题
mcs文件里包含地址信息,iMPACT烧录时会按地址写入Flash。如果mcs的地址范围超过了Flash的实际容量,烧录会失败或数据被截断。比如Flash是8Mbit(1MB),mcs覆盖了2MB的地址范围,那超出部分写不进去。
反过来,如果mcs的地址范围小于Flash容量,没问题,剩余空间空着就行。但要注意,有些FPGA启动时会从Flash的0地址开始读,如果mcs的起始地址不是0,FPGA可能读不到正确的配置数据。所以生成mcs时,起始地址一定要设为0。
我遇到过一种情况:客户用了一个第三方工具生成mcs,起始地址设成了0x1000,结果FPGA启动时从0地址读,读到的全是0xFF,配置失败。后来用iMPACT重新生成,起始地址设为0,问题解决。
5.3 多配置镜像与Golden Image的注意事项
有些应用需要多个配置镜像,比如Golden Image加Update Image,用于远程升级。这种情况下,mcs的地址分配要提前规划。Golden Image放在0地址,Update Image放在某个偏移地址,FPGA启动时先加载Golden Image,再由逻辑决定是否切换到Update Image。
ISE的iMPACT支持生成多个mcs,但地址分配需要手动设置。我建议在生成mcs之前,先画一张Flash地址映射表,明确每个镜像的起始地址和大小,避免地址重叠。另外,多镜像模式下,ConfigRate和启动时序要留足裕量,因为FPGA可能需要尝试多次加载才能成功。
5.4 批处理脚本中的常见报错与处理
用impact -batch时,常见的报错有:
ERROR:Flash download failed - Target DLL has been cancelled:这个通常是下载线驱动问题或USB连接不稳定。换一个USB口,或者重新安装下载线驱动。WARNING:Failed to communicate with the Flash chip:SPI通信失败,检查Flash型号、SPI时钟、硬件连线。Cannot load flash programming algorithm:iMPACT找不到对应Flash的编程算法,需要手动指定或更新iMPACT的Flash列表。
处理这些报错时,我一般先用GUI模式跑一遍,看具体是哪一步出错,然后再回到批处理模式调整脚本。GUI的报错信息更详细,有助于定位问题。
6. 固化完成之后:上电自启动的验证与长期可靠性
6.1 上电自启动的完整验证流程
烧录完Flash,拔掉下载线,断电,再上电。观察几个信号:
- DONE信号:FPGA配置完成后DONE引脚会拉高。如果板子上有DONE指示灯,灯亮表示配置成功。
- INIT信号:配置开始前INIT会拉低,配置完成后拉高。如果INIT一直低,说明配置过程出错。
- 功能验证:配置成功后,跑一个简单的测试逻辑,比如LED闪烁或串口输出,确认FPGA逻辑正常工作。
如果DONE不亮,先别急着重新烧,用示波器看Flash的CS、CLK、MOSI信号,确认FPGA上电后有没有发起SPI读取。如果没有信号,说明FPGA没有进入SPI启动模式,查M[2:0]引脚。如果有信号但DONE不亮,说明Flash里的数据有问题,重新烧录。
6.2 长期可靠性:Flash数据保持与刷新
Flash存储的数据不是永久的,一般数据保持时间是10到20年,但高温会加速数据丢失。工业级应用里,如果设备长期运行在高温环境,建议定期刷新Flash内容。另外,Flash的擦写次数有限,SPI Flash一般10万次左右,频繁烧录会缩短寿命。
我个人的做法是,在产品出厂前烧录一次,之后除非固件升级,否则不轻易擦写Flash。固件升级时,用iMPACT的擦除加编程流程,不要只编程不擦除,否则旧数据残留可能导致校验失败。
6.3 从ISE到Vivado:固化流程的异同
虽然这篇讲的是ISE,但很多人也在用Vivado。Vivado里生成mcs的流程类似,但工具换成了write_cfgmem命令,烧录用program_hw_cfgmem。Vivado的mcs生成更自动化,地址分配和Flash型号识别也更智能。不过底层原理一样:bit转mcs,JTAG烧Flash,上电自启动。
如果你同时维护ISE和Vivado项目,建议把mcs生成和烧录的脚本统一管理,比如用Makefile封装,避免两套流程混淆。我在实际项目中,ISE和Vivado的烧录脚本放在同一个目录下,用不同的目标名区分,比如make flash_ise和make flash_vivado,这样切换项目时不容易搞错。
最后再分享一个小技巧:烧录Flash前,先用iMPACT的Read ID功能确认Flash能被正确识别。这一步只要几秒钟,但能提前发现硬件连接问题,避免烧到一半失败。我现在的习惯是,每次烧录前都先Read ID,确认Flash在线,再执行擦除和编程。这个习惯帮我省了不少返工时间。