【FPGA】 verilog设计spi读写flash实现数据DMA搬运

verilog设计spi读写flash实现数据DMA搬运

引言

在 FPGA 系统中,Flash 存储器常用于存储固件、配置参数或用户数据。传统的 Flash 读写操作通常依赖于嵌入式软核(如 MicroBlaze、RISC-V 等)通过 SPI 或其他接口逐字节/逐页处理,这种方式会占用大量 CPU 时间,导致系统响应延迟,尤其在需要高速、大数据量搬运的场景(如 OTA 升级、参数批量配置)中成为性能瓶颈。

采用 DMA(直接存储器访问)方式读写 Flash,能够将数据搬运任务从 CPU 剥离,由硬件逻辑直接控制 Flash 的读写时序与数据流,实现零 CPU 占用的并行数据传输。这不仅大幅提升了 Flash 访问速度,缩短了系统启动与升级时间,还释放了 CPU 资源,使其能够专注于其他实时任务。

本文将详细介绍基于 Verilog 实现的 SPI Flash DMA 搬运模块的设计与实现,涵盖以下核心内容:

  1. 整体架构:说明 Flash DMA 搬运系统的模块划分与数据流。
  2. 关键模块设计 :包括 DMA 控制器(dma_ctrl.v)、Flash 控制器(flash_ctrl.v)、SPI 主控(spi_master.v)等核心模块的功能与接口。
  3. 数据搬运流程:从软核下发指令(方向、地址、目标地址、使能)到硬件完成整个搬运过程的详细时序。
  4. 仿真验证:介绍仿真环境搭建、测试用例设计以及如何通过仿真模型(如 M25P16)验证功能的正确性。
  5. 性能优势分析:对比软核处理与 DMA 搬运在速度、资源占用等方面的差异。

通过本文,读者将掌握在 FPGA 中利用硬件逻辑实现高效、可靠的 Flash 数据 DMA 搬运方案,为实际项目中的高速存储访问需求提供参考。

设计说明

复制代码
设计主要包括
	flash_dma_mem.v
	dma_ctrl.v
	flash_ctrl.v
	flash_dev.v
	flash_read_cmd.v
	flash_write_cmd.v
	spi_master.v
仿真驱动
	dma_tb.v
	dma_tb_dev.v
	dma_buff.v
	get_clk_rst.v
	printf_txt.v
	flash m25p16仿真模型
仿真数据
	input.txt
	output.txt

接口时序

flash 写搬运时许如上图所示

flash 读搬运时许如上图所示

i_dir :读写方向 0:flash写入 1:flash写入

o_rport_data_lengths和o_wport_data_lengths等于(i_data_lengths/2)是因为flash数据为8bit,i_rport_data和o_wport_data输入输出16bit

flash JEDEC ID 如下所示,读出来的数据正确o_dma_pass拉高,不同的flash ID值不一样,对应文件flash_dma_mem.v

根据手册更改flash指令,对应文件flash_ctrl.v

附代码

verilog 复制代码
// ================================================================
// SPDX-License-Identifier: Proprietary
//
// Copyright (C) 2026, <li_lys>
//
// THIS SOFTWARE IS PROVIDED FOR NON-COMMERCIAL, EDUCATIONAL, AND
// PERSONAL USE ONLY. ANY COMMERCIAL USE, INCLUDING BUT NOT LIMITED
// TO INCORPORATION INTO ANY PRODUCT, SALE, OR LICENSING OF THIS
// CODE, IN WHOLE OR IN PART, IS EXPRESSLY FORBIDDEN.
//
// File Name   : flash_dma_mem.v
// Version     : v1.0
// For commercial licensing inquiries, please contact:
// <li_lys@163.com>
// ================================================================

module flash_dma_mem (
    input                   i_rst_n                 ,
    input                   i_clk                   ,
    input                   i_en                    ,
    input                   i_dir                   ,
    input        [23:0]     i_flash_addrs           ,
    input        [31:0]     i_mem_addrs             ,
    input                   i_mem_addrs_vld         ,
    input        [31:0]     i_data_lengths          ,
    output                  o_wport_clk             ,
    output                  o_wport_start           ,
    output       [15:0]     o_wport_data            ,
    output                  o_wport_data_vld        ,
    output       [31:0]     o_wport_data_lengths    ,
    output       [31:0]     o_wport_data_addrs      ,
    output                  o_rport_clk             ,
    output                  o_rport_start           ,
    input        [15:0]     i_rport_data            ,
    output                  o_rport_data_vld        ,
    output       [31:0]     o_rport_data_lengths    ,
    output       [31:0]     o_rport_data_addrs      ,
    output                  o_dma_busy              ,
    output reg              o_dma_pass              ,
    input                   i_miso                  ,
    output                  o_mosi                  ,
    output                  o_hlod                  ,
    output                  o_wp                    ,
    output                  o_cs_n                  ,
    output                  o_sck
);

localparam           FLASH_ID = 24'h202015;

wire        [23:0]   flash_addrs       ;
wire                 flash_wr_en       ;
wire                 flash_rd_en       ;
wire        [31:0]   flash_data_lengths;
wire        [23:0]   flash_id_data     ;
wire                 flash_id_data_vld ;
wire        [7:0]    flash_old_byte_id ;
wire                 done              ;
wire        [7:0]    read_data         ;
wire                 read_data_vld     ;
wire        [7:0]    write_data        ;
wire                 write_data_req    ;

always @(posedge i_clk ) begin
    if(!i_rst_n) begin
        o_dma_pass <= 1'b0;
    end
    else if((flash_id_data_vld == 1'b1) && (flash_id_data == FLASH_ID)) begin
        o_dma_pass <= 1'b1;
    end
    else begin
        o_dma_pass <= o_dma_pass;
    end
end

dma_ctrl dma_ctrl_inst(
    .i_rst_n                 (i_rst_n                   ), // input
    .i_clk                   (i_clk                     ), // input
    .i_dir                   (i_dir                     ), // input //0 wr flash 1 read flash
    .i_en                    (i_en                      ), // input
    .i_lengths               (i_data_lengths            ), // input [31:0]
    .i_flash_addrs           (i_flash_addrs             ), // input [23:0]
    .i_mem_addrs             (i_mem_addrs               ), // input [31:0]
    .i_mem_addrs_vld         (i_mem_addrs_vld           ), // input
    .o_flash_wr_en           (flash_wr_en               ), // output reg
    .o_flash_rd_en           (flash_rd_en               ), // output reg
    .o_flash_addrs           (flash_addrs               ), // output reg [23:0]
    .o_flash_lengths         (flash_data_lengths        ), // output reg [31:0]
    .i_done                  (done                      ), // input
    .i_flash_data            (read_data                 ), // input [7:0]
    .i_flash_data_vld        (read_data_vld             ), // input
    .i_flash_data_req        (write_data_req            ), // input
    .o_flash_data            (write_data                ), // output reg [7:0]
    .o_dma_busy              (o_dma_busy                ), // output reg
    .o_wport_clk             (o_wport_clk               ), // output
    .o_wport_start           (o_wport_start             ), // output reg
    .o_wport_data            (o_wport_data              ), // output reg [15:0]
    .o_wport_data_vld        (o_wport_data_vld          ), // output reg
    .o_wport_data_lengths    (o_wport_data_lengths      ), // output reg [31:0]
    .o_wport_data_addrs      (o_wport_data_addrs        ), // output reg [31:0]
    .o_rport_clk             (o_rport_clk               ), // output
    .o_rport_start           (o_rport_start             ), // output reg
    .o_rport_data_vld        (o_rport_data_vld          ), // output reg
    .o_rport_data_lengths    (o_rport_data_lengths      ), // output reg [31:0]
    .o_rport_data_addrs      (o_rport_data_addrs        ), // output reg [31:0]
    .o_miso                  (i_miso                    ), // input
    .o_mosi                  (o_mosi                    ), // output
    .o_hlod                  (o_hlod                    ), // output
    .o_wp                    (o_wp                      ), // output
    .o_cs_n                  (o_cs_n                    ), // output
    .o_sck                   (o_sck                     )  // output
);

endmodule

链接: https://pan.baidu.com/s/15ys5xKXoleQRubPeXuYPXA?pwd=r26u 提取码: r26u

相关推荐
LabVIEW开发1 天前
LabVIEW 如何把地下震源定位误差做到5米内
fpga开发·labview·labview知识·labview功能·labview程序
Dr.kangder1 天前
嵌入式处理器架构解析(十)——DSP+FPGA
fpga开发·架构·嵌入式
szxinmai主板定制专家2 天前
Zynq MPSoC实现半导体真空腔体状态监测|FPGA多传感器时序同步采集方案
fpga开发·zynq·控制器·mpsoc·半导体设备·fpga同步
学习FPGA的电气小兴兴2 天前
基于FPGA的DDS信号发生器设计,生成频率可调的正弦波、三角波、锯齿波和方波
fpga开发·数字信号处理·fpga·vivado·dsp·dds·diy
szxinmai主板定制专家2 天前
Zynq MPSoC半导体高精度运动控制方案|FPGA硬件插补实现亚微米级伺服实时控制
人工智能·嵌入式硬件·fpga开发·zynq·运动控制·ethercat
星夜离尘563 天前
ILA 在线调试:Vivado 抓取片内信号与定位下板问题(附完整实操)
fpga开发
jz_ddk3 天前
[学习] 深入理解 USB 通信与 FPGA 实现:从协议到逻辑设计
学习·fpga开发·xilinx·usb2.0
星夜离尘564 天前
PWM 信号生成与呼吸灯:占空比控制、按键调光与舵机扩展(附完整 Verilog 实操)
fpga开发