这一篇在干嘛?
系列收尾篇,素材最短但值得展开:EF2、EF3 器件的 Flash 不仅用来存位流,还允许用户逻辑直接读写——这是实现”非 JTAG 在线升级”和”掉电不丢数据存储”的钥匙。本篇讲清楚:为什么它值得讲、怎么打开这个能力、读写流程、以及”一个地址写错就变砖”的风险控制。前置知识见:硬件接口(三):加载模式全解(Dual Boot 机制)、硬件接口(一):IO 上下拉与 PCI Clamp。
为什么内部 Flash 值得单独讲
先理清一个背景:FPGA 是 SRAM 工艺,配置数据(位流)必须存在非易失存储器里,上电加载进来。EF2、EF3 系列把这颗 SPI Flash 直接合封在芯片内部(合封结构见硬件接口(三)的 MSPI 一节),而 EG4 系列的 Flash 在片外。
这颗 Flash 里通常装着什么?
- 位流数据:加载参数区 + Primary/Golden 两份位流(EF2/EF3 的合封 Flash 容量设计得能装两份位流);
- 大片剩余空间:位流一般只占 Flash 的一小部分(EF2 也就几百 Kbit 量级),剩下的空间白白闲置。
用户逻辑直接读写它的价值就在这两点上:
- 非 JTAG 在线升级:没有 JTAG 线、没有外部 CPU 摸得到 Flash 的场合(EF2/EF3 的 Flash 在片内,外部总线根本接不到),只能让 FPGA 自己的逻辑当”编程器”,通过 UART/SPI/I2C 等任意接口接收新位流写进 Flash——产品部署到现场后照样能升级。
- 用户数据存储:用剩余空间保存校准系数、序列号、运行日志、掉电记忆配置等需要掉电不丢的数据,省掉一颗外部 EEPROM/Flash。
注意区分:本篇机制针对 EF2、EF3(内置 Flash);EG4 的 Flash 在片外,CPU 或 FPGA 逻辑可通过外部 SPI 总线访问,思路类似但不存在”内部 Flash 原语”一说。参赛者若用 EG4S20,可把本篇的流程与风险控制思想迁移到外挂 Flash 上。
打开大门的两个前提条件
默认情况下,加载完成后 Flash 是”加载系统专用”的,用户逻辑碰不到它。要让用户代码访问内置 Flash,EF2、EF3 各需满足两个条件:
条件一:调用 PHY 原语,把 Flash 的 SPI 接口引出到用户逻辑
- EF2 调用 EF2_PHY_INTFLASH 原语,EF3 调用 EF3_PHY_INTFLASH 原语;
- 原语(Primitive)是芯片里真实存在的硬件单元,在 Verilog 里像用模块一样例化它。EF2 的原语定义:
module EF2_PHY_INTFLASH (
mosi_io0, // SPI 数据线 0(PT6D)
miso_io1, // SPI 数据线 1(PT23B)
wp_n_io2, // 写保护,低有效(PT20D)
hold_n_io3, // 保持,低有效(PT9D)
cclk, // SPI 时钟(PT9B)
cs_n // 片选,低有效(PT11D)
);
endmodule本质:这颗合封 Flash 就是一个标准 SPI Flash 芯片,这六根信号就是它的 SPI 总线。原语把这条总线”桥接”进你的逻辑——例化后,你用 SPI 时序协议就能和它对话。引脚注释里的 PT6D、PT23B 等是这些信号对应的物理管脚位置,说明配置端口的走线在芯片内是固定的。
条件二:TD 软件里把 persist_bit 设为 0
在 TD 软件的 Properties -> Control Option 里将 persist_bit(配置端口保持位)设置为 0。
persist_bit 是什么?它决定加载完成后,配置专用端口(本来归加载硬件管的 SPI 接口)交不交给用户逻辑。设为 0 = 加载完成后端口释放给用户;默认(1)= 端口继续保持配置功能。不设这一项,原语引出来了也连不到 Flash 上。
代价:persist_bit = 0 之后,通过 JTAG 编程时不能再选择 bg_mode(后台模式)——因为配置端口的所有权换人了。这是硬件配置上的取舍,做方案时要想清楚你的产品是否还需要 JTAG 后台升级(JTAG 常规编程仍可用,只是后台模式受限)。
读写流程:先擦后写
拿到 SPI 总线后,剩下的就是标准 SPI Flash 的操作学。如果你读过 W25Q 之类的 SPI Flash 芯片手册,会发现流程一模一样:
- 读(Read):拉低 CS_N → 发读命令 + 地址 → 按时钟移出数据 → 拉高 CS_N。读是无破坏性的,可以随便读。
- 擦除(Erase):Flash 的物理特性决定了写只能是”把 1 改成 0”,想重新写必须先整块擦回全 1。擦除按块/扇区进行,不能擦单个字节。
- 写/编程(Program):擦完后按页(Page)写入数据。所以顺序永远是:先擦除,再写入。
这些命令时序、扇区大小、页大小、擦写时间,都可以参考合封 Flash 的数据手册(安路提供 Flash 型号信息),FAE 也明确说了:用户可参考 Flash 数据手册,自行编写 SPI 接口逻辑完成读写——不一定非要等安路的参考设计。自己写的好处是接口、缓存策略完全可控;缺点是要自己处理好命令时序与等待时间(擦除可能要几十到几百毫秒,期间不能操作该块)。
还有一个方向性的问题:怎么知道该读/写哪个地址? Flash 开头是加载参数区和位流区(Dual Boot 的地图见硬件接口(三)),用户数据必须放到位流占用范围之外——这个边界要按你的位流实际大小和 Flash 地图划定,并且最好在软件里做地址保护(写入前检查地址范围)。
典型应用:非 JTAG 在线升级与数据存储
应用一:非 JTAG 在线升级。 安路提供了三种现成的”接口桥”参考设计,把常见通信口桥接到 Flash 编程逻辑:
| 参考设计 | 桥接接口 | 典型场景 |
|---|---|---|
| Uart_2_Flash | UART 串口 | 最简单:上位机串口发升级包,无需额外硬件 |
| SPI_2_Flash | SPI | 主控 MCU 通过 SPI 更新 FPGA 的 Flash |
| IIC_2_Flash | I2C | 挂在系统 I2C 总线上,多设备共享总线升级 |
工作流程串起来看:外部(上位机/MCU)通过桥接接口把新位流发给 FPGA → FPGA 逻辑按 SPI 时序擦除 Flash 的 Primary 区域 → 写入新位流 → 校验 → 复位重新加载。升级过程中 FPGA 自身还在跑旧逻辑(写 Flash 不影响 SRAM 里的运行状态),所以配合上一篇讲的 Dual Boot:Golden 区域永不擦写,Primary 写坏了也能从 Golden 启动、保持通信能力再次升级——这套组合拳就是量产产品在线升级的标准姿势。
应用二:用户数据存储。 用 Flash 剩余空间做”EEPROM”:上电后用户逻辑从约定地址读出校准系数/配置参数;需要保存时按”先擦后写”更新。相比外部 EEPROM 省一颗芯片、省走线;缺点是擦写粒度大(扇区级)、擦写寿命有限(通常 10 万次量级),高频写入的数据不适合直接落 Flash,可先攒在 RAM 里、低频批量写入。
注意事项与风险控制
把手册的注意事项展开成一张风险清单:
- 头号红线:不能破坏已有的 bit 位流数据。 用户代码访问 Flash 时地址算错、越界,擦掉的就是位流本身——下次上电直接变砖。对策:固化 Flash 地图文档(参数区/Primary/Golden/用户数据区各自的起止地址);代码里做地址范围保护;升级逻辑严格遵守 Dual Boot 分工(只动 Primary,Golden 神圣不可侵犯)。
- persist_bit = 0 的副作用要记牢:JTAG 编程不能选 bg_mode(后台模式)。如果产品既要做用户逻辑访问 Flash、又依赖 JTAG 后台模式做产线烧录,两者冲突,需要重新规划升级路径。
- 擦写等待时间必须做足:擦除/编程期间状态寄存器会指示 busy,逻辑要轮询等待,不能掐表硬等——超时不足就是写入失败。
- 擦写寿命与掉电保护:Flash 擦写次数有限,升级、存参数都不应频繁全量擦写;写入过程中突然掉电会导致数据不完整——升级场景靠 Golden 兜底,参数存储场景可设计”双页备份 + CRC 校验”。
- 不用原语自写 SPI 逻辑时,注意 Flash 的四线信号(含 WP_N 写保护、HOLD_N)都要正确驱动,写保护功能可以利用起来保护位流区。
本篇通关标准:
- 能说出用户逻辑访问内置 Flash 的两个前提条件(例化 EF2_PHY_INTFLASH/EF3_PHY_INTFLASH 原语 + TD 里 persist_bit=0),以及 persist_bit=0 牺牲了什么(JTAG 不能选 bg_mode)。
- 能完整叙述 Flash “先擦除再写入”的流程及原因(物理上只能把 1 写成 0),并说出 Uart_2_Flash / SPI_2_Flash / IIC_2_Flash 三种参考设计的用途。
- 能画出”在线升级 + Dual Boot”的组合安全方案:Golden 永不擦写、只动 Primary、写坏也能启动并再次升级。
persist_bit 设为 0 和设为 1 分别是什么含义?设 0 的代价是什么?
persist_bit 决定加载完成后配置专用端口(SPI Flash 接口)的控制权归属。设 1(默认)端口归配置硬件保持;设 0 端口释放给用户逻辑,用户代码才能通过原语访问 Flash。代价:JTAG 编程时不能选择 bg_mode 后台模式。
为什么往 Flash 写数据前必须先擦除?
NOR/SPI Flash 的物理写入只能把比特从 1 改成 0,无法把 0 改回 1;擦除操作以块/扇区为单位把数据整体恢复成全 1。所以更新数据的流程必然是:读出旧值 → 擦除扇区 → 按页写入新值 → 校验。直接向未擦除区域编程会得到新旧数据的”与”结果,数据损坏。
用 Uart_2_Flash 升级时如果升级到一半断电了,怎么保证设备还能救回来?
依赖 Dual Boot:Golden 区域在出厂时烧入保底位流,且在线升级过程永不擦写。断电导致 Primary 位流不完整时,下次上电加载 Primary 失败,FPGA 自动跳转到 Golden 位流启动,系统中仍保留通信通道,可再次发起升级修复 Primary。
EG4S20 有"内部 Flash"可以按本篇方法用原语访问吗?
没有。本篇的 EF2_PHY_INTFLASH / EF3_PHY_INTFLASH 原语只适用于 Flash 合封的 EF2、EF3 器件;EG4 系列 Flash 在片外,FPGA 或外部 CPU 需通过外部 SPI 总线访问。但”先擦后写、地址保护、Dual Boot 兜底、擦写寿命管理”这套流程与风险控制思想完全适用。
想用 Flash 剩余空间保存每秒更新的运行计数,直接每次落 Flash 合适吗?
不合适。Flash 擦除是扇区级、擦写寿命约 10 万次量级,每秒擦写几个月就报废。正确做法:数据先攒在 RAM/寄存器中,关机前或低频(如每小时)批量写入;或采用双页备份 + CRC 校验的磨损均衡写法,把擦写频率降低几个数量级。