当前位置: 首页 > news >正文

FPGA驱动SPI Flash的读写时序与Verilog实现

1. SPI Flash基础与FPGA控制原理

SPI Flash是一种采用串行外设接口(SPI)的闪存芯片,在嵌入式系统中广泛用于存储固件、配置参数等数据。与并行Flash相比,SPI Flash引脚数量少(通常4-6线)、封装尺寸小,特别适合空间受限的应用场景。FPGA作为可编程逻辑器件,通过硬件描述语言(Verilog/VHDL)可以灵活实现SPI控制器,直接操作Flash存储器。

以常见的M25P16为例,这款16Mbit容量的SPI Flash支持最高50MHz时钟频率。实际项目中我发现,要稳定驱动这类芯片,关键在于两点:一是正确配置SPI通信模式,二是准确理解Flash的操作指令集。这就像与人交流,既要说对方听得懂的语言(SPI时序),又要用正确的词汇表达意图(操作指令)。

2. SPI模式选择与时序设计

2.1 SPI工作模式解析

SPI有四种工作模式,由CPOL(时钟极性)和CPHA(时钟相位)两个参数决定:

  • CPOL=0:时钟空闲时为低电平
  • CPOL=1:时钟空闲时为高电平
  • CPHA=0:数据在时钟第一个边沿采样
  • CPHA=1:数据在时钟第二个边沿采样

M25P16支持模式0(CPOL=0,CPHA=0)和模式3(CPOL=1,CPHA=1)。实测中模式3稳定性更好,其特点是:

  1. 时钟空闲时为高电平
  2. 数据在下降沿输出(FPGA发送)
  3. 数据在上升沿采样(FPGA接收)

2.2 Verilog时序实现技巧

下面是用Verilog实现SPI主机的核心代码段:

// SPI模式3:CPOL=1, CPHA=1 always @(posedge sys_clk) begin if(state == SPI_DATA) begin // 时钟信号翻转 spi_clk_reg <= ~spi_clk_reg; // 下降沿发送数据 if(spi_clk_reg == 1'b1) spi_mosi_reg <= write_data_reg[7]; // 上升沿接收数据 if(spi_clk_reg == 1'b0) read_data_reg <= {read_data_reg[6:0], spi_miso}; end end

特别要注意的是第一个数据位的处理。在模式3下,由于第一个下降沿前没有时钟边沿,需要在初始状态就设置好MOSI电平。我曾在项目中因为这个细节导致首字节数据错误,调试了整整一天才发现问题。

3. Flash指令集详解与操作流程

3.1 关键指令解析

M25P16的指令集包含约20条命令,但实际常用的是以下几个:

  1. WREN (06h):写使能。任何写入或擦除操作前必须执行,相当于"解锁"开关。
  2. READ (03h):读取数据。发送24位地址后可连续读取,地址自动递增。
  3. PP (02h):页编程。每次写入最多256字节,需先擦除对应区域。
  4. SE (D8h):扇区擦除。以64KB为单位擦除,耗时约0.5-1秒。
  5. RDID (9Fh):读取厂商ID。常用于硬件调试和SPI通路验证。

3.2 典型操作时序

读取数据流程

  1. 拉低CS片选信号
  2. 发送READ指令(03h)
  3. 发送24位地址(3字节)
  4. 连续读取数据
  5. 拉高CS结束传输

写入数据流程

  1. 发送WREN指令
  2. 发送PP指令(02h)
  3. 发送24位地址
  4. 发送1-256字节数据
  5. 等待TPP时间(典型值0.7ms)

这里有个易错点:PP指令只能将bit从1改为0,若要写入新数据,必须先执行擦除操作将目标区域恢复为全1状态。我在早期项目中就犯过直接写入未擦除区域的错误,导致数据异常。

4. Verilog实现完整架构

4.1 模块划分建议

完整的Flash控制器建议分为两个模块:

  1. SPI主机模块:处理底层时序,提供字节级读写接口
  2. Flash控制模块:解析高层指令,管理操作流程
