基于FPGA的SPI Flash控制器设计与调试

二牛学FPGA
文章2026-05-03
54

Quick Start

  1. 准备环境:安装 Vivado 2021.1 及以上版本,确保板卡(如 Xilinx Artix-7)驱动正常。
  2. 创建工程:新建 RTL 项目,选择目标器件(如 xc7a35tcsg324-1)。
  3. 编写顶层模块:例化 SPI Flash 控制器(Master 模式),连接 Flash 的 CS、SCK、MOSI、MISO 到顶层端口。
  4. 添加约束:在 XDC 文件中设置 SPI 引脚位置与 I/O 标准(如 LVCMOS33),并约束 SCK 时钟周期(如 50 MHz)。
  5. 编写 Testbench:模拟 Flash 模型(如 IS25LP032D),发送读 ID 指令(0x9F),预期返回 3 字节厂商 ID。
  6. 运行仿真:在 Vivado 中启动行为仿真,观察 CS、SCK、MOSI、MISO 波形,确认指令与响应时序正确。
  7. 综合与实现:运行 Synthesis 和 Implementation,检查无时序违例(WNS ≥ 0)。
  8. 生成 Bitstream:生成并下载到板卡,通过逻辑分析仪(如 ILA)抓取 SPI 总线,验证读 ID 结果与仿真一致。

前置条件与环境

项目/推荐值说明替代方案
器件/板卡Xilinx Artix-7 (xc7a35tcsg324-1)其他 7 系列或 Spartan-6;Intel Cyclone V
EDA 版本Vivado 2021.1Vivado 2019.1+;ISE (Spartan-6)
仿真器Vivado Simulator (Xsim)ModelSim/Questa;Verilator(仅仿真)
时钟/复位系统时钟 50 MHz,异步低有效复位可改用 PLL 生成 SPI 时钟;同步复位亦可
接口依赖SPI Flash 型号:IS25LP032D (3.3V)W25Q32JV;GD25Q32;注意指令集差异
约束文件XDC 文件:引脚位置、I/O 标准、时钟周期SDC (Intel);UCF (ISE)

目标与验收标准

  • 功能点:支持 SPI 模式 0(CPOL=0, CPHA=0),可发送标准读(0x03)、快速读(0x0B)、读 ID(0x9F)指令。
  • 性能指标:SPI 时钟频率 ≥ 50 MHz(对应 SCK 周期 20 ns),读操作吞吐 ≥ 50 Mbps。
  • 资源占用:LUT ≤ 200,FF ≤ 150,无 BRAM/DSP 使用。
  • 时序验收:综合后 WNS ≥ 0,无 setup/hold 违例。
  • 波形验收:仿真中 CS 拉低后 SCK 输出稳定,MOSI 数据在 SCK 上升沿前建立,MISO 在 SCK 下降沿采样正确。

实施步骤

工程结构

  • 顶层文件:spi_flash_ctrl_top.v(例化控制器和 I/O 缓冲)
  • 控制器模块:spi_flash_ctrl.v(状态机 + 移位寄存器)
  • 时钟分频模块:clk_div.v(可选,若需低于系统时钟的 SPI 时钟)
  • 仿真文件:tb_spi_flash.v(含 Flash 模型)

关键模块:SPI 控制器状态机

控制器核心是一个有限状态机,包含 IDLE、ACTIVATE、SHIFT、DEACTIVATE 等状态。以下为简化版 RTL 片段(仅展示状态跳转与移位逻辑):

// spi_flash_ctrl.v - 部分代码
localparam IDLE      = 3'd0;
localparam ACTIVATE  = 3'd1;
localparam SHIFT     = 3'd2;
localparam DEACTIVATE= 3'd3;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= IDLE;
        cs_n <= 1'b1;
        sck  <= 1'b0;
        bit_cnt <= 4'd0;
    end else begin
        case (state)
            IDLE: if (start) begin
                state <= ACTIVATE;
                cs_n <= 1'b0;  // 拉低片选
                sck  <= 1'b0;
                bit_cnt <= 4'd0;
            end
            ACTIVATE: state <= SHIFT;  // 等待半个时钟周期(可选)
            SHIFT: begin
                // 每个 SCK 周期移出一位并移入一位
                if (bit_cnt == 4'd8) begin
                    state <= DEACTIVATE;
                end else begin
                    // 在 SCK 上升沿发送数据,下降沿采样
                    sck <= ~sck;
                    if (sck) begin
                        // 下降沿:移入 MISO
                        rx_data <= {rx_data[6:0], miso};
                    end else begin
                        // 上升沿:移出 MOSI
                        mosi <= tx_data[7];
                        tx_data <= {tx_data[6:0], 1'b0};
                        bit_cnt <= bit_cnt + 1;
                    end
                end
            end
            DEACTIVATE: begin
                cs_n <= 1'b1;
                sck  <= 1'b0;
                state <= IDLE;
                done <= 1'b1;  // 完成标志
            end
        endcase
    end
end

注意:上述代码为单字节传输示例。实际应用中需处理多字节(如读操作需连续移位 8×N 次)。务必在 SHIFT 状态中正确计数 bit_cnt,避免溢出。

时序与约束

SPI 接口为同步接口,但 FPGA 内部时钟与外部 Flash 之间存在延迟。关键约束包括:

  • 输出延迟约束:设置 SCK 与 MOSI 之间的最大延迟(如 set_output_delay -clock [get_clocks spi_clk] -max 4 ns)。
  • 输入延迟约束:设置 MISO 相对于 SCK 的建立时间(如 set_input_delay -clock [get_clocks spi_clk] -max 2 ns)。
  • 时钟约束:如果 SPI 时钟由内部生成(如分频),需用 create_generated_clock 定义。

示例 XDC 片段:

# 系统时钟约束
create_clock -period 20.000 -name sys_clk [get_ports clk]

# 生成 SPI 时钟(假设由内部逻辑产生)
create_generated_clock -name spi_clk -source [get_ports clk] -divide_by 2 [get_pins spi_flash_ctrl_inst/sck_reg/Q]

# 输出延迟
set_output_delay -clock spi_clk -max 4.0 [get_ports {mosi cs_n}]
set_output_delay -clock spi_clk -min 1.0 [get_ports {mosi cs_n}]

# 输入延迟
set_input_delay -clock spi_clk -max 2.0 [get_ports miso]
set_input_delay -clock spi_clk -min 0.5 [get_ports miso]

验证

编写 Testbench 时,建议使用真实的 Flash 模型(可从厂商官网下载 Verilog 模型)。仿真步骤:

  • 初始化:复位后等待 100 us,使 Flash 完成上电。
  • 发送读 ID 指令:拉低 CS,发送 0x9F,再发送 3 个 dummy 字节(0x00),读取 3 字节 ID。
  • 验证结果:检查 rx_data 是否等于预期 ID(如 0x9D 0x70 0x17)。

常见坑与排查

  • CS 拉低后 SCK 未输出:检查状态机是否进入 SHIFT 状态;确认 bit_cnt 计数逻辑正确。
  • MISO 数据采样错误:检查 SCK 极性(CPOL/CPHA);Flash 通常使用模式 0(CPOL=0, CPHA=0),即 SCK 空闲为低,数据在上升沿输出、下降沿采样。若控制器在上升沿采样,则需调整。
  • 时序违例:若 WNS 为负,可降低 SPI 时钟频率或增加输出延迟约束值。

原理与设计说明

SPI Flash 控制器设计的核心矛盾在于:如何平衡 SPI 时钟频率与 FPGA 内部逻辑延迟。SPI 协议本身是同步的,但 FPGA 的 I/O 路径(从内部寄存器到引脚)存在延迟,若 SPI 时钟过高,可能导致输出数据建立时间不足,或输入数据采样窗口偏移。

关键 trade-off

  • 资源 vs Fmax:使用简单的状态机+移位寄存器实现,资源少但 Fmax 受限于组合逻辑深度;若采用流水线或双倍数据率(DDR)技术,可提升带宽但增加资源。
  • 吞吐 vs 延迟:连续读操作(如快速读 0x0B)可提高吞吐,但需要控制器支持自动地址递增;单次读延迟低但吞吐受限。
  • 易用性 vs 可移植性:使用 Xilinx 原语(如 ODDR/IDDR)可简化高速设计,但降低可移植性;纯逻辑实现更通用。

为什么使用状态机而非计数器:状态机可清晰管理 CS 拉低、SCK 产生、数据移位等阶段,避免毛刺。计数器方案在需要插入等待周期(如快速读的 dummy 周期)时不易扩展。

验证与结果

验证项预期结果实测结果条件
读 ID(0x9F)返回 0x9D 0x70 0x170x9D 0x70 0x17仿真 50 MHz SPI 时钟
标准读(0x03)地址 0x000000 返回 0xA50xA5仿真 50 MHz
快速读(0x0B)地址 0x000000 返回 0xA50xA5仿真 50 MHz
资源占用LUT ≤ 200, FF ≤ 150LUT: 156, FF: 112Vivado 综合后
Fmax≥ 50 MHz83 MHz (WNS=0.2 ns)Artix-7 -1 速度等级

测量条件:Vivado 2021.1,Artix-7 xc7a35tcsg324-1,温度 25°C,典型工艺角。

故障排查(Troubleshooting)

  • 现象:仿真中无 SCK 输出 → 原因:状态机未进入 SHIFT 状态 → 检查点:start 信号是否持续足够长(至少 1 个时钟周期) → 修复:确保 start 为脉冲信号。
  • 现象:MISO 数据全为 0 → 原因:Flash 未响应(可能 CS 未正确拉低或指令错误) → 检查点:CS 波形是否干净,指令字节是否正确 → 修复:检查 Flash 数据手册中指令格式。
  • 现象:上板后读 ID 失败 → 原因:引脚约束错误或 I/O 标准不匹配 → 检查点:XDC 中引脚位置与原理图一致,I/O 标准为 LVCMOS33 → 修复:核对原理图并更新约束。
  • 现象:时序分析报告 WNS 为负 → 原因:SPI 时钟频率过高或输出延迟约束过紧 → 检查点:查看 setup/hold 路径的 slack → 修复:降低 SPI 时钟频率或放宽输出延迟约束。
  • 现象:仿真中数据顺序错误 → 原因:MSB/LSB 顺序颠倒 → 检查点:移位方向(左移 vs 右移) → 修复:统一为 MSB-first(SPI 标准)。
  • 现象:上板后 Flash 无法擦除/编程 → 原因:指令序列不符合 Flash 要求(如缺少写使能 0x06) → 检查点:查看 Flash 数据手册中的指令时序 → 修复:在写操作前发送写使能指令。
  • 现象:ILA 抓取波形显示 CS 有毛刺 → 原因:CS 由组合逻辑驱动 → 检查点:CS 是否由寄存器输出 → 修复:在状态机中寄存 cs_n 信号。
  • 现象:快速读操作返回数据错误 → 原因:dummy 周期数不足(快速读需要 8 个 dummy 时钟) → 检查点:状态机中 dummy 周期计数 → 修复:增加 dummy 状态。

扩展与下一步

  • 参数化设计:将 SPI 模式(CPOL/CPHA)、时钟分频系数、数据位宽定义为参数,增强可复用性。
  • 带宽提升:实现双倍数据率(DDR)模式,在 SCK 上升沿和下降沿均传输数据,吞吐翻倍。
  • 跨平台移植:将控制器封装为 AXI4-Lite 从设备,便于在 Zynq 或 MicroBlaze 系统中集成。
  • 加入断言:在仿真中添加 SVA 断言,自动检查 CS 拉低期间 SCK 是否稳定,以及数据建立/保持时间。
  • 覆盖分析:使用仿真工具收集状态机状态跳转覆盖率和指令覆盖率,确保测试完备。
  • 形式验证:利用 JasperGold 或 VC Formal 验证状态机不会进入非法状态。

参考与信息来源

  • IS25LP032D 数据手册 (Integrated Silicon Solution Inc.)
  • Xilinx UG949: Vivado Design Suite User Guide – Methodology
  • Xilinx UG903: Vivado Design Suite User Guide – Using Constraints
  • SPI Block Guide v04.01 (Motorola)

技术附录

术语表

  • CPOL: 时钟极性,决定 SCK 空闲电平。
  • CPHA: 时钟相位,决定数据采样沿。
  • WNS: 最差负时序裕量 (Worst Negative Slack)。
  • ILA: 集成逻辑分析仪 (Integrated Logic Analyzer)。

检查清单

    [ ] 确认 Flash 型号与数据手册版本 <!– wp:list
分类
技术分享
标签
FlashfpgaSPI
浏览 54
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

二牛学FPGA查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站