☰
FPGA远程升级关键:STARTUPE2原语控制SPI Flash的完整指南
2026/10/2 7:48:01 网站建设 项目流程

1. 为什么远程升级总会卡在Flash这一环

做FPGA远程升级的朋友应该都有过这种经历:功能仿真全对,板级测试也正常,但一到现场升级就翻车,有的板子升级失败后直接变砖,有的板子能升级但重启后固件起不来,还有的板子干脆下载Flash时报错Flash download failed - target dll has been cancelled。排查到最后,问题往往出在一个很容易被忽视的原语上——STARTUPE2。

先说清楚事情的本质。FPGA上电后,首先要从外部SPI Flash读取配置比特流,这个过程由FPGA内部的专用配置逻辑控制,外部Flash挂在FPGA的专用配置引脚上。一旦配置完成,FPGA进入用户模式,这些专用引脚大多会被释放,但Flash的时钟引脚CCLK和片选引脚FCS_B并不会自动交还给用户逻辑使用。换句话说,当你想在用户模式下通过逻辑代码读写SPI Flash时,默认情况下你根本控制不了这些引脚上的信号。这时候就需要STARTUPE2原语登场,它就像一把钥匙,把FPGA内部用户逻辑产生的时钟和使能信号,重新引导到CCLK和FCS_B这两个外部引脚上。

这篇文章主要面向正在做FPGA远程升级、需要实现用户模式读写SPI Flash的工程师,也适合那些在调试中遇到Flash控制异常、下载失败问题的朋友。我会把STARTUPE2的引脚定义、例化方法、约束写法、常见坑点一次讲清楚,并且给出可以直接抄走的Verilog模板。

2. STARTUPE2原语到底做了什么

2.1 从FPGA启动流程看引脚归属

FPGA的启动配置流程可以分为三个阶段:上电复位、配置阶段、用户模式。在配置阶段,FPGA根据配置模式引脚的电平设置,决定从SPI Flash、BPI Flash还是JTAG读取比特流。这个阶段里,FPGA的专用配置引脚完全由内部配置逻辑控制,外部器件没有干预的余地。

配置完成后,FPGA会释放大部分配置引脚,但CCLK和FCS_B是个例外。以7系列FPGA为例,在SPI x1模式中,FPGA通过CCLK输出时钟、FCS_B输出片选、D0输入数据。配置完成后,CCLK引脚进入高阻态或者保持配置时钟状态,FCS_B引脚被释放,但这并不意味着用户逻辑可以直接把这些引脚当普通IO来用。在Xilinx的引脚定义中,CCLK和FCS_B属于专用配置引脚,它们在用户模式下仍然归属于配置逻辑,普通IO的输入输出缓冲器无法直接驱动它们。

这就是STARTUPE2存在的原因。它的核心功能是把用户逻辑内部产生的时钟信号通过USRCCLKO端口引到CCLK引脚上,同时把用户逻辑产生的片选信号通过USRCCLKO的配套使能信号USRCCLKT引到FCS_B引脚上。严格来说,STARTUPE2的USRCCLKO和USRCCLKT是成对使用的,USRCCLKO提供时钟和数据通道,USRCCLKT控制时钟输出使能。

2.2 STARTUPE2引脚功能详解

Xilinx 7系列FPGA中,STARTUPE2原语的端口定义如下表所示:

端口名方向功能说明
CFGCLK输出配置时钟输出,反映配置阶段使用的时钟频率,用户模式下也可使用
CFGMCLK输出配置主时钟输出,通常是CFGCLK的分频版本
EOS输出End Of Startup,启动完成信号,高电平表示FPGA已进入用户模式
PREQ输出编程请求信号,用于请求进入编程模式
CLK输入用户逻辑时钟输入,连接到FPGA内部时钟网络
GSR输入全局复位/置位输入
GTS输入全局三态控制输入,高电平时所有IO输出为高阻
KEYCLEARB输入加密密钥清除信号,用于安全相关操作
PACK输入编程Ack信号,用于确认编程请求
USRCCLKO输入用户时钟输出,连接到外部CCLK引脚
USRCCLKT输入用户时钟三态使能,高电平有效,控制USRCCLKO是否输出到CCLK引脚
USRDONEO输入用户DONE信号,可替代DONE引脚状态输出
USRDONETS输入用户DONE三态控制

