☰
FPGA远程升级实战:STARTUPE2原语与SPI NOR Flash MultiBoot避坑指南
2026/10/7 1:22:57 网站建设 项目流程

FPGA做远程升级,尤其是走SPI NOR Flash + MultiBoot这套方案的工程师,对STARTUPE2原语应该都不陌生。这个原语说简单也简单——例化一下、把Flash控制器的时钟接到USRCCLKO就完事;但说复杂也真复杂——管脚时序、三态控制、地址对齐、回退机制,任何一个环节没弄对,轻则升级后无法启动,重则整板变砖只能拆机JTAG抢救。我最初接触远程升级时,也是照着参考设计把STARTUPE2例化进去,结果被一系列“看起来没问题但就是跑不起来”的故障折磨了很久。这篇博文把我从方案选型、代码集成到实板验证踩过的坑完整梳理一遍,打算做FPGA远程升级、或者正在被Flash控制时序搞到头大的工程师,可以参考一下。

1. 远程升级链路拆解:STARTUPE2到底卡在哪个环节

1.1 一条典型远程升级链路的完整走向

先捋清楚远程升级的整体流程,后面所有问题都出在这条链路的某个节点上。一个基于7系列FPGA的典型方案大概是这样:上位机通过网络或串口把升级镜像分包下发,FPGA内部跑着一个软核(MicroBlaze)或者状态机,接收数据后先写入DDR缓存,再通过SPI控制器把镜像搬运到板载SPI NOR Flash的某个地址区域。整个写入过程往往会包含擦除、编程、回读校验三个阶段,只有校验通过才算升级成功。写完镜像之后,FPGA再通过ICAPE2原语写WBSTAR寄存器,把下次启动的地址指到新镜像的起始位置,然后发送IPROG命令触发内部重配置。此时配置引擎重新开始工作,如果新镜像加载成功,远程升级就算闭环了。

问题就出在“搬运镜像到Flash”这个环节。正常情况下,FPGA配置完成进入用户模式后,CCLK引脚就不再由配置引擎驱动,而是释放出来。但SPI Flash的时钟输入恰恰就接在CCLK这根专用引脚上。用户逻辑要用SPI控制器去驱动Flash,就必须在用户模式下重新获得CCLK引脚的控制权。这个控制权不是普通RTL可以直接去拨的,必须通过STARTUPE2原语提供的专用内部路径来接管。这就是整个方案里最核心、也最容易出问题的地方。

1.2 CCLK引脚的“双重身份”和配置时钟的开销

CCLK这根引脚在FPGA的不同生命周期里扮演完全不同的角色。上电配置阶段,它是配置引擎的输出时钟,用来同步从Flash读取配置数据;配置完成后,它变成普通IO可以复用的资源。对于远程升级这种需要“用户程序自己烧自己”的场景,CCLK必须被复用为Flash控制器的时钟输出,否则Flash就没有时钟,擦除和写入根本无从谈起。

但这里有一个很多人没意识到的细节:STARTUPE2原语接管的是CCLK引脚的“输出三态控制”和“时钟源选择”这两件事,而不是简简单单把一根信号引进来。原语内部有两套路径——USRCCLKO是用户时钟输出,USRCCLKTS是三态控制。只有USRCCLKTS处于低电平(使能输出)时,FPGA才会把USRCCLKO上的时钟真正送到CCLK引脚上;如果USRCCLKTS拉高,CCLK引脚就变成高阻。这个机制和普通GPIO的OE信号几乎一样,区别只在于它作用在专用配置引脚上。

这一设计带来的直接后果就是:Flash控制器不仅要输出时钟,还要输出一个和时钟同步的“使能”信号,这个信号必须严格跟着SPI总线的空闲状态走。SPI控制器在擦除、写配置寄存器这种长时间不产生时钟的操作期间,必须把时钟线拉成高阻,否则外部可能有其他驱动源和它打架。很多人的事故就出在这——直接把USRCCLKTS接地,让CCLK一直被FPGA内部驱动,结果在擦除期间Flash的时钟线上出现了不确定电平,后续写入数据全部错乱。

1.3 用普通IO模拟SPI和走STARTUPE2的取舍关系

