ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA远程升级实战:STARTUPE2原语与SPI NOR Flash MultiBoot避坑指南

FPGA远程升级实战:STARTUPE2原语与SPI NOR Flash MultiBoot避坑指南 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系列用STARTUPE3Spartan-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通常接0GTS也接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 (1b0), .GSR (1b0), .GTS (1b0), .KEYCLEARB (1b1), .PACK (1b0), .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接到逻辑1USRDONETS接到逻辑0让DONE引脚在用户模式下被内部逻辑稳定驱动。还要注意CS、MOSI、MISO这几个Flash控制信号走的是普通IO路径与STARTUPE2无关。如果这块电路在设计时复用了一些配置引脚比如D00-D03等就要额外处理这些引脚在配置阶段和用户阶段的方向切换。Xilinx 7系列的专用配置引脚在用户模式下可以被当作普通IO但需要在约束或者原语层面明确其方向否则默认行为未必是你想要的。3.3 MultiBoot地址规划和WBSTAR设置细节MultiBoot的基础就是Flash里放两个镜像一个是工厂出厂固化的Golden镜像一个是用于升级的Update镜像。Golden放在Flash最低地址0x0Update放在另一个高位地址比如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(32hAA995566); // 2. 写WBSTAR寄存器地址为Update镜像起始地址 icape2_cmd({8h30, update_addr[23:0]}); // 3. 发送IPROG命令 icape2_cmd(32h00000008); // 4. 发送NOOP等待配置引擎启动 icape2_cmd(32h20000000);这里有个容易被忽略的点ICAPE2的数据输出相对于命令的时序需要根据数据手册要求做对齐。比如有些系列要求IPROG命令后必须追加至少一条NOOP命令有些则要求在写WBSTAR之前先写同步字。不同器件系列的指令集存在差异使用前一定要查对应数据手册。实际调试时我习惯在ICAPE2输出后面挂一个ILA core把发送的每一拍命令抓出来确认WBSTAR的地址值没有被高低字节反转。曾经有次升级失败查了半天发现是ICAPE2数据总线的字节序配置错了WBSTAR里写入的地址从0x100000变成了0x001000配置引擎直接跑到错误地址读镜像。4. 避坑实录从偶发失败到稳定运行的完整排查链路4.1 坑一USRCCLKTS悬空导致升级完成后重启卡死这个坑出现在第一次做远程升级打样的阶段。当时参照参考设计把STARTUPE2例化好USRCCLKO接了spi_clk_outUSRCCLKTS没接直接悬空。在线调试模式下一切正常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寄存器写入的数据看起来也正确——0x100000IPROG命令也发出去了。问题出在哪儿后来我抱着试试看的心态把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控制器自己使用一个外部有源晶振提供的时钟通过IBUFGBUFG把这个时钟接进逻辑。需要注意的是这类外部时钟和STARTUPE2的USRCCLKO之间会存在相位偏差如果跨时钟域处理不当可能在写Flash时出现偶发数据错误。两种情况在工程中都遇到过放在一起说的意思是对应原语各自有明确的位置不要在不该用的时候硬套。6.3 Altera、高云等平台的远程升级原语对比做完Xilinx的方案后如果你在别的平台上也做过远程升级会对“原语只是工具思路才是核心”这句话有更深体会。AlteraIntel的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地址。这个按钮可以不用放在产品外壳上但调试阶段和产线阶段几乎一定会用到。留一个物理回退手段比在软件里做一百个判断都踏实。
返回列表