ARTICLE DETAIL

资讯详情

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

FPGA远程升级全解析:STARTUPE2原语与SPI Flash

FPGA远程升级全解析:STARTUPE2原语与SPI Flash 搞FPGA的都知道远程升级是产品交付之后最让人头疼的事。板子铺到现场结果固件要改总不能一台一台拆机插JTAG。所以这几年我接触的不少项目第一版设计就会把“远程升级”写进需求。而一旦要做远程升级就绕不开一个东西STARTUPE2原语。它和SPI Flash之间的关系几乎所有新人都要踩几轮坑才能理顺。这篇文章就把它彻底讲清楚。先说这篇文章适合谁看你在用Xilinx 7系列FPGAArtix-7/Kintex-7/Spartan-7等板子上挂了SPI NOR Flash想实现“上位机发数据→FPGA写Flash→重启后跑新固件”的完整链路但发现Flash挂在专用配置引脚上普通逻辑根本驱动不了那这篇文章就是给你写的。我会把STARTUPE2的端口、代码、约束、整个升级流程设计、以及实际调试中容易踩的坑全部过一遍你可以直接照着搭。1. 远程升级先搞懂一件事STARTUPE2到底卡在哪一环1.1 Flash专用引脚用户逻辑为什么碰不得Xilinx 7系列FPGA上电后要从外部SPI Flash加载配置文件。这个阶段里CCLK、D00_MOSI、D01_DIN、CS_B这几个引脚完全由FPGA内部的配置逻辑接管它们负责把Flash里的比特流读进来初始化LUT和BRAM。问题在于配置完成之后这几个引脚并没有自动交给用户逻辑使用。尤其是CCLK这个引脚它在配置结束后既不是普通GPIO也不能通过顶层端口直接引出。就算你把一个普通的SPI控制器写在HDL里顶层往CCLK上连一根输出线综合布局布线的时候Vivado直接会报错或者你根本找不到这个物理引脚该怎么约束。我见过不少刚接触远程升级的工程师第一反应都是“把Flash信号接到普通Bank的IO上不就行了”。如果你板子设计得早配置Flash确实可以同时引到普通GPIO上那是一套独立于配置链路的物理连线需要板级设计时就留好。但绝大多数量产板Flash就是直接挂在专用配置引脚上的省了走线也省了BOM。这时候要让用户逻辑去控制Flash就必须用原语把内部信号“映射”到专用引脚上。STARTUPE2就是干这个的。打个比方专用配置引脚像一条平时由门禁系统控制的通道FPGA上电配置时门禁自动开门、读数、关门。配置完了你需要再进去读写Flash但你手里没有门禁卡。STARTUPE2就是这张能打开专用通道的卡用它把用户逻辑里的时钟和数据信号引到那条物理链路上。1.2 别混淆两件事改配置流和改数据区远程升级这个词其实包含两种完全不同的需求一种是只更新FPGA配置比特流也就是Verilog工程编译出来的bit文件。这种升级的核心动作是“把新bit文件写到Flash里的某个地址”然后让FPGA重新从Flash加载。这种场景里你不仅要写Flash还要用ICAPE2原语触发重配置甚至要做双镜像回退。另一种是更新挂在FPGA上的软核程序、参数表、算法系数或者把Flash当大容量掉电不丢的存储介质来用。这种场景只涉及Flash数据的读写不需要重新配置FPGA你可以直接把Flash挂在普通GPIO上做个SPI控制器。真正复杂的是第一种——更新配置流而且很多项目的远程升级是“配置流数据区”混合的。比如你用MicroBlaze跑了一个协议栈协议栈程序放在SPI Flash里FPGA配置完成后由软核去读那么升级的时候既要把新的配置流写到Flash也要把软核程序写到对应的存储区域。这个时候你就在同一颗Flash上工作既要用STARTUPE2访问配置引脚的物理链路又要规划不同的地址区间还要在写完配置流后触发ICAPE2让FPGA重配置。这个区分非常关键因为你会发现有些远程升级方案根本不需要STARTUPE2直接用普通IO控制一颗额外的Flash就能实现。但那叫“外挂Flash升级”不是“升级系统配置Flash”。如果你要升级的就是启动配置那颗Flash那STARTUPE2基本是绕不开的。2. STARTUPE2原语实战端口、代码、约束一次讲透2.1 8个端口真正常用的就3个STARTUPE2原语在7系列里的端口定义比较固定我先把端口列出来再逐个说用法。端口名方向说明CLK输入空闲时输入时钟一般直接接地GSR输入全局置位复位非必要接地GTS输入全局三态控制非必要接地KEYCLEARB输入密钥清零信号默认拉高PACK输入控制PACK位默认接地USRCCLKO输入用户逻辑输出给CCLK引脚的时钟USRCCLKTS输入USRCCLKO的三态控制低有效USRDONEO输出用户逻辑读DONE状态USRDONETS输入USRDONEO的三态控制低有效这么多端口说实话大部分项目里用到的就那么几个而已。CLK、GSR、GTS直接接0或者接地PACK接0KEYCLEARB按需处理大部分情况直接拉1。核心是USRCCLKO和USRCCLKTS这两个。USRCCLKO是你自己生成的SPI时钟信号通常从一个分频器或者MMCM输出然后接进来它会直接驱动CCLK引脚。USRCCLKTS是三态控制这个东西极其坑后面避坑章节我会单独讲现在你只需要记住要让USRCCLKO真正输出到引脚上USRCCLKTS必须为低也就是不能让它处于高阻态。USRDONEO是DONE引脚的反馈配置完成时它是高电平。有些设计会用这个信号作为“Flash控制器只有在FPGA配置完成后才能开始工作”的使能条件这是合理的但是要注意它和全局复位时序的配合。如果你不关心DONE状态USRDONETS接1USRDONEO悬空即可。2.2 最小连接代码直接抄就行下面是一个最常用的实例化写法。假设你有一个模块叫spi_flash_ctrl负责产生SPI时钟、CS、MOSI并采集MISOSTARTUPE2 #( .PROG_USR (FALSE), .SIM_CCLK_FREQ (0.0) ) startupe2_inst ( .CLK (1b0), .GSR (1b0), .GTS (1b0), .KEYCLEARB (1b1), .PACK (1b0), .USRCCLKO (spi_clk), .USRCCLKTS (1b0), .USRDONEO (1b0), .USRDONETS (1b1) );这套例化里spi_clk来自你的SPI控制器它最终会从FPGA的CCLK引脚输出直接作为Flash的SCK使用。MOSI、MISO、CS_B则根据你板卡的实际连接要么是普通IO要么也需要复用专用引脚这种情况需要结合IO约束通常板卡设计时已经预留了连接。关键一点SPI Flash时钟频率不要往上顶太狠。很多SPI Flash标称支持104MHz那是针对连续读指令的你的用户逻辑写的擦除、写命令低频操作更稳。我自己的习惯是系统时钟100MHz进来SPI时钟控制在12.5MHz或者25MHz留足时序裕量。这样无论是时钟走线延迟还是Flash芯片本身的响应时间都不会成为偶发故障的源头。约束方面如果你使用的MOSI、MISO、CS_B是普通Bank IO正常在XDC里约束IOSTANDARD即可。但CCLK不需要也无法走普通的PACKAGE_PIN约束因为它在物理位置上已经固定了。如果这几根线复用专用引脚Vivado的BITSTREAM配置项里还有相关的设置具体要看你的板级设计。# 举例普通IO的信号约束 set_property IOSTANDARD LVCMOS33 [get_ports {spi_mosi spi_miso spi_cs_n}] # 系统时钟约束 create_clock -name sys_clk -period 10.000 [get_ports sys_clk]注意这里的PACKAGE_PIN没有写因为你必须按自己的原理图填实际管脚。真正的硬件连接、电平标准、引脚编号一定要以板卡原理图为准这是一条铁律。2.3 为什么说SPI Mode和时序参数必须较真SPI Flash常见的模式是Mode 0CPOL0CPHA0和Mode 3CPOL1CPHA1。Xilinx 7系列SPI配置模式大多数情况下是工作在Mode 0也就是时钟空闲为低、数据在上升沿采样。我在实际项目里踩过这样一个坑Flash能正常配置说明配置阶段时序没问题但用户逻辑去读Flash ID的时候返回全FF。查来查去最后发现是我在SPI控制器里不小心把CPOL写反了变成了时钟空闲为高。配置阶段用的是FPGA内部配置逻辑跟你的用户SPI控制器没关系所以配置正常但用户逻辑完全没用。这种“配置链路正常但用户SPI异常”的问题最迷惑人因为现象看起来像Flash坏了或者焊接虚了其实只是模式没对齐。还有一个细节是擦除等待时间。SPI NOR Flash擦除一个扇区不同厂商、不同型号差异很大有的几百毫秒有的几秒。规格书里给的tBE、tPP这些参数必须认真算进状态机里。很多“升级到一半卡死”的问题本质都是擦除等待时间不够或者轮询状态寄存器的方式写错了。写入之前必须发0x06写使能命令写完后通过读状态寄存器0x05检查WIP位是否归零这属于SPI Flash操作的基本功但在远程升级这种批量数据写入场景里任何一步出错都会导致整片数据损坏。3. 从写Flash到触发重配置一套能落地的远程升级流程3.1 升级链路框架先画清楚再写代码完整的一套远程升级一般分上位机、传输通道、FPGA内部逻辑三层。这里我重点说FPGA内部。FPGA内部至少要包含这几个模块通信接口模块UART或者以太网负责接收上位机下发的一帧一帧数据命令解析模块解析帧头、命令字、地址、长度、CRC校验SPI Flash控制器通过STARTUPE2原语访问配置Flash执行擦除、写入、回读升级状态机协调上面几个模块管理整个升级过程ICAPE2重配置模块升级校验通过后触发FPGA重新加载新比特流通信帧格式建议设计得简单清晰我常用的一种结构是这样字段长度说明帧头2字节固定0xAA55用于帧同步命令字1字节0x01擦除、0x02写入、0x03回读校验、0x04触发重配置地址3字节Flash目标地址支持到16MB空间数据长度1字节数据区长度最大256字节数据N字节要写入Flash的内容CRC162字节对前面所有字段的校验每条命令都回ACKACK里带一个状态码至少能区分“成功、CRC错误、Flash超时、地址非法”。别嫌这个协议简单很多项目就是卡在“上位机不知道下位机到底写没写进去”这个最基本的沟通问题上。3.2 Flash存储布局Golden镜像和Update镜像一定要分开远程升级最怕什么最怕写到一半掉电Flash里留下一个残缺的新比特流而上电加载时又正好加载到这个残缺数据设备直接变砖。所以在设计存储布局时必须把“升级失败还能回退”当作硬性需求。我常用的布局是这样Flash起始地址0x000000放一个出厂Golden版本这个版本永远不会被远程升级覆盖它就是一个基础加载器功能很单一能通信、能接收固件、能写Flash、能触发跳转。真正的应用程序放在0x200000开始的地址区域。升级流程是上位机先把新应用写入0x200000区域写完回读校验CRC之后再发一个重配置命令FPGA通过ICAPE2触发跳转到0x200000加载新应用。这里有个细节就是这个“基础加载器”的设计。它不需要很复杂但它必须存在而且必须保证通过任何恶劣的升级失败现场它都能被正常加载。如果设备升级新应用失败看门狗超时或者上电检测到启动失败标志就自动停在Golden版本等待上位机重新下发升级数据。在这个设计里0x000000区域的内容就是最后的保命稻草。为什么我强调这个因为很多工程师第一次做远程升级时整个Flash只放一个应用升级失败等于整机报废只能返厂拆机重烧。做过一次返厂你就懂了那点Flash容量、那点布局规划的时间成本跟一整个售后团队的差旅费比起来简直不值一提。3.3 ICAPE2原语触发重配置和回退写完Flash之后要让FPGA立刻加载新版本就要通过ICAPE2原语向FPGA内部配置逻辑发送IPROG命令。IPROG的作用是让FPGA重新从指定地址读取配置数据。ICAPE2的端口定义如下ICAPE2 #( .ICAP_WIDTH (X32), .SIM_CFG_FILE_NAME (NONE) ) icape2_inst ( .CLK (icap_clk), .CSIB (icap_csib), .RDWRB (icap_rdwrb), .O (icap_data_out[31:0]), .I (icap_data_in[31:0]), .CE (icap_ce), .SEL (icap_sel) );具体发送IPROG的指令序列Xilinx官方UG470里写得很清楚核心是先写一个同步头再写命令寄存器最后指定跳转地址。实际工程里不建议自己手撸直接参考官方提供的example design或者XAPP1247里的时序代码量很小关键是指令字节不能错。触发重配置之前还有一个容易被忽略的操作先把Flash的访问权从你的用户逻辑中释放掉。因为重配置一开始FPGA内部配置逻辑就要接管Flash引脚了如果你这边CS_B还没拉高或者SPI控制器还占着总线两边同时驱动轻则这次的配置加载失败重则可能产生物理冲突。正常流程是先让SPI控制器回到IDLE态CS_B拉高然后等一小段时间再触发ICAPE2。还有一个回退设计思路我实际用下来比较稳在写新应用之前先在Flash某个固定地址写一个“升级进行中”标志。新应用正常启动后第一件事就是清掉这个标志改成“应用正常”。如果FPGA加载新应用后没能正常启动看门狗没有喂狗系统复位后回到Golden版本Golden版本检查到这个标志还在就知道上次升级失败了自动进入待升级状态等待新数据。这个标志成本极低但排查现场问题时作用巨大。3.4 升级状态机别把流程写成面条状态机这块我见过太多“感觉能跑”的写法了。升级状态机最忌讳的就是每个状态之间跳转条件含糊。我的建议是状态机至少要区分这么几个状态IDLE、ERASE、WRITE、VERIFY、WAIT_CONFIG、ERROR。IDLE等命令。ERASE阶段只做一件事擦除目标扇区擦除完了回IDLE或者直接进WRITE。WRITE阶段按帧写入每写一帧回ACK上位机确认后再发下一帧。VERIFY阶段把写进去的数据读回来和原始数据比对。全部通过后进WAIT_CONFIG等待上位机的触发重配置命令或者是硬件触发信号。任何一个状态里出现CRC错误、Flash超时、状态寄存器WIP位卡住等异常直接进ERROR在ERROR里上报错误码然后看门狗复位让系统回到Golden版本。这套流程看起来繁琐但好处是每一步都有明确结果出了问题现场工程师能根据ACK里的状态码快速定位是通信问题、Flash问题、还是数据源头问题。省掉任何一步“感觉差不多就行”后面都要用线上事故来还。4. 避坑实录这些坑我替你们先踩过了4.1 坑一USRCCLKTS没拉低时钟根本没输出这是STARTUPE2最常见的坑没有之一。很多新手照着文档例化完发现Flash SCK引脚没波形查原理图查连接查约束查了半天最后才发现USRCCLKTS一直处于高电平也就是三态USRCCLKO的信号根本没被驱动到引脚上。USRCCLKTS是低电平有效的三态控制。想让它正常输出时钟这个端口就必须接0。有些参考设计为了省事直接把它固定接地这种做法在大多数应用里没问题但如果你需要在某个时刻把CCLK释放掉比如重配置之前要彻底断开用户逻辑对配置引脚的影响那就可以通过一个寄存器动态控制它。我现在的习惯是平时拉低让时钟正常输出触发ICAPE2之前先拉高把引脚让出来等配置逻辑接管。4.2 坑二重配置前没释放Flash总线加载失败这个坑也很隐蔽。升级程序跑完你发送IPROG命令但FPGA重新加载时却卡住了或者加载出来还是旧版本。排查一圈发现是因为你的SPI控制器在触发重配置的瞬间还在驱动着MOSI、CS_B这些引脚配置逻辑初始化Flash时读到的全是乱时序自然加载失败。触发重配置之前一定要让所有Flash相关的用户逻辑回到一个安静状态CS_B拉高、MOSI置低或者高阻、SPI时钟停止输出同时把USRCCLKTS拉高释放时钟引脚。这个顺序千万别反。我的做法是在状态机里专门加了一个RELEASE_FLASH状态里面就干这几件事然后延时一定时间再进CONFIG状态触发ICAPE2。4.3 坑三Flash擦除等待时间不够状态机卡死SPI NOR Flash的扇区擦除时间不是固定的温度、电压、芯片老化程度都会影响。如果你在状态机里写死一个延时比如1秒结果某片Flash状态差一点需要1.2秒你的状态机可能已经在超时边缘反复试探了表现出来就是“升级十次偶尔有一两次失败”。正确做法是轮询Flash状态寄存器0x05命令检查WIP位。擦除命令发完之后循环读状态寄存器WIP为0说明擦除完成。同时要加一个总超时保护比如超过规格书最大时间的两倍就报错。轮询方式既不会过早误判也不会在Flash正常工作时傻等。4.4 常见问题速查表我把自己做远程升级时遇到过的典型问题和排查方向整理成了一张表你可以直接截图放团队文档里。现象可能原因处理办法读Flash ID返回全FFSPI模式不对、CS_B拉低时序异常检查CPOL/CPHA确认Mode 0用示波器抓CS_B和SCK关系写使能命令无效WP引脚被拉低硬件写保护检查Flash WP引脚电平必须为高擦除正常写入后数据全是FF忘记发送WREN写使能命令每次页编程前都要发0x06升级完成后重新配置失败新比特流CRC错误、地址映射不对JTAG回读Flash内容和上位机原始数据比对触发ICAPE2后板卡无反应IPROG指令序列错误、Flash总线未释放核对UG470命令序列检查CS_B是否已拉高上电瞬间Flash被误写USRCCLKTS悬空、CS_B在配置阶段被用户逻辑拉低上电默认拉高CS_BUSRCCLKTS接0用户SPI控制器加复位使能运行期间偶发Flash读数据出错SPI时钟频率过高时序裕量不足降低SPI时钟频率检查走线长度必要时调整时钟相位最后再分享一个我自己的习惯。每次做完远程升级功能我一定会做一次叫做“假升级”的测试故意往新应用区域写一段乱码然后看系统能不能正确回退到Golden版本。这个动作看着简单但真能救设备。还有升级期间每完成一个关键步骤比如擦除完成、每写满一定字节、校验通过都通过串口打印日志现场排障的时候这些日志能帮你少走至少一半的弯路。
返回列表