也有一部分设计图省事,干脆不碰CCLK引脚,而是把SPI Flash的时钟接到普通IO引脚上,完全绕开STARTUPE2。这种方案在原理上完全可行,Flash控制器跑在普通IO上,时序没有任何特殊限制,调试反而更方便。但代价是必须牺牲一根额外的IO,而且这根IO在配置阶段必须保持高阻,不能影响到Flash。很多板级设计为了省引脚,把Flash的时钟焊在CCLK上,这时候STARUPE2就不是可选项,而是必选项了。

还有个方案是使用AXI Quad SPI IP,它在Xilinx的参考设计里已经预留了原语对接接口。这个IP会输出spi_clk_out和spi_clk_tri两个信号,分别接到USRCCLKO和USRCCLKTS即可。但IP默认生成的顶层不会帮你例化STARTUPE2,需要手动补上。如果你用的是老工程,还要注意7系列用STARTUPE2、UltraScale系列用STARTUPE3,Spartan-6则是STARTUP_SPARTAN6,三者的管脚名和时序略有差异,不能直接照搬。

2. STARTUPE2逐脚位拆解:哪些必须接、哪些可以悬空

2.1 输出类管脚:USRCCLKO、USRDONEO、EOS和时钟反馈

STARTUPE2的管脚数量不多,但每个都有特定含义,接错或者漏接往往会在实板上以非常隐蔽的方式暴露出来,下面先把输出类管脚逐一说明。

USRCCLKO是用户时钟输出,也就是Flash控制器时钟要接入的引脚。它最终会驱动到CCLK引脚上,前提是USRCCLKTS要使能。这个管脚最核心,Flash控制器的spi_clk_out必须和它直接相连。注意USRCCLKO是一个真实的时钟输出路径,不是普通逻辑信号,建议在约束文件里把它声明为生成时钟,否则时序分析可能对它不关心,导致实板时序违例。

USRDONEO把FPGA的DONE状态输出到用户逻辑,一般接到一个寄存器里做状态监控,或者不需要用途。EOS是End Of Startup信号,配置流程结束后会拉高,这个信号对远程升级很有用——它表示FPGA已经稳定进入用户模式,可以开始执行Flash写入流程了。PREQ是PROG请求信号,当外部PROG_B引脚被拉低时,它会输出有效电平,某个设计里我用它做外部复位看门狗的状态判断。CFGCLK和CFGMCLK是配置引擎的时钟输出,前者是配置逻辑使用的时钟,后者是分频后的版本,一般不用接,悬空即可。

2.2 输入类管脚:USRCCLKTS、USRDONETS、GSR、GTS、KEYCLEARB、PACK

输入管脚的关键性不亚于输出管脚。USRCCLKTS是三态控制信号,高电平让CCLK引脚变成高阻,低电平让FPGA把USRCCLKO上的时钟驱动出去。这个信号就是AXI Quad SPI的spi_clk_tri,直接对接即可,但不能悬空,很多FPGA内部逻辑对悬空输入的处理并不保证是确定的0或1,一旦悬空就可能出现CCLK引脚上的毛刺。

USRDONETS是DONE引脚的三态控制,用途比较特殊。它和USRDONEO配合,允许用户逻辑接管DONE引脚的控制权。远程升级过程中,如果配置失败,DONE引脚会被配置引擎拉低,外部复位电路可能因此触发系统复位,导致升级流程被打断。让USRDONEO输出逻辑1、USRDONETS拉低,可以在用户模式下强制维持DONE引脚为高,避免误触发外部复位。这里要特别提醒:控制权在配置失败时会被配置引擎收回,这个方案只能解决用户模式期间的DONE状态问题,不能指望它掩盖真正的配置失败。

GSR通常接0,GTS也接0,这两个信号是全局复位和全局三态,正常用户逻辑不需要操作。KEYCLEARB接1,表示不去清除安全密钥。PACK是PROG请求应答信号,一般接0即可。CLK这个输入在文档里标注为“用户时钟输入”,很多设计直接接0,它主要用于某些需要同步的配置场景,我们在普通Flash控制链路里用不到。

2.3 从管脚连接反推的常见错误模式

梳理完管脚,常见的错误模式其实已经能看出来了。第一类是USRCCLKTS悬空或者直接接地,导致CCLK引脚一直处于内部驱动状态,SPI总线上多个驱动源冲突;第二类是USRCCLKO接错成系统时钟,而不是Flash控制器的spi_clk_out,结果Flash的时钟和SPI控制器的逻辑时钟不同源,出现偶发写入错误;第三类是USRDONETS/USRDONEO完全悬空,实测中会导致DONE引脚在IPROG重配置期间出现毛刺,进而触发外部逻辑误动作。