module flash_control( input sys_clk, input sys_rstn, // 用户接口 input read_req, input [23:0] read_addr, output [7:0] read_data, // SPI接口 output spi_clk, output spi_mosi, input spi_miso ); // 状态机定义 localparam IDLE = 3'd0; localparam CMD = 3'd1; localparam ADDR = 3'd2; localparam DATA = 3'd3; reg [2:0] state; reg [7:0] cmd_reg; reg [23:0] addr_reg;

4.2 状态机设计要点

Flash操作本质上是状态机驱动的过程。以页编程为例:

  1. IDLE状态:等待用户请求
  2. WREN状态:发送写使能指令
  3. PP_CMD状态:发送页编程指令
  4. ADDR状态:发送目标地址
  5. DATA状态:传输数据字节
  6. WAIT状态:等待编程完成

实际项目中,建议为每个重要操作添加超时检测。我曾遇到Flash芯片异常导致永久卡死的情况,后来加入超时机制后系统稳定性大幅提升。

5. 工程实践中的经验分享

5.1 时序约束关键点

在FPGA工程中,必须添加正确的时序约束:

# SPI时钟约束(假设系统时钟50MHz) create_clock -period 20 [get_ports sys_clk] set_output_delay -clock [get_clocks sys_clk] -max 2 [get_ports {spi_mosi spi_clk}]

特别要注意跨时钟域信号的处理。如果SPI时钟分频自系统时钟,需要使用适当的同步器处理控制信号。

5.2 常见问题排查

  1. 读取全FF或00

    • 检查CS信号是否正常
    • 确认SPI模式设置正确
    • 测量电源电压是否稳定
  2. 写入失败

    • 是否先执行了WREN
    • 目标区域是否已擦除
    • 等待时间是否足够(TPP/TSE)
  3. 间歇性错误

    • 检查PCB布线(SPI走线要短且等长)
    • 添加去耦电容(0.1uF靠近VCC引脚)
    • 降低时钟频率测试

记得第一次调试时,我遇到随机数据错误,最后发现是PCB上SPI走线过长(>10cm)导致信号完整性问题。将Flash芯片移近FPGA后问题立即解决。

6. 性能优化技巧

6.1 双缓冲读取技术

对于大数据量读取,可采用双缓冲机制:

// 双缓冲实现 reg [7:0] buffer0[0:255]; reg [7:0] buffer1[0:255]; reg buffer_sel; always @(posedge sys_clk) begin if(read_ack) begin if(!buffer_sel) buffer0[addr_lsb] <= read_data; else buffer1[addr_lsb] <= read_data; if(addr_lsb == 8'hFF) buffer_sel <= ~buffer_sel; end end

这种方法允许在读取一个缓冲区的数据时,后台同时填充另一个缓冲区,显著提高吞吐量。

6.2 批量擦除策略

Flash擦除耗时较长(扇区擦除约0.5s,整片擦除可达10s),建议:

  1. 上电时检查是否需要全局擦除
  2. 运行时采用"脏块标记"管理
  3. 空闲时执行后台擦除操作

在某个数据采集项目中,我通过预擦除空闲扇区的方法,将关键写入延迟从毫秒级降低到微秒级,满足了实时性要求。

http://www.jsqmd.com/news/658962/

相关文章:

  • 从命令行到C++代码:手把手教你用OpenSSL 1.1.1实现AES-CBC文件加密与解密
  • 20个现代Web UI组件原型完全指南:打造专业级用户界面
  • FileKit性能优化指南:10个提升文件操作效率的方法
  • 最完整的Vue可视化编辑器方案:OXOYO/X-Flowchart-Vue核心功能与实战指南
  • TorchMetrics与PyTorch Lightning集成:如何实现无代码度量管理
  • Python 字典高效合并:自定义处理重复键的完整指南
  • HJ181 相差不超过k的最多数
  • 低代码平台为何突然“写不出代码”?揭秘AI生成逻辑断层的7个致命信号及48小时修复方案
  • 深入浅出Tcache Attack(一):机制剖析与Poisoning实战
  • django-fsm与Django版本兼容性:从1.8到6.0完整适配
  • FPGA丨高斯滤波算法实现:从理论到硬件架构的平滑之旅
  • 企业培训为什么值得优先上智能体?
  • WMRouter适配器扩展:轻松集成RxJava3与Kotlin协程的终极指南
  • 2026年3月涂胶设备生产厂家推荐,55加仑压盘泵/PACK涂胶机/压盘泵供胶系统/螺杆阀,涂胶设备实力厂家口碑推荐 - 品牌推荐师
  • 【权威实测】生成式AI通信方案吞吐量排行榜:SSE vs Websocket vs gRPC-Web vs QUIC-HTTP/3(TPS/首字节延迟/错误率三维打分)
  • 从零构建企业级流程图引擎:OXOYO/X-Flowchart-Vue 架构解密与实战指南
  • 第 26 课:任务表格列配置与持久化
  • 题解:洛谷 P1554 梦中的统计
  • 彻底搞懂NuGetForUnity架构设计:Unity包管理器核心原理与工作流程解析
  • STC89C51单片机驱动RC522读卡器,手把手教你实现门禁卡识别(附完整代码)
  • 奇点倒计时187天:2026大会AI重构建议的“不可逆窗口期”详解——错过这波,下一轮技术红利至少延迟3.2年
  • TorchMetrics部署指南:从开发到生产环境的完整流程
  • 从零开始:Carbon测试驱动开发实战指南
  • /华硕冰锐 GA502DU GU502DU 原厂Win10 20H1系统分享下载-宇程系统站
  • OpenVAS Scanner扫描插件结果数据备份介质管理终极指南
  • vLLM 0.7.0实战:用PagedAttention技术提升Qwen2.5-72B推理效率3倍以上
  • 因为目前opencv所有代码都是在activity里面展示的,所以我的opencv代码全都在activity里面
  • 奇点大会闭门报告流出:AISQL生成准确率从68%跃升至99.2%的关键7步工程化改造
  • 中炬高新2026Q1归母净利润创新高 经营修复动能强劲
  • 终极揭秘:Fastfetch硬件信息获取原理与核心检测技术详解