FPGA:spi_flash读写测试
SIP_FLASH逻辑设计
- 需求
SPI读写flash,最大4mbit,实现给定地址,长度可完成存储数据读取。
本设计包含指令:RSR-1、PP、SE、BE、READ。
用户接口:
Port name |
Direction |
Type |
Description |
clk_sys |
INPUT |
WIRE |
输入系统时钟 |
rst_sys |
INPUT |
WIRE |
输入系统复位 |
rd_en |
INPUT |
WIRE |
flash读使能 |
wr_en |
INPUT |
WIRE |
flash写使能 |
chip_es_en |
INPUT |
WIRE |
flash片清除信号 |
rd_sr |
INPUT |
WIRE |
读flash状态寄存器 |
sr_data |
OUTPUT |
WIRE |
读寄存器输出值 |
data_i |
INPUT |
[31:0] |
写FIFO,32位数据信号 |
data_vld_i |
INPUT |
WIRE |
写FIFO,数据有效信号 |
wr_almostfull_o |
OUTPUT |
WIRE |
写FIFO,fifo满信号 |
wr_datacnt_o |
OUTPUT |
WIRE |
写FIFO,计数 |
data_o |
OUTPUT |
REG |
读FIFO,数据输出 |
data_o_en |
INPUT |
WIRE |
读FIFO,数据使能信号 |
rd_empty_o |
OUTPUT |
WIRE |
读FIFO,高有效表示FIFO内数据为空。 |
rd_almostfull_o |
OUTPUT |
WIRE |
读FIFO,almost_full信号,当信号为1,表示FIFO内存仅剩50个数存储量。 |
rd_datacnt_o |
OUTPUT |
WIRE |
FIFO内存数据计数。 |
length_i |
INPUT |
[15:0] |
写数据总长度,16位宽最大为2mbit。 |
address_i |
INPUT |
[23:0] |
写数据初始地址 |
addr_vld |
INPUT |
WIRE |
数据地址和数据长度有效信号 |
物理层接口:
Port name |
Direction |
Type |
Description |
si_io |
input |
wire |
SPI输入数据线,MOSI |
so_io |
output |
reg |
SPI输出数据线,MOSO |
cs_io |
output |
reg |
片选信号,低有效 |
用户接口时序:
1) 读使能与写使能以及片擦除使能均有效两个时钟周期,下图仅展示读使能。

2) 写入数据时序如下图所示:

3) 当full_o信号为1时,暂停数据写入,等待full_o信号置0后,继续写入数据。

4) 读数据时序如下图:

5) 读数据时,empty为0时数据有效。如下如:

6) 数据长度、起始地址信号时序如下:

状态机:


Figure 1 SPI_FLASH 状态机
总结:
1) 尝试配置 FLASH 管脚时,会发现 CCLK 管脚是不可配置的,这实际上是因为 CCLK_0 管脚在内部已经被占用,我们必须通过其他方式获取/设置,这里使用原语控制CCLK_0。读SR1寄存器,在读写操作前后均需要发出都寄存器指令检测FLASH是否能够继续下一步操作,主要检测WEL和WIP位。
2) SO_IO并不是一定随着时序在发送结束后立即置高。是否为一不影响后续指令操作,一切看状态寄存器。
3) PP操作时,若给定地址不是页头地址,当数据写数据超过当前页区域时,后续数据将从当前页区域首地址继续写数据。而读书则自动做地址偏移。
- Flash命令和SPI协议时序
1.1. Flash指令


写使能(WREN)需要在PP、SE、BE、操作前将wel写使能处于锁存状态。

.
1.2. SPI时序
1.2.1. READ(Read Data Bytes)
作用:读取数据;
时序图(SPI mode):

1.2.2. SE(Sector Erase)
作用:将扇区所有数据擦除即全“1”;在发送SE之前需要发送WREN将WEL位设置;
时序图(SPI mode):

1.2.3. CE(Chip Erase)
作用:将FLASH上所有数据擦除即全“1”;在发送之前需要发送WREN将WEL位设置;
时序图(SPI mode):

1.2.4. PP(Page Program)
作用:对内存写0;在发送之前需要发送WREN将WEL位设置;若发送的数据超过256Bytes,它会将最后256Bytes发送到FLASH;若发送的数据不超过256Bytes,数据将发送到请求的地址;
时序图(SPI mode):

- 仿真以及时序对齐:
Spi读FIFO数据,初始设计时序逻辑的spi_clk下降沿触发32位宽的fifo_dout左移一位,由于spi写数据时为连续写入,导致从fifo中读出数据时无法及时触发读rd_en使能,所以写入数据始终为初始数据;
修改为组合逻辑,设置spi_clk 的上升沿信号(spi_pos)和下降沿信号(spi_neg),组合逻辑中触发32位宽数据fifo_dout的移位以及从FIFO中读数据。
跨时钟域处理:对于clk_sys时钟域的读写指令信号,通过打三拍的方式在spi_clk慢时钟域抓取。

仿真:
写数据仿真;
读数据仿真;

FLASH时钟原语配置:
STARTUPE2 #(
.PROG_USR ("FALSE"),
.SIM_CCLK_FREQ (0.0)
)
STARTUPE2_inst(
.CFGCLK (cfgclk),
.CFGMCLK (cfgmclk),
.EOS (eos),
.PREQ (),
.CLK (0),
.GSR (0),
.GTS (0),
.KEYCLEARB (1),
.PACK (1),
.USRCCLKO (usrcclk),
.USRCCLKTS (0),
.USRDONEO (1),
.USRDONETS (0)
);
CFGCLK,配置逻辑主时钟,仅在配置时有输出,在 master 模式下也一直存在(存疑;根据后面的测试,文档所说的 Persist Enabled 应当指的是 Master 模式下一直使能/连接,但除了配置的时候存在时钟,其他时候为无效的高电平;在配置了 USRCCLK 时,在 FPGA 配置结束后三个 clk 该时钟将切换到 USRCCLK)。Configuration mode 是根据 M[2:0] 管脚配置的,最常用的是 M[2:0]=001 对应的 Master SPI 配置模式,笔者开发板即本模式,Master 模式下 CCLK 由 FPGA 输出给 FLASH 的 SCK;
EOS,End Of Start,指示 FPGA 配置的结束;
CFGMCLK,配置内部振荡器时钟,是从内部的一个锁相环输出的 65MHz 时钟(不是很准,笔者输出到 GPIO 用示波器看过,那块板子的 ~68MHz);
USRDONEO,输出到FPGA 的 DONE_0 管脚。一般而言,在硬件设计中会在这个管脚挂一个 LED 灯,以指示 FPGA 完成配置开始运行;
USRDONETS,控制 DONE 管脚的三态门,1 将设置为高阻,0 将会把用户给入的 USRDONEO 输出到 DONE_0 管脚;
USRCCLKO,在配置完成后,驱动 CCLK_0 管脚的用户自定义时钟。在配置完成后,前三个时钟周期用于切换时钟源,且不会被输出,但如果使用了 EMCCLK 管脚,则 EMCCLK 信号会出现在 CCLK 管脚上,直到过渡到用户自定义时钟;
USRCCLKTS,控制 USRCCLKO 的三态门;
GTS,全局三态门使能,要用户自定义 CCLK/DONE 等,此管脚必须置低。

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理