另有一个容易忽略的点:STARTUPE2和普通逻辑不同,它内部的路径不是普通LUT到IO的组合逻辑,而是经过配置IO专用路径的。因此对USRCCLKO这个时钟路径,最好加上明确的时序约束,比如声明为生成时钟,并和对应的Flash控制逻辑做跨时钟域约束。很多工程师在设计中完全不对STARTUPE2做约束,纯粹依赖布局布线“自然收敛”,这在低速SPI时钟下问题不大,但一旦把SPI时钟提到50MHz以上,偶发失败的随机性会非常头疼。我在实际项目里把SPI时钟从40MHz提到80MHz时,升级失败率从0直接升到百分之几,后来在约束里显式创建时钟并加了set_clock_groups才稳定下来。

3. 实操:AXI Quad SPI + STARTUPE2 + MultiBoot的完整接线

3.1 Vivado中AXI Quad SPI的配置细节

AXI Quad SPI这个IP大家都不陌生,但远程升级场景里的配置和普通调试完全不同。在Vivado中创建IP时,首先要把SPI模式选成Standard或者Quad,这取决于你的Flash是x1还是x4。若使用Quad模式,性能更好,但片选信号虽然只有一个,数据线却有四条,对PCB走线和IO约束的要求更高。更关键的是,IP的Options里有一项叫“Enable STARTUPE2 primitive”,Vivado会自动在IP内部例化STARTUPE2,把spi_clk_out和spi_clk_tri接到原语上。如果你用的是旧版本Vivado或者IP没有这个选项,就得自己在顶层手动例化。

IP时钟频率设置方面,AXI Quad SPI的时钟源通常来自AXI总线的时钟,经过内部分频得到SPI时钟。这个分频系数要仔细算,最稳妥的办法是让SPI时钟不超过25MHz。Flash擦除写入本身对频率不敏感,真正敏感的是IPROG之后配置引擎重新读取镜像的时钟,那个频率是在bitstream属性里设置的。两个频率不一致并不冲突,但如果你把IP时钟拉到80MHz以上,就要做好约束和实测验证。

3.2 顶层例化的代码参考与信号对应关系

下面是一段典型的顶层例化代码,对应AXI Quad SPI输出信号到STARTUPE2的连接关系:

STARTUPE2 #( .PROG_USR ("FALSE"), .SIM_CCLK_FREQ (0.0) ) startup_e2_inst ( .CFGCLK (), .CFGMCLK (), .EOS (), .PREQ (), .CLK (1'b0), .GSR (1'b0), .GTS (1'b0), .KEYCLEARB (1'b1), .PACK (1'b0), .USRCCLKO (axi_quad_spi_inst.spi_clk_out), .USRCCLKTS (axi_quad_spi_inst.spi_clk_tri), .USRDONEO (done_ctrl), .USRDONETS (done_ctrl_ts) );

这段代码里关键是把spi_clk_tri直接给到USRCCLKTS。从逻辑上说,spi_clk_tri在SPI控制器不输出时钟时是高电平,对应CCLK引脚高阻,完全符合预期。USRDONEO和USRDONETS的控制逻辑不是本文主角,但一般建议把USRDONEO接到逻辑1,USRDONETS接到逻辑0,让DONE引脚在用户模式下被内部逻辑稳定驱动。

还要注意,CS、MOSI、MISO这几个Flash控制信号走的是普通IO路径,与STARTUPE2无关。如果这块电路在设计时复用了一些配置引脚,比如D00-D03等,就要额外处理这些引脚在配置阶段和用户阶段的方向切换。Xilinx 7系列的专用配置引脚在用户模式下可以被当作普通IO,但需要在约束或者原语层面明确其方向,否则默认行为未必是你想要的。

3.3 MultiBoot地址规划和WBSTAR设置细节

MultiBoot的基础就是Flash里放两个镜像,一个是工厂出厂固化的Golden镜像,一个是用于升级的Update镜像。Golden放在Flash最低地址0x0,Update放在另一个高位地址,比如1MB偏移或者2MB偏移。上电时配置引擎默认从0x0地址读取Golden;远程升级流程写完Update镜像后,通过ICAPE2写WBSTAR把下次启动地址改为Update地址,再发IPROG触发重配置。