实际远程升级场景中,最常用的就是USRCCLKO和USRCCLKT这两个端口。USRCCLKO接你用户逻辑生成的Flash时钟信号,USRCCLKT接一个常高电平或者由控制逻辑产生的使能信号。当USRCCLKT为高电平时,USRCCLKO上的时钟信号就会出现在CCLK引脚上。

有一点要特别注意,USRCCLKO并不仅仅是驱动一个引脚那么简单。在FPGA内部,从USRCCLKO到CCLK引脚之间存在专用的路由路径,这与普通IO的路径完全不同。所以综合工具通常要求STARTUPE2原语例化在顶层模块中,并且相关的组合逻辑必须经过特定的buffer资源。

2.3 不同FPGA系列的差异

不同系列的FPGA,对应原语名称和端口定义不一样,别搞混了。

Spartan-6系列使用STARTUP_SPARTAN6原语,端口包括CLK、GSR、GTS、USRCCLKO、USRDONEO等,基本思路一致。

7系列使用STARTUPE2,是当前项目中使用最广泛的。

UltraScale系列使用STARTUPE3,端口增加了USRCCLKTS等信号,时钟管理机制略有调整,但核心思路仍然是引导内部时钟到CCLK引脚。

如果你的项目跨系列移植,不能直接照搬原语例化代码,需要查阅对应系列的手册。这一点在项目规划阶段就要考虑清楚,否则换芯片时会踩不少坑。

3. 用户模式下控制SPI Flash的完整方案

3.1 系统架构与引脚规划

在实际的远程升级系统中,FPGA需要具备两个能力:一是通过SPI接口读写外部Flash,用于存储应用程序固件或者用户数据;二是在特定条件下触发重配置,加载新的比特流。这两件事都会用到SPI Flash,但属于不同的工作阶段。

先说SPI Flash读写部分。当FPGA处于用户模式时,FPGA内部逻辑需要主动产生SPI时钟、片选、数据信号,发送读命令和写命令,读写Flash中的数据。这里的SPI时钟就是USRCCLKO的信号源,片选信号则是FCS_B。数据线方面,SPI x1模式只需要MOSI和MISO,通常连接到FPGA的普通IO引脚;SPI x4模式(QSPI)则需要额外的数据引脚,同样连接到普通IO。

这里要特别说明,很多工程师第一次接触这个场景时,容易把CCLK和普通IO混为一谈,以为直接把外部Flash的CLK引脚接到FPGA的普通IO上就完事了。实际上,在SPI Flash配置模式下,Flash的CLK引脚必须接到FPGA的CCLK专用引脚上,这是硬件设计阶段就确定好的。如果你在原理图上看到Flash的CLK引脚没有接到FPGA的CCLK引脚,而是接到了普通IO,那这个设计恐怕无法支持配置功能,只能用于用户模式下的数据读写,两者兼具时硬件拓扑一定要认真核对。

还有一种常见的硬件设计是Flash的CLK引脚通过一个电阻同时连接FPGA的CCLK引脚和一个普通IO,配置时用CCLK驱动,用户模式下用普通IO驱动。这种设计在低速下可行,但存在信号质量问题,我不推荐在量产产品中使用。

3.2 参考Verilog例程

下面给出一个典型的顶层模块例化代码,这个模块同时包含SPI Flash控制逻辑和STARTUPE2例化。

