Verilog 实现 SPI 器件 Flash 驱动文章目录Verilog 实现 SPI 器件 Flash 驱动一、器件说明本工程实际使用的核心功能用到指令简表二、项目设计三、代码实现控制写的模块 ctrl_w控制读模块 ctrl_r四、上板验证4.1 上板硬件与引脚约束五、总结与参考资料5.1 关键要点总结5.2 参考资料摘要本文基于 AWC_C4 DVK 开发板使用 Verilog 实现 FPGA 驱动 SPI Flash 芯片 M25P16 的读写。涵盖页编程、扇区擦除、读状态寄存器等核心指令通过自定义串口帧与同步 FIFO 解决速率匹配并给出 ctrl_w/ctrl_r 模块代码与上板验证。一、器件说明本次工程使用 SPI 串行 Flash 芯片M25P16‑VMN6TP该芯片为 AWC_C4 DVK 开发板板载 AS 主动串行配置 Flash。开发板硬件电路说明开发板板载 M25P16‑VMN6TP原生用于 FPGA 上电 AS 模式加载配置程序。引脚映射FPGA ↔ Flash信号名称M25P16引脚FPGA主控引脚描述I/O标准DATA02H2MOSIFPGA 输出→Flash 输入3.3VDCLK6H1SCLKSPI 主机时钟输出3.3VASDO5C1MISOFlash输出→FPGA输入3.3VNCSO1D2S_N片选低有效FPGA输出→Flash输入3.3V硬件电路细节W_N(引脚3)、HOLD_N(引脚7)开发板硬件直接上拉VCC_3V3写保护、HOLD保持功能硬件永久关闭软件无需处理VCC接3.3V搭配0.1uF去耦电容VSS接地DATA0、DCLK、ASDO串联20Ω电阻做阻抗匹配。存储架构M25P16存储容量16Mbit2MB32个Sector扇区每个扇区64KB每扇区分256Page页每页256字节地址范围0x000000 ~ 0x1FFFFF。参数特性扇区擦写寿命10万次数据保存20年仅支持SPIMode0CPOL0 CPHA0、Mode3CPOL1 CPHA1。写操作前必须擦除。擦除后存储单元全部为0xFF页编程只能将bit由1改写为0不能把0改写回1。本工程实际使用的核心功能舍弃未使用功能DP 深度掉电、唤醒 RES、写状态寄存器 WRSR、高速读 FAST_READ、写禁止 WRDI、整片擦除 BE。禁用整片擦除 BE板载 Flash 存储 FPGA 配置程序执行整片擦除会导致开发板无法启动。写使能 WREN(0x06)页编程、扇区擦除前必须发送 WREN执行完写/擦除硬件自动清除 WEL 写使能锁存位。页编程 PP(0x02)实现多字节写入发送 WREN → PP 指令 24bit 地址 待写数据拉高 S_N 片选后进入芯片内部tPP编程周期。限制单次最多写同一页连续 256 字节跨页会发生页回卷覆盖页起始数据。扇区擦除 SE(0xD8)擦除指定一整个 64KB 扇区擦除后扇区全部字节变为0xFF发送前必须先发 WREN。读状态寄存器 RDSR(0x05)只读取 WIP 忙位用来轮询判断芯片内部编程/擦除操作是否完成。WIP1芯片忙内部操作执行中WIP0操作完成可以下发下一条指令。读数据 READ(0x03)从指定24bit地址开始连续读取存储器数据。用到指令简表指令助记符指令码地址字节功能写使能WREN0x060打开写使能锁存读状态寄存器RDSR0x050读取 WIP 忙状态位读数据READ0x033读取 Flash 存储数据页编程PP0x023页写编程最多 256 字节扇区擦除SE0xD83擦除单个 64KB 扇区二、项目设计前置文章上一篇实现 FPGA 内部 SPI 主‑从闭环模拟。M25P16 需要 WREN 写使能、扇区擦除、页写、状态轮询、读数据。自定义串口下发帧格式0xFE data_len wr_lenth cmd addr0 addr1 addr2 data1 data2… 0xEE0xFE帧头0xEE帧尾data_len本次 SPI 交互总字节数wr_lenth主机发送字节数量cmdM25P16 指令码addr0‑addr224 bit 存储地址data1~dataN页编程待写入的数据速率匹配矛盾UART 串口下发字节慢SPI 收发速度快引入两组 8 bit×256 同步 FIFO 做速率缓冲。ctrl_w写控制模块解析串口帧指令、地址、待写数据存入写 FIFOSPI每完成1字节写done_w从FIFO读出下一个字节给到SPI主机。ctrl_r读控制模块SPI从Flash读出的数据写入读 FIFOSPI整组通信结束后逐字节送给tx模块回传上位机。依靠done_w / done_r / done_spi / done_tx握手信号完成跨模块时序对齐。工程模块分层rx模块复用UART回环工程输出data_rx、done_rxctrl_w写控制模块解析串口自定义帧FIFO缓存发送数据输出SPI主机控制信号spi_m SPI主机模块复用上篇博文输出SCLK、CS_N、MOSI接收MISOctrl_r读控制模块缓存SPI读出数据FIFO缓冲输出给txtx模块复用UART把读到的数据回传给PC串口助手top顶层完成全部子模块、FIFO IP 例化与信号互联。硬件提醒本开发板DCLK/DATA0/ASDO/NCSO为FPGA AS配置复用引脚Quartus需要正确配置避免AS模式占用IO。整体工程模块框图三、代码实现说明rx、tx、spi_mSPI 主机模块复用上一篇SPI主从模拟通信文章ctrl_w、ctrl_r 为本篇新增控制模块调用 Quartus 同步 FIFO IP8 bit‑256 深度。控制写的模块 ctrl_w//接收rx的并行数据单字节一个一个的来所以在接收的过程中以done_rx信号作为条件 //可以用状态机来划分各节点接收信号,该模块作用就是解析数据帧 //rx:fe **data_len wr_lenth cmd** addr0 addr1 addr2 data... ee/传输的帧结构 //引入 FIFO 匹配速率进行后续多字节写数据 //用到指令写使能、扇区擦除、页编程、读状态寄存器、读数据 module ctrl_w( input wire clk , input wire rst_n , input wire [7:0] data_rx ,//rx发送过来的单个指令 input wire done_rx ,//数据有效信号 input wire done_w , input wire done_spi , output wire start_spi , output wire [7:0] data_len , output wire [7:0] wr_lenth , output wire [7:0] data_i_spi ); //参数 //状态定义 parameter IDLE 1 ,//等待包头 LENTH 2 , W_LEN 3 , DATA 4 ,//包尾跳到STOP STOP 5 ; reg [3:0] state_c,state_n; //状态转移 wire IDLE_LENTH , LENTH_W_LEN , W_LEN_DATA , DATA_STOP , STOP_IDLE ; //内部信号 reg [7:0] data_len_r ; reg [7:0] wr_lenth_r ; //FIFO8*256-----存地址和数据先进先出模式 //写 reg wr_en ;//写使能 reg [7:0] data_fifo ;//FIFO 的输入数据 //读 wire rd_en ;//读使能 wire [7:0] q ;//读出的数据 //握手信号 wire empty ;//空信号 wire full ;//满信号 wire [7:0] usedw ;//数据量 //状态机第一段 always (posedge clk or negedge rst_n) begin if (rst_n 0) begin state_c IDLE; end else begin state_c state_n; end end //状态机第二段 always (*) begin if (rst_n 0) begin state_n IDLE; end else begin case (state_c) IDLE : state_n (IDLE_LENTH ) ? LENTH : state_c; LENTH : state_n (LENTH_W_LEN) ? W_LEN : state_c; W_LEN : state_n (W_LEN_DATA ) ? DATA : state_c; DATA : state_n (DATA_STOP ) ? STOP : state_c; STOP : state_n (STOP_IDLE ) ? IDLE : state_c; default : state_n IDLE; endcase end end //状态转移条件 assign IDLE_LENTH (state_c IDLE ) done_rx data_rx 8hfe; assign LENTH_W_LEN (state_c LENTH) done_rx; assign W_LEN_DATA (state_c W_LEN) done_rx; assign DATA_STOP (state_c DATA ) done_rx data_rx 8hee; assign STOP_IDLE (state_c STOP ) done_spi; //状态机第三段 always (posedge clk or negedge rst_n) begin if (rst_n 0) begin data_len_r 0; wr_lenth_r 0; wr_en 0; end else begin case (state_c) IDLE :; LENTH :begin if (done_rx) begin data_len_r data_rx; end end W_LEN :begin if (done_rx) begin wr_lenth_r data_rx; end end DATA :begin if (done_rx data_rx ! 8hee) begin wr_en 1; data_fifo data_rx; end else begin wr_en 0; data_fifo 0; end end STOP :; default :begin data_len_r 0; wr_lenth_r 0; wr_en 0; end endcase end end assign rd_en done_w; //输出信号 assign start_spi (DATA_STOP) ; assign data_len data_len_r; assign wr_lenth wr_lenth_r; assign data_i_spi q ; //同步 FIFO 8*256 fifo fifo_inst_w ( .aclr (~rst_n || done_spi),//保险 .clock (clk ), .data (data_fifo),//done_rx .rdreq (rd_en ),//done_w .wrreq (wr_en ),//done_rx .empty (empty ), .full (full ), .q (q ), .usedw (usedw ) ); endmodule控制读模块 ctrl_rmodule ctrl_r ( input wire clk , input wire rst_n , input wire [7:0] data_o_spi ,//spi的输出数据也就是读出的 input wire done_r ,//完成一次读数据的握手信号 input wire done_tx ,//读数据控制读使能的信号 input wire done_spi ,//数据寄存 FIFO 结束 output wire [7:0] data_out ,//tx的输入 output wire rd_done //tx的开始 ); //FIFO8*256 //写 FIFO wire wr_en ;//写使能 wire [7:0] data_fifo ;//FIFO 的输入数据 //读 FIFO wire rd_en ;//读使能 reg rd_enr ;//读使能寄存 wire [7:0] q ;//读出的数据 //握手信号 wire empty ;//空信号 wire full ;//满信号 wire [7:0] usedw ;//数据量 //写 FIFO assign wr_en done_r; assign data_fifo data_o_spi; //读 FIFO assign rd_en done_tx; assign data_out q; assign rd_done (done_spi || rd_enr) (empty 0); always (posedge clk or negedge rst_n) if(rst_n 0) begin rd_enr 0; end else begin rd_enr rd_en; end //同步 FIFO 8*256 fifo fifo_inst_r ( .aclr (~rst_n ), .clock (clk ), .data (data_fifo),//done_r .rdreq (rd_en ),//done_tx .wrreq (wr_en ),//done_r .empty (empty ), .full (full ), .q (q ), .usedw (usedw ) ); endmoduletop顶层模块、Testbench仿真模块省略顶层例化 rx、ctrl_w、spi_m、ctrl_r、tx 模块例化两组 FIFO IP 核。四、上板验证4.1 上板硬件与引脚约束复用开发板板载 M25P16引脚映射信号FPGA 引脚说明SPI_SCLKH1DCLKSPI_MOSIH2DATA0SPI_MISOC1ASDOSPI_CS_ND2NCSOS_N 低有效片选DCLK/DATA0/ASDO/NCSO 是 FPGA AS 配置复用引脚需要在 Quartus 中关闭 AS 配置占用才能当作普通 IO 使用硬件 W_N、HOLD_N 已经板载上拉 3.3V。五、总结与参考资料5.1 关键要点总结本工程基于 AWC_C4 DVK 开发板使用 Verilog 完整实现了 SPI 串行 Flash 芯片 M25P16 的读写驱动核心要点如下WREN 写使能是写操作的前置条件页编程、扇区擦除前必须发送 WREN(0x06) 打开写使能锁存执行完写/擦除后硬件自动清除 WEL 位下一次写操作前需重新发送。页编程存在 256 字节边界限制单次 PP(0x02) 最多写同一页连续 256 字节跨页会发生页回卷覆盖页起始数据因此跨页写入必须分多次下发。写操作前必须擦除Flash 存储单元擦除后为0xFF页编程只能将 bit 由 1 改写为 0不能把 0 改写回 1本工程禁用整片擦除 BE避免破坏板载 FPGA 配置程序。FIFO 解决速率匹配矛盾UART 串口下发字节慢、SPI 收发速度快引入两组 8 bit×256 同步 FIFO 做速率缓冲配合done_w / done_r / done_spi / done_tx握手信号完成跨模块时序对齐。状态机解析自定义串口帧ctrl_w 模块通过状态机解析0xFE data_len wr_lenth cmd addr data 0xEE帧结构将指令、地址、待写数据存入写 FIFO 后逐字节交给 SPI 主机。AS 配置复用引脚需在 Quartus 中配置DCLK/DATA0/ASDO/NCSO 为 FPGA AS 配置复用引脚需关闭 AS 配置占用后才能当作普通 IO 使用。5.2 参考资料M25P16 数据手册STMicroelectronics M25P16 Serial Flash Memory Datasheet16 MbitSPI 总线接口支持 Mode0/Mode3AWC_C4 DVK 开发板原理图板载 M25P16‑VMN6TP 的 AS 主动串行配置电路与引脚映射Quartus Prime 用户手册FPGA 引脚分配与 AS 配置复用引脚设置说明上一篇 SPI 主从模拟通信文章FPGA 内部 SPI 主‑从闭环模拟实现作为本工程rx、tx、spi_m模块的复用基础