WBSTAR的设置有一个非常容易踩的细节:地址对齐。7系列SPI x1模式下,WBSTAR的值就是Flash字节地址;但SPI x4模式要求地址对齐到4字节或者更高(取决于配置引擎内部的FIFO粒度),如果你把Update镜像放到了非对齐的中断位置,配置引擎会去错误的位置解析镜像头,导致加载失败后自动回退。更稳妥的做法是直接把Update镜像放在1MB、2MB这样的大边界上,并且两侧各留出64KB以上的冗余空间,方便后续镜像变大或者增加参数区。

在Vivado里,Golden镜像的bitstream属性里需要明确设置回退地址,这个属性通常叫next_config_addr。当配置引擎从Update地址加载失败时,它会自动返回到这个地址重新加载,也就是Golden所在位置。务必要确认这个回退地址和实际Golden镜像的存放地址一致,否则失败后无法自动恢复,只能手动JTAG处理。

3.4 ICAPE2命令序列的发送逻辑

远程升级的最后一步是用ICAPE2发送重配置命令,很多示例代码会直接在状态机里写出一长串ICAPE2的写序列。这里给一段伪代码说明核心步骤:

// 1. 发送同步字 icape2_cmd(32'hAA995566); // 2. 写WBSTAR寄存器,地址为Update镜像起始地址 icape2_cmd({8'h30, update_addr[23:0]}); // 3. 发送IPROG命令 icape2_cmd(32'h00000008); // 4. 发送NOOP,等待配置引擎启动 icape2_cmd(32'h20000000);

这里有个容易被忽略的点:ICAPE2的数据输出相对于命令的时序,需要根据数据手册要求做对齐。比如有些系列要求IPROG命令后必须追加至少一条NOOP命令,有些则要求在写WBSTAR之前先写同步字。不同器件系列的指令集存在差异,使用前一定要查对应数据手册。实际调试时我习惯在ICAPE2输出后面挂一个ILA core,把发送的每一拍命令抓出来,确认WBSTAR的地址值没有被高低字节反转。曾经有次升级失败,查了半天发现是ICAPE2数据总线的字节序配置错了,WBSTAR里写入的地址从0x100000变成了0x001000,配置引擎直接跑到错误地址读镜像。

4. 避坑实录:从偶发失败到稳定运行的完整排查链路

4.1 坑一:USRCCLKTS悬空导致升级完成后重启卡死

这个坑出现在第一次做远程升级打样的阶段。当时参照参考设计把STARTUPE2例化好,USRCCLKO接了spi_clk_out,USRCCLKTS没接,直接悬空。在线调试模式下一切正常,Flash的擦写和校验都没问题,但一旦断电重启,FPGA直接卡死在配置阶段。上电后DONE一直为低,INIT_B也被拉低,整个系统起不来。

排查过程很曲折。先怀疑Flash里的镜像被写坏了,于是重新通过JTAG烧写原始bitstream,因为JTAG烧Flash本身依赖配置引擎逻辑,结果JTAG烧写也报错——这算是个重要线索。后来把整个工程回退到不使用STARTUPE2的版本,JTAG烧写恢复正常,说明问题出在原语本身。

进一步阅读UG470才发现,USRCCLKTS这个信号在文档里被标注为“3-state control for CCLK”,如果悬空不接入任何驱动,内部逻辑读到的电平是不确定的。在多个启动周期的实测中,它有时候为高、有时候为低,导致CCLK引脚时而高阻、时而驱动出莫名其妙的分频时钟。当配置引擎在启动阶段需要读取Flash时,CCLK引脚被用户逻辑残留的高阻或乱时钟干扰,配置当然失败。解决办法极其简单:把spi_clk_tri信号显式接到USRCCLKTS,不能悬空。这个教训算是远程升级最典型的第一课。

4.2 坑二:WBSTAR地址错位导致每次跳转都回退Golden

另一个让人抓狂的问题是:Update镜像明明已经烧写成功,IPROG也发出去了,但FPGA每次都会回到Golden启动。从现象上看,升级过程“成功”完成,但Update不起作用。