module flash_remote_upgrade_top ( input wire sys_clk, // 系统时钟,例如50MHz input wire rst_n, // 复位 output wire flash_clk, // 用于观察/调试的flash时钟 // SPI Flash 引脚(连接到外部Flash) inout wire flash_dq0, // IO0 / MOSI inout wire flash_dq1, // IO1 / MISO output wire flash_cs_n, // 片选(注意这里并不直接连接到FCS_B) // 其他控制信号 input wire upgrade_start, output wire upgrade_done ); // 内部信号 reg spi_clk_gen; reg spi_cs_n; reg flash_cs_n_reg; wire clk_to_flash; wire spifi_clock; // 生成SPI时钟,假设系统时钟50MHz,SPI时钟=50MHz/2=25MHz reg [3:0] clk_div_cnt; always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin clk_div_cnt <= 4'd0; spi_clk_gen <= 1'b0; end else begin if (clk_div_cnt == 4'd1) begin clk_div_cnt <= 4'd0; spi_clk_gen <= ~spi_clk_gen; end else begin clk_div_cnt <= clk_div_cnt + 1'b1; end end end // 片选信号生成 always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin spi_cs_n <= 1'b1; end else begin if (upgrade_start) begin spi_cs_n <= 1'b0; // 拉低片选,开始SPI通信 end else begin spi_cs_n <= 1'b1; end end end // 顶层输出flash时钟用于观察 assign flash_clk = spi_clk_gen; // 将生成的SPI时钟连接到STARTUPE2输入 assign clk_to_flash = spi_clk_gen; // 片选输出:注意普通IO的片选可以用,但FCS_B由STARTUPE2控制 assign flash_cs_n = spi_cs_n; // STARTUPE2 原语例化 STARTUPE2 #( .PROG_USR("FALSE"), // 不使用用户编程功能 .SIM_CCLK_FREQ(0.0) // 仿真时的CCLK频率参数 ) STARTUPE2_inst ( .CFGCLK ( ), // 1-bit 输出: 配置时钟 .CFGMCLK ( ), // 1-bit 输出: 配置主时钟 .EOS ( ), // 1-bit 输出: 启动完成信号 .PREQ ( ), // 1-bit 输出: 编程请求 .CLK (1'b0 ), // 1-bit 输入: 用户时钟 .GSR (1'b0 ), // 1-bit 输入: 全局置位/复位 .GTS (1'b0 ), // 1-bit 输入: 全局三态 .KEYCLEARB (1'b1 ), // 1-bit 输入: 密钥清除 .PACK (1'b0 ), // 1-bit 输入: 编程确认 .USRCCLKO (clk_to_flash ), // 1-bit 输入: 用户时钟输出,连接到CCLK .USRCCLKT (1'b1 ), // 1-bit 输入: 用户时钟三态使能,1使能 .USRDONEO (1'b0 ), // 1-bit 输入: 用户DONE .USRDONETS (1'b1 ) // 1-bit 输入: 用户DONE三态控制 );

这段代码中,SPI时钟由系统时钟分频产生,经过STARTUPE2的USRCCLKO端口输出到CCLK引脚。USRCCLKT接常高,意味着时钟始终使能。片选信号则通过普通IO输出到Flash的CS引脚。如果你希望片选也使用FCS_B专用引脚,那需要对代码做调整,把片选信号接到STARTUPE2相关的专用路径上,需要将片选信号连接到Flash的CS引脚(通常是普通IO)即可。

3.3 SPI x4模式的接线变化

上面例程适用于SPI x1模式。如果你使用的是QSPI x4模式,也就是四线数据读写,那么除了时钟和片选之外,还需要额外的数据引脚。这些引脚在配置阶段同样有特殊用途,但在用户模式下可以当作普通IO使用,相对简单。

不过要注意,QSPI x4模式中,Flash的IO0、IO1、IO2、IO3引脚在配置阶段有特定的上拉/下拉要求,这些通常由外部电阻实现。用户模式下这些引脚可以作为普通双向IO使用,但上拉电阻的取值会影响通信速率和信号完整性,一般建议4.7kΩ到10kΩ,具体以Flash芯片手册为准。

4. STARTUPE2使用中那些容易踩的坑

4.1 坑一:例化了原语却看不到时钟输出

这种情况很常见。代码综合、布局布线都通过了,上板调试发现CCLK引脚上没有波形,或者Flash完全无响应。排查顺序是这样的。

首先检查USRCCLKT是否有效。这个信号是高电平有效,如果忘记连接或者接到了低电平,USRCCLKO上的时钟信号根本不会被传递到CCLK引脚。我在调试中就遇到过,USRCCLKT被综合工具默认接成了低电平,导致整个Flash通信一动不动。最简单的做法是直接把USRCCLKT接1'b1,或者用寄存器控制它,但初始值必须为1。

接着检查是否在顶层模块例化。Vivado综合时,STARTUPE2原语如果被综合到某个子模块内部,布局布线可能会因为层级关系处理不当,导致路由失败或信号丢失。建议把STARTUPE2例化在顶层,这样最稳妥。

最后检查约束。STARTUPE2相关引脚通常不需要额外的Tcl约束,但如果你在设计中对CCLK引脚添加了错误的IO约束,比如设置了PULLUP或PULLDOWN,可能会导致输出信号异常。CCLK引脚在用户模式下不应该添加额外的上下拉约束。

4.2 坑二:仿真完全正常,上板Flash读写却乱码

这类问题多半出在时钟相位和时序上。SPI通信对时钟相位比较敏感,特别是在高速条件下。FPGA内部产生的SPI时钟经过STARTUPE2路由到CCLK引脚,再经过外部PCB走线到达Flash,整个路径上有延迟。如果你的逻辑在时钟上升沿发送数据,而Flash在同一个上升沿采样,时序裕量可能不足。

经验做法是:FPGA在SPI时钟的下降沿输出数据,上升沿采样输入数据,这样给数据稳定的窗口更大。具体到Verilog实现,可以用Rising-edge和Falling-edge分别控制输出和输入逻辑,或者使用DDR原语。如果时钟频率不高(25MHz以下),直接用下降沿打拍输出数据通常就够用了。

还有一点容易被忽略,就是IOBank电压。CCLK引脚所在的Bank电压必须与Flash的供电电压一致,否则电平不匹配会导致通信不稳定。这是硬件设计层面的问题,软件调不了,只能修改硬件。

4.3 坑三:远程升级过程中擦除和写入时间太长,看门狗复位

远程升级时,Flash擦除和写入往往需要几百毫秒甚至几秒。如果系统中有看门狗或者外部复位电路,超时后系统复位,升级就会中断,Flash里可能留下不完整的固件,下次启动时FPGA配置失败,系统无法正常启动。

这种情况下,建议在升级流程中做状态机设计。擦除和写入Flash之前,先给上层系统发送一个“升级中”的状态,暂停看门狗喂狗;或者通过握手信号周期性地与看门狗通信,保证升级过程中不会被误复位。同时,在升级完成并校验通过后,再触发FPGA重新配置(通过ICAPE2原语IPROG命令),这样最安全。

4.4 坑四:关于ICAPE2和STARTUPE2的分工

很多初接触远程升级的工程师,对ICAPE2和STARTUPE2的分工感到困惑。简单来说,ICAPE2负责FPGA内部的配置寄存器访问和触发重配置,它不控制外部Flash的时钟引脚;STARTUPE2负责把用户逻辑的时钟引导到CCLK引脚供外部Flash使用。两者是配合关系,而不是替代关系。

一个典型的升级流程是:用户逻辑通过STARTUPE2驱动的SPI接口读取Flash中存储的最新固件,校验成功后写入一个标记寄存器,然后通过ICAPE2发送IPROG命令触发FPGA重新加载。重新加载过程中,FPGA自动进入配置模式,CCLK引脚重新由配置逻辑控制,从Flash读取新的比特流。这个流程中,STARTUPE2和ICAPE2各司其职,缺一不可。

4.5 坑五:下载Flash时提示 target dll has been cancelled

这个错误信息在调试中很折磨人。它通常出现在使用Vivado Hardware Manager下载.bit或.mcs文件到Flash时,原因是目标设备处于异常状态,或者下载连接不稳定。常见诱因包括:JTAG链上的其他设备干扰、FPGA处于用户模式且Flash正被用户逻辑频繁访问、下载电缆信号质量差等。

解决思路是先尝试将FPGA配置为已知状态,具体操作可以断开Startup或者通过Hardware Manager里的Program Device操作,把FPGA先加载一个空设计或者安全设计,然后再执行Flash下载。如果你的设计中已经用STARTUPE2接管了CCLK和Flash,下载时Flash可能正在被用户逻辑占用,导致下载器无法正常访问Flash,这种情况下需要先让用户逻辑释放Flash控制权,或者把USRCCLKT拉低关闭时钟输出。

4.6 坑六:复位信号处理不佳导致升级完成后不稳定

FPGA升级完成后重新配置,相当于一次重新上电。如果你在设计中使用了外部复位芯片,它的复位时间可能和FPGA配置时间不匹配。FPGA配置完成后立即开始执行用户逻辑,但外部复位芯片还在输出复位脉冲,用户逻辑在复位期间没有正常初始化,导致Flash通信时序错乱。

我遇到过的情况是,FPGA重新配置后SPI接口状态异常,需要额外操作才能恢复。解决方法是提升用户逻辑的复位逻辑设计,在配置完成后主动等待一段时间(比如10ms),确保外部复位信号已经完全释放,再初始化SPI控制器。在代码里可以用计数器实现这个延时,简单有效。

5. 工程实战:完整约束与原语优化

5.1 必须添加的约束

STARTUPE2虽然是原语,但为了确保综合和布局布线正确处理,还是建议在XDC约束文件中添加一些基本约束。

首先是时钟约束。如果是用户逻辑生成的SPI时钟,需要在XDC中声明这个时钟。比如:

create_clock -name spi_clk -period 40.000 [get_pins {STARTUPE2_inst/USRCCLKO}]

上面这条约束的作用是声明STARTUPE2的USRCCLKO输入时钟频率为25MHz。如果不加这个约束,时序分析工具可能无法准确分析SPI时钟域的逻辑路径,时序收敛全靠运气。

其次是PACKAGE_PIN约束。外部Flash的时钟引脚在FPGA封装中对应CCLK专用引脚,这个引脚在原理图符号中通常已经被FPGA软件识别,一般不需要额外约束。但如果你在设计中使用普通IO连接到Flash的时钟引脚(不推荐),那需要像普通IO一样添加约束,但这样的话就无法通过STARTUPE2驱动了。

5.2 时序优化技巧

STARTUPE2的路径延迟比普通IO要长,这是由专用路由结构决定的。如果你在设计中把SPI时钟频率设得非常高,比如超过100MHz,时序收敛会很困难。实际工程中,SPI Flash的常用工作频率在25MHz到50MHz之间,远程升级场景建议设置30MHz以下,稳定性和时序裕量都有保障。

如果时序还是无法收敛,可以考虑使用BUFR或BUFH在内部先做时钟整形,再接入STARTUPE2的USRCCLKO。但这种做法会引入额外的时钟延迟,不推荐在SPI这种半双工通信中使用。

5.3 和MultiBoot功能配合使用

Xilinx的7系列FPGA支持MultiBoot功能,也就是在同一个Flash中存储多个版本的比特流,通过ICAPE2触发重新配置,加载指定地址的比特流。这个功能是实现远程升级的最佳实践之一。

MultiBoot流程中,Flash的地址空间被划分为多个区域,每个区域存放一个版本的比特流。FPGA上电时默认从地址0x0加载,升级逻辑通过STARTUPE2控制的SPI接口将新固件写入到指定地址,然后通过ICAPE2发送IPROG命令重新加载。这里的SPI接口仍然依赖STARTUPE2提供CCLK时钟,所以STARTUPE2的稳定性直接关系到MultiBoot的成败。

在实现MultiBoot时,务必要在比特流中配置好起始地址。如果加载失败,FPGA会自动回退到备份地址。这个备份机制是FPGA芯片内部的,不需要额外逻辑实现,但需要在Vivado中配置相关参数。

6. 常见问题速查表

为了便于现场调试,我把最常见的几类问题和对应排查方法整理成了下面的表格,可以直接打印出来放工位上。

现象可能原因排查方法
Flash无响应,SCLK无波形USRCCLKT未拉高,时钟未使能检查STARTUPE2例化中USRCCLKT是否为1
Flash无响应,SCLK有波形片选信号未正确拉低检查片选信号的时序和极性
上板读写乱码SPI模式设置不一致确认Flash工作模式(x1/x4)与代码一致
升级后FPGA无法启动Flash写入数据不完整擦除后重新写入,增加校验机制
下载Flash报target dll错误Flash正被用户逻辑占用先加载空设计释放Flash控制权
时序收敛失败SPI时钟频率过高降低时钟频率到30MHz以下
CCLK引脚无输出但仿真正常STARTUPE2被综合优化掉了检查综合选项,添加DONT_TOUCH约束
升级过程中系统复位看门狗未暂停升级期间暂停看门狗或增加握手信号

7. 调试工具和方法分享

7.1 使用ILA观察内部信号

调试STARTUPE2相关电路时,ILA(集成逻辑分析仪)是很有用的工具。你可以把USRCCLKT、SPI时钟、片选信号、SPI数据线信号插入ILA,实时观察FPGA内部生成的这些信号是否正常。但有一个限制要注意:ILA只能观测FPGA内部的信号,无法直接观测CCLK引脚上的实际波形。如果内部信号正常,但CCLK引脚没有输出,那就是STARTUPE2到引脚之间的路由或约束问题。

7.2 示波器和逻辑分析仪

最直接的验证方法是使用示波器测量CCLK引脚波形。注意示波器探头的负载电容,高频信号需要低电容探头,否则测到的波形可能失真。FPGA内部生成的时钟,经过STARTUPE2和引脚驱动,波形一般比较稳定,但PCB走线过长或者负载过重时,边沿可能变缓。如果在这个节点测量到的波形上升沿超过10ns,就要考虑信号完整性问题了。

7.3 使用Vivado的Hardware Manager

调试过程中,Vivado的Hardware Manager不仅能下载比特流,还能读取FPGA状态寄存器,查看EOS信号状态。EOS信号是判断FPGA是否完成启动的重要标志。如果你的设计依赖STARTUPE2工作,可以在启动完成后通过ILA抓取EOS信号,确认FPGA已经处于用户模式。

8. 我自己的工程复盘和经验总结

做了六七年FPGA远程升级相关的项目,我踩过的坑比写过的代码还多。在这里分享三个印象深刻的调试经历,希望能帮大家少走弯路。

第一个是某型设备现场升级失败率居高不下。排查了很久,最后发现是USRCCLKT没复位时直接拉低,上电后用户逻辑没有主动拉高USRCCLKT,导致整个系统中的Flash在配置完成后一直处于无时钟状态。后来我在上电复位流程中增加了对USRCCLKT的控制逻辑,上电后立即拉高,升级成功率一下就恢复正常了。

第二个是Flash擦写速度和WatchDog的冲突。设备里有独立的硬件看门狗,升级过程中Flash擦除需要800ms,超过了看门狗超时时间,导致升级过程中系统反复复位。后来我们升级前先通知上层暂停看门狗,升级完成后重新启用,问题消除。如果你是做产品集成,这个点一定要提前和系统团队沟通好。

第三个是STARTUPE2被综合工具优化掉的案例,虽然不常见但确实会发生。当你的设计中没有明显的外部连接时,某些综合工具可能会把未使用的原语信号优化掉,导致USRCCLKO连接丢失。解决办法是在综合属性中给STARTUPE2例化添加(* KEEP = "TRUE" *)或者(* DONT_TOUCH = "TRUE" *)属性,防止被优化。

STARTUPE2这个原语,从原理上讲并不复杂,但实际工程中涉及的知识点不少。它的核心价值在于让FPGA在用户模式下依然能够控制配置引脚,从而实现本地或者远程的Flash读取和更新。弄懂它的原理和坑点,FPGA远程升级这条路就平坦了一大半。

最后再分享一个小技巧,FPGA启动完成后EOS信号会拉高,如果你的设计中存在配置引脚冲突问题,可以通过检测EOS信号来决定何时接管Flash控制权,这样可以避免配置阶段和用户模式切换时的信号冲突。这个方法在很多工业级产品中都有应用,实测下来稳定可靠。

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

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

立即咨询