verilog设计spi读写flash实现数据DMA搬运
引言
在 FPGA 系统中,Flash 存储器常用于存储固件、配置参数或用户数据。传统的 Flash 读写操作通常依赖于嵌入式软核(如 MicroBlaze、RISC-V 等)通过 SPI 或其他接口逐字节/逐页处理,这种方式会占用大量 CPU 时间,导致系统响应延迟,尤其在需要高速、大数据量搬运的场景(如 OTA 升级、参数批量配置)中成为性能瓶颈。
采用 DMA(直接存储器访问)方式读写 Flash,能够将数据搬运任务从 CPU 剥离,由硬件逻辑直接控制 Flash 的读写时序与数据流,实现零 CPU 占用的并行数据传输。这不仅大幅提升了 Flash 访问速度,缩短了系统启动与升级时间,还释放了 CPU 资源,使其能够专注于其他实时任务。
本文将详细介绍基于 Verilog 实现的 SPI Flash DMA 搬运模块的设计与实现,涵盖以下核心内容:
- 整体架构:说明 Flash DMA 搬运系统的模块划分与数据流。
- 关键模块设计 :包括 DMA 控制器(
dma_ctrl.v)、Flash 控制器(flash_ctrl.v)、SPI 主控(spi_master.v)等核心模块的功能与接口。 - 数据搬运流程:从软核下发指令(方向、地址、目标地址、使能)到硬件完成整个搬运过程的详细时序。
- 仿真验证:介绍仿真环境搭建、测试用例设计以及如何通过仿真模型(如 M25P16)验证功能的正确性。
- 性能优势分析:对比软核处理与 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