一开始我以为是ICAPE2命令序列写错了,于是用ILA抓ICAPE2管脚上的数据流。从波形上看,同步字AA995566正确,WBSTAR寄存器写入的数据看起来也正确——0x100000,IPROG命令也发出去了。问题出在哪儿?后来我抱着试试看的心态把Flash内容回读出来,发现一个诡异的事实:Flash里0x100000地址处的内容确实是我们写入的Update bitstream,但bitstream头部的前面有256字节的偏移,相当于镜像没有放在预期的块边界上。

再查写Flash的流程,发现是AXI Quad SPI在写入时开启了“Write Offset”选项,这个选项在配置IP时默认值并不是0,而是根据页面大小做了一层对齐。也就是说,实际写入Flash的起始地址比WBSTAR里填的地址偏移了一点。在普通图像写入测试中,Flash内容全部正确,CRC校验也能过,唯独MultiBoot跳转时配置引擎从0x100000开始读,但真正的bitstream起始位置在0x100100,头部错位导致加载失败,配置引擎自动触发fallback回到Golden。

这个问题提醒我:写入Flash时一定要确认偏移量参数,要么关闭Write Offset,要么把WBSTAR填成实际的物理地址,两者必须完全对应。之后我在升级流程里增加了“升级前对目标地址回读256字节头验证”的步骤,专门检查0xAA 0x99这种同步图案是否在预期位置,避免此类问题再犯。

4.3 坑三:JTAG烧写Flash报Target DLL has been cancelled

还有一种高频错误发生在远程升级链路之外的调试环节:用Vivado Hardware Manager通过JTAG直接烧写SPI Flash时,经常报“Flash Download Failed - Target DLL has been cancelled”。这个错误常常让人误判为下载器驱动问题,换USB线、换JTAG cable后依然存在。

实际排查发现,当FPGA当前加载的bitstream中使用了STARTUPE2原语,并且USRCCLKO上一直有时钟输出时,JTAG烧Flash流程会尝试同时控制CCLK引脚,二者形成竞争关系。Vivado的Flash烧写过程本质上是通过JTAG间接操作配置引擎,再用CCLK去驱动Flash,这时候用户逻辑里的STARTUPE2如果还占着CCLK,就会导致时序冲突。

解决方案也很直接:在烧Flash之前,先通过JTAG加载一个没有例化STARTUPE2的“空bitstream”,这个bitstream只需要把目标IO全部设成高阻或保持不活动状态,然后立刻执行Flash烧写。烧写完成后,再加载正式镜像或者直接断电重启。类似的思路也适用于升级包制作:如果升级包写Flash的过程和JTAG调试冲突,就先让系统进入一个“bootloader模式”,这个模式下不启用STARTUPE2对CCLK的接管。

4.4 坑四:压缩bitstream在MultiBoot中反复失败

为了减小升级包体积,我在Vivado里勾选了“Compress bitstream”选项。本地用JTAG烧写这套压缩流运行是正常的,但一旦走远程升级链路,IPROG跳转到Update镜像就大概率失败,或者启动后功能异常。这个问题的隐蔽性在于:压缩bitstream在配置引擎解析时,必须以某种自描述格式定位到镜像内部的数据块,而MultiBoot跳转时WBSTAR指定的地址是镜像起始位置,并不能直接映射到有效的压缩块地址。

经过多次测试,我发现在7系列上,压缩bitstream可以用于Flash启动,但前提是必须放在固定块边界,且Flash控制器的擦除粒度要匹配。如果在升级流程中把整个分区按页擦除、写入时又按非对齐偏移,压缩流在重建配置时就会出现CRC错误。最终我放弃了对远程升级镜像使用Compress选项,保留Golden镜像也不压缩,只对功能侧非关键镜像做压缩。这个取舍意味着升级包体积会大一些,但稳定性明显提升,在量产阶段这个稳定性远比体积更值钱。

4.5 坑五:Flash写保护引脚和HOLD引脚在板级引起的写入成功但校验失败

还有一个几乎让人怀疑人生的问题:升级流程提示写入成功,但紧接着的校验环节却报数据不匹配。直接读Flash的ID和状态寄存器都是正常的,而手动用JTAG烧写同一片Flash没有任何问题。

后来检查原理图,发现板子上Flash的WP引脚没有接上拉电阻,而是直接连到FPGA一个普通IO上。FPGA上电后,这个IO默认输出状态是三态(高阻),WP引脚电平悬空,有些Flash芯片会把悬空识别为写保护有效,导致页面写入阶段正常、实际Flash内容没有被编程。另一个类似的坑是HOLD引脚,当HOLD拉低时,Flash对时钟和数据线置为高阻,不执行任何新的操作,写流程的中段数据就会被丢弃。

最终的对策很简单:把WP和HOLD引脚在原理图上接10k欧姆上拉到VCC,同时软件层面在每次操作Flash前先读状态寄存器,确认非写保护状态,再执行擦除和写入。这个经验也提醒我,远程升级方案的软硬件设计必须放在一起看,不能只盯着FPGA逻辑。

5. 验证策略:仿真、实板、故障注入三板斧

5.1 仿真阶段需要验什么

工程上大家普遍对STARTUPE2的仿真不太重视,但仿真反而是最快发现接线错误的途径。Xilinx在仿真库中提供了STARTUPE2的行为模型,例化后可以观察到CFGCLK和EOS等信号的变化。在仿真里至少要做三件事:第一,确认USRCCLKO上出现了有效的SPI时钟,在SPI控制器不工作时USRCCLKTS拉高、CCLK输出被禁止;第二,确认ICAPE2的命令序列能够在仿真模型中正确执行,WBSTAR的地址值能按预期写入到内部寄存器;第三,模拟一次配置失败场景,用ILA观测配置引擎拉低INIT_B后是否自动跳转到回退地址。

仿真虽然不能覆盖所有时序问题,但能过滤掉大量连线错误和地址位序错误。举个例子,我曾在仿真里发现WBSTAR地址的低字节被交换,正是因为在行为模型观测到了错误的寄存器写值,这在实板调试中可能要花掉两三天。

5.2 实板验证的关键观测点

实板验证时,第一优先抓的就是CCLK引脚上的波形和ICAPE2管脚的命令时序。用示波器观察CCLK的波形,确认在SPI控制器运行时,CCLK上有时钟输出,在控制器释放总线时,CCLK变为高阻浮空。如果CCLK一直在输出时钟,说明USRCCLKTS被错误拉低;如果CCLK从来没有时钟,说明USRCCLKO的时钟路径没建立起来。

其次抓Flash的CS信号,观察升级流程中CS的有效时序,确保擦除和写入阶段片选逻辑正确。再抓Flash的MISO回读数据,在Megafunction读写测试阶段,可以通过GPIO引出Flash的MISO,在逻辑分析仪上观察回读数据和写入数据是否一致。这个步骤能快速区分问题是Flash芯片本身、PCB布线还是FPGA逻辑。

关于ICAPE2管脚,尽量在综合后保留ILA核,专门监控ICAPE2的命令序列输出。很多远程升级失败的原因都能在ILA中直接看出——WBSTAR地址错误、IPROG命令缺失、NOOP数量不对等。这些在文本日志里可能全无记录,但在波形里一目了然。

5.3 故障注入和循环可靠性测试

远程升级系统的可靠性,很大程度上取决于故障注入测试的覆盖度。我的做法是准备三套测试用例:第一套是把Update镜像中若干个字节故意改错,然后触发IPROG,观察系统是否能够自动回到Golden镜像,并且记录回退所需的时间;第二套是擦除Update区域的过程中随机下电,重新上电后检查系统是否能正常从Golden启动;第三套是连续升级200次,每次升级后断电重启再进入下一轮升级,统计每一轮的时间戳和CRC校验结果。

在前两轮测试中,我通常用脚本自动控制电源和网络接口,把升级结果记录到内存或SD卡。有一套实板在擦除过程中随机掉电了两个月都没有出现一次启动失败,这就能给人很大信心。相反,如果只做一次完整升级测试就认为“成功”,后续批量产线上的偶发问题大概率会让你怀疑人生。

故障注入测试还要覆盖一种特殊场景:Update镜像版本号和Golden镜像版本号相同的情况。如果升级流程没有强制校验版本号,可能用户明明烧了新版镜像,但因为版本相同,系统误判为不用升级,导致现场设备永远停留在旧版本。这种问题不是硬件故障,但在远程升级可靠性测试中经常被忽略。

5.4 关于FPGA配置失败时的DONE信号行为

前面提过USRDONEO和USRDONETS用于控制DONE引脚,但实板验证时会发现一个关键现象:无论你在用户逻辑里怎么强制DONE为高,一旦配置引擎在IPROG之后发现自己加载的镜像有问题,它会把DONE拉低。这时候外部复位电路可能已经开始动作,整个系统进入重启流程。

工程师在设计外部复位电路时,应该意识到DONE引脚在配置失败时是可靠的低电平,完全可以把它当成一个“配置完成”的状态信号。但如果外部逻辑里用DONE上升沿做系统解锁,就要小心IPROG重配置期间的DONE毛刺。我通常会在外部逻辑里对DONE信号做一个至少几十微秒的防抖,避免误触发。

6. 扩展思考:NAND Flash、OBUF/IBUFG和其他平台方案的关联

6.1 为什么配置引擎原生只能直接驱动SPI NOR Flash

很多工程师疑惑:既然远程升级要存镜像,为什么不用成本更低、容量更大的NAND Flash?答案在于FPGA配置引擎的启动协议天然面向NOR型Flash——SPI NOR支持按字节寻址、支持XIP执行,配置引擎可以直接根据指令把配置数据流顺序读出。而NAND Flash有坏块、需要ECC纠错、按页读写且不能直接映射到线性地址空间,配置引擎在启动阶段根本没有能力处理这些复杂逻辑。因此,使用NAND Flash做远程升级的板卡,通常在第一个阶段加载一个很小的SPI NOR镜像,这个镜像里的软核再通过专用的NAND控制器读取主镜像到DDR,最后用ICAPE2或者SelectMAP机制把数据写入配置逻辑。整个过程比本文描述的STARTUPE2方案复杂得多,但本质思路相同——用一个可启动的“基础镜像”去引导更大镜像。

6.2 OBUF和IBUFG在Flash控制链路中的实际角色

热搜词里同时出现了OBUF和IBUFG,这两个原语和STARTUPE2之间的关系有必要澄清一下。OBUF是普通输出缓冲,STARTUPE2内部已经包含了对CCLK引脚的专用输出驱动,这个路径并不需要额外例化OBUF。但在Flash数据引脚(如MOSI、MISO、CS)上,如果你的设计里这些引脚同时被配置为配置引脚和用户IO,就会涉及到方向切换,这时IOBUF原语才是主角,OBUF反而用得不多。IBUFG则用于把外部时钟引入全局时钟网络,典型场景是Flash控制器自己使用一个外部有源晶振提供的时钟,通过IBUFG+BUFG把这个时钟接进逻辑。需要注意的是,这类外部时钟和STARTUPE2的USRCCLKO之间会存在相位偏差,如果跨时钟域处理不当,可能在写Flash时出现偶发数据错误。两种情况在工程中都遇到过,放在一起说的意思是:对应原语各自有明确的位置,不要在不该用的时候硬套。

6.3 Altera、高云等平台的远程升级原语对比

做完Xilinx的方案后,如果你在别的平台上也做过远程升级,会对“原语只是工具,思路才是核心”这句话有更深体会。Altera(Intel)的MAX10等器件有Remote System Update IP核,它内部封装了CFM的访问逻辑,不再像Xilinx这样让你手动例化STARTUPE2,而是通过IP配置界面选择双镜像还是三镜像,使用起来更傻瓜,但灵活性反而低一点。高云FPGA在GW1N系列中也有类似的内部Flash控制原语,名字叫GowinConfigurationIP,需要配合相关的用户Flash读写操作。虽然叫法不同,但做的事情完全一样——把用户逻辑对配置Flash的访问权限和配置引擎的启动控制做一个安全交接。

对比这些平台的共同点,可以发现它们都要求:写Flash之前必须搞清楚自己有没有完整控制Flash时钟总线的权限,切换启动地址时有没有对齐,失败后有没有可靠的fallback机制。只要这三件事想清楚,换任何平台都能顺利落地。

6.4 最后给一个小建议

如果现在有人问我远程升级系统怎么验收,我会说:不要只看“升级成功率100%”,要看“升级失败后能不能自动恢复”。系统设计真正值钱的不是把新镜像烧进去,而是任何异常情况下都能回到一个可靠的Golden状态。所以我现在的每个远程升级工程都会加一个硬件的“强制回退按钮”,也就是把一个GPIO接成按钮,长按后清除WBSTAR或者触发IPROG回到0x0地址。这个按钮可以不用放在产品外壳上,但调试阶段和产线阶段几乎一定会用到。留一个物理回退手段,比在软件里做一百个判断都踏实。

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

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

立即咨询