17 SPI FLASH读写

SPI 协议简介

SPI 即 Serial Periphera linterface 的缩写,顾名思义就是串行外围设备接口,主要用于与FLASH、实时时钟、AD 转换器等外设模块的通信,它是一种高速的全双工同步的通信总线。
SPI 设备分为主设备和从设备,SPI 通信必须由主设备发起,主设备通过片选引脚(CSn)来选择对应的从设备,通过时钟引脚(SCK)向从设备提供时钟,通过数据输出引脚(MOSI)引脚向从设备发送数据,通过数据输入引脚(MISO)引脚来读取从设备返回的数据,如下是 SPI 的总线拓扑图(分别是1个主设备对应1个从设备和1个主设备对应多个从设备):
在这里插入图片描述
在这里插入图片描述

SPI 时序

通过 SPI 的时钟极性(CPOL)和相位( CPHA)可以组合出4种工作模式,如下表所示是对应的4种工作模式:
在这里插入图片描述

  1. 模式0(CPOL = 0, CPHA = 0)
    CPOL = 0:空闲时是低电平,第一个跳变沿是上升沿,第二个跳变沿是下降沿
    CPHA = 0:数据在第一个跳变沿(上升沿)采样
    在这里插入图片描述
  2. 模式1(CPOL = 0, CPHA = 1)
    CPOL = 0:空闲时是低电平,第一个跳变沿是上升沿,第二个跳变沿是下降沿
    CPHA = 1:数据在第二个跳变沿(下降沿)采样
    在这里插入图片描述
  3. 模式2(CPOL = 1, CPHA = 0)
    CPOL = 1:空闲时是高电平,第一个跳变沿是下降沿,第二个跳变沿是上升沿
    CPHA = 0:数据在第一个跳变沿(下降沿)采样
    在这里插入图片描述
  4. 模式3(CPOL = 1, CPHA = 1)
    CPOL = 1:空闲时是高电平,第一个跳变沿是下降沿,第二个跳变沿是上升沿
    CPHA = 1:数据在第二个跳变沿(上升沿)采样
    在这里插入图片描述

FLASH 简介

实验使用的 FLASH 芯片型号为W25Q128,它是华邦公司推出的一款的 NOR FIash 芯片,其存储空间为128 Mbit,相当于16MB,支持 Standard SPI、Dual SPI 和 Quad SPI 三种 SPI 协议通信方式,最大传输数据速率可达104MHZ,如下是芯片的引脚示意图:
在这里插入图片描述
在这里插入图片描述

存储结构

整个存储阵列被分成被为 256 给我块,每个块又分为 16 个扇区,每个扇区由分为 16 个页,每页包含 256 个字节。
在这里插入图片描述

操作指令

在这里插入图片描述在这里插入图片描述
FLASH 操作指令有很多,其中常用的有读ID指令(0x9F)、写使能指令(0x06)、扇区擦除指令(每次擦除4KB,0x20)、全擦除指令(0xC7)、读指令(0x03)、写指令(又叫页编程,0x02)、读状态寄存器1指令(0x05)。

FLASH 操作时序

  1. 读ID指令
    在操作 FLASH 之前应读取其ID,校验 FLASH 型号,如下是读 ID 的时序:
    在这里插入图片描述
  2. 写使能指令
    在进行擦除、写入操作前需要先发送写使能指令,其时序如下:
    在这里插入图片描述
  3. 扇区擦除指令
    W25Q128在写数据时只能将1修改为0,所以在写入新数据之前必须要进行擦除操作,将FLASH中的存储单元全部设置为1,如下是扇区擦除时序:
    在这里插入图片描述
  4. 全擦除指令
    除了扇区擦除指令外,还有全擦除指令,其时序如下:
    在这里插入图片描述
  5. 读指令
    读取 FLASH 一次可以读取多个字节,其时序如下:
    在这里插入图片描述
  6. 写指令
    向 FLASH 写入数据时不能跨页(页大小256B),若需要写入多页或者写入数据跨页则需要分多次写入,如下是写入数据的时序:
    在这里插入图片描述
  7. 读状态寄存器1指令
    在进行擦除、写入操作后需要轮询状态寄存器0的bit0,以检查擦除或写入操作是否结束(全擦除指令耗时很长),如下是读状态寄存器的时序:
    在这里插入图片描述

硬件设计

FLASH 芯片的硬件原理图较为简单,芯片本身就 8 个引脚,其原理图如下:
在这里插入图片描述

代码编写

代码一共分为3个模块,分别是 SPI 驱动模块、FLASH 驱动模块、FLASH 读写测试模块,其功能如下:
SPI 驱动模块;提供 SPI 总线收发数据的功能。
FLASH 驱动模块;基于 SPI 驱动模块实现 FLASH 的一些基本操作,如读 ID、写使能、写、擦除、读、读状态寄存器等。
FLASH 读写测试模块;利用 FLASH 驱动模块提供的 FLASH 基本操作进行 FLASH 读ID、擦除、写、读等测试,测试过程中状态 LED 常灭,测试出错状态 LED闪烁,测试完成状态 LED 常亮。

SPI 驱动模块

module spi_driver #(parameter SPI_CS_MAX = 1,					//片选数量parameter SPI_BITS = 8,						//SPI位宽parameter SPI_CLK_PERIOD = 4,				//SPI时钟周期,以系统时钟为参考,最小为4parameter SPI_MODE = 0,						//SPI模式parameter ALMOST_DONE_ADVANCE = 0,			//即将传输完成信号提前输出时间,小于SPI_CLK_PERIOD-1parameter ALMOST_IDLE_ADVANCE = 0			//即将空闲信号提前输出时间,小于SPI_CLK_PERIOD-1
)
(input sys_rst_n,							//系统复位input sys_clk,								//系统时钟input [SPI_BITS-1:0] tx_data,				//需要发送的数据input tr_start,								//启动传输output reg [SPI_BITS-1:0] rx_data,			//接收到的数据output reg tr_done,							//传输完成output reg almost_tr_done,					//传输即将完成output tr_idle,								//SPI空闲output reg almost_tr_idle,					//SPI即将空闲input [SPI_CS_MAX-1:0] sel_cs,				//片选设置output spi_clk,								//SPI时钟output spi_mosi,							//SPI MOSIinput spi_miso,								//SPI MISOoutput [SPI_CS_MAX-1:0] spi_cs				//SPI片选
);//SPI时钟周期,只能是偶数分频
localparam CLK_PERIOD = (SPI_CLK_PERIOD / 2 * 2);//spi传输忙标志
reg tr_busy;//SPI时钟周期计数器,按SPI时钟周期进行计数
reg [15:0] clk_period_count;//传输bit计数
reg [7:0] bit_cnt;//发送移位寄存器
reg [SPI_BITS-1:0] tx_shift_reg;
//接收移位寄存器
reg [SPI_BITS-1:0] rx_shift_reg;//空闲标志
assign tr_idle = ~tr_busy;//启动SPI传输
always @(posedge sys_clk) beginif(!sys_rst_n)tr_busy <= 1'b0;else if((tr_start == 1'b1) && (tr_busy == 1'b0))tr_busy <= 1'b1;else if((tr_busy == 1'b1) && (bit_cnt == (SPI_BITS - 1)) && (clk_period_count == (CLK_PERIOD - 1)))tr_busy <= 1'b0;
end//SPI即将空闲
always @(posedge sys_clk) beginif(!sys_rst_n)almost_tr_idle <= 1'b1;else if((tr_start == 1'b1) && (almost_tr_idle == 1'b1))almost_tr_idle <= 1'b0;else if((almost_tr_idle == 1'b0) && (bit_cnt == (SPI_BITS - 1)) && (clk_period_count == (CLK_PERIOD - 1 - ALMOST_IDLE_ADVANCE)))almost_tr_idle <= 1'b1;
end//按SPI时钟周期进行计数
always @(posedge sys_clk) beginif(!sys_rst_n)clk_period_count <= 0;else if(tr_busy == 1'b1)if(clk_period_count < (CLK_PERIOD - 1))clk_period_count <= clk_period_count + 1;elseclk_period_count <= 0;elseclk_period_count <= 0;
end//进行传输计数
always @(posedge sys_clk) beginif(!sys_rst_n)bit_cnt <= 0;else if(tr_busy == 1'b1) beginif(clk_period_count == (CLK_PERIOD - 1)) beginif(bit_cnt < (SPI_BITS - 1))bit_cnt <= bit_cnt + 1;endendelsebit_cnt <= 0;
end//输出接收到的数据,接收完最后1bit时输出
always @(posedge sys_clk) beginif(!sys_rst_n) beginrx_data <= 0;tr_done <= 0;endelse if((tr_busy == 1'b1) && (bit_cnt == (SPI_BITS - 1)) && (clk_period_count == (CLK_PERIOD - 1))) beginrx_data <= rx_shift_reg;tr_done <= 1;endelsetr_done <= 0;
end//输出传输完成预告
always @(posedge sys_clk) beginif(!sys_rst_n)almost_tr_done <= 0;else if((tr_busy == 1'b1) && (bit_cnt == (SPI_BITS - 1)) && (clk_period_count == (CLK_PERIOD - 1 - ALMOST_DONE_ADVANCE)))almost_tr_done <= 1;elsealmost_tr_done <= 0;
endgenerateif(SPI_MODE == 0) begin//输出SPI时钟,模式0//时钟默认为低电平,工作时前半段为低电平,后半段为高电平assign spi_clk = ((tr_busy == 1'b1) && (clk_period_count > (CLK_PERIOD / 2 - 1))) ? 1'b1 : 1'b0;endelse if(SPI_MODE == 1) begin//输出SPI时钟,模式1//时钟默认为低电平,工作时前半段为高电平,后半段为低电平assign spi_clk = ((tr_busy == 1'b1) && (clk_period_count <= (CLK_PERIOD / 2 - 1))) ? 1'b1 : 1'b0;endelse if(SPI_MODE == 2) begin//输出SPI时钟,模式2//时钟默认为高电平,工作时前半段为高电平,后半段为低电平assign spi_clk = ((tr_busy == 1'b1) && (clk_period_count > (CLK_PERIOD / 2 - 1))) ? 1'b0 : 1'b1;endelse begin//输出SPI时钟,模式3//时钟默认为高电平,工作时前半段为高电平,后半段为低电平assign spi_clk = ((tr_busy == 1'b1) && (clk_period_count <= (CLK_PERIOD / 2 - 1))) ? 1'b0 : 1'b1;end
endgenerate//启动时锁定数据,随后进行发送数据移位操作
always @(posedge sys_clk) beginif(!sys_rst_n)tx_shift_reg <= (1 << SPI_BITS) - 1;else if((tr_start == 1'b1) && (tr_busy == 1'b0))tx_shift_reg <= tx_data;else if((tr_busy == 1'b1) && (bit_cnt < (SPI_BITS - 1)) && (clk_period_count == (CLK_PERIOD - 1)))tx_shift_reg <= {tx_shift_reg[6:0], tx_shift_reg[7]};
end
//输出SPI MOSI
assign spi_mosi = tx_shift_reg[7];//采样SPI MOSI
//在时钟周期的3/4处采样
always @(posedge sys_clk) beginif(!sys_rst_n)rx_shift_reg <= 0;else if(tr_busy == 1'b1) beginif(clk_period_count == (CLK_PERIOD - CLK_PERIOD / 4 - 1))rx_shift_reg = {rx_shift_reg[6:0], spi_miso};endelserx_shift_reg <= 0;
end//输出片选信号
assign spi_cs = sel_cs;endmodule

FLASH 驱动模块

module flash_driver  #(parameter SPI_CLK_PERIOD = 4,parameter SPI_CS_DELAY = 500,parameter DATA_REQ_ADVANCE = 1
)
(input sys_rst_n,input sys_clk,input flash_start,input [7:0] flash_cmd,input [24:0] flash_addr,input [8:0] wr_data_len,input [7:0] wr_data,output wr_data_req,input [8:0] rd_data_len,output [7:0] rd_data,output rd_data_flag,output reg [23:0] flash_id,output reg flash_id_flag,output reg [7:0] flash_sr_reg,output reg flash_sr_reg_flag,output flash_idle,output spi_clk,output spi_mosi,input spi_miso,output spi_cs
);//状态机的状态
localparam IDLE_STATE = 8'h01;			//空闲状态
localparam RDID_STATE = 8'h02;			//读FLASH ID状态
localparam WREN_STATE = 8'h04;			//写使能状态
localparam SSE_STATE = 8'h08;			//子扇区擦除状态
localparam BE_STATE = 8'h10;			//全擦除
localparam READ_STATE = 8'h20;			//读状态
localparam PP_STATE = 8'h40;			//写状态(页编程)
localparam RDSR_STATE = 8'h80;			//读状态寄存器状态//指令集
localparam RDID_CMD = 8'h9f;			//读ID指令
localparam WREN_CMD = 8'h06;			//写使能指令
localparam SSE_CMD = 8'h20;				//子扇区擦除指令
localparam BE_CMD = 8'hc7;				//全擦除指令
localparam READ_CMD = 8'h0b;			//读指令
localparam PP_CMD = 8'h02;				//写指令(页编程)
localparam RDSR_CMD = 8'h05;			//读状态寄存器指令//传输计数
reg [15:0] tx_count;
reg [15:0] rx_count;//当前状态
reg [7:0] current_state;
//下一刻的状态
reg [7:0] next_state;
//当前状态结束标志,切换到下一个状态
reg state_done;//片选前延时
reg [16:0] cs_front_delay_count;
//片选后延时
reg [16:0] cs_back_delay_count;
//指令操作延时,部分命令发送完成后需要延时
reg [16:0] cmd_back_ddelay_count;//SPI需要发送的数据
reg [7:0] spi_tx_data;
//启动SPI发送
reg spi_tr_start;
//SPI片选控制
reg spi_ctrl_cs;//SPI接收到的数据
wire [7:0] spi_rx_data;
//SPI传输完成
wire spi_tr_done;
//SPI即将传输完成
wire spi_almost_tr_done;//SPI空闲
wire spi_tr_idle;
//SPI即将空闲
wire spi_almost_tr_idle;//状态跳转
always @(posedge sys_clk)beginif(!sys_rst_n)current_state <= IDLE_STATE;elsecurrent_state <= next_state;
end//根据当前状态确定下一刻状态
always @(*)begincase(current_state)IDLE_STATE: beginif((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == RDID_CMD))next_state = RDID_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == WREN_CMD))next_state = WREN_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == SSE_CMD))next_state = SSE_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == BE_CMD))next_state = BE_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == READ_CMD))next_state = READ_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == PP_CMD))next_state = PP_STATE;else if((state_done == 1'b0) && (flash_start == 1'b1) && (flash_cmd == RDSR_CMD))next_state = RDSR_STATE;elsenext_state = IDLE_STATE;endRDID_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = RDID_STATE;endWREN_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = WREN_STATE;endSSE_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = SSE_STATE;endBE_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = BE_STATE;endREAD_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = READ_STATE;endPP_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = PP_STATE;endRDSR_STATE: beginif(state_done == 1'b1)next_state = IDLE_STATE;elsenext_state = RDSR_STATE;enddefault:next_state = IDLE_STATE;endcase
end//空闲标志输出
assign flash_idle = ((current_state == IDLE_STATE) && (state_done == 1'b0)) ? 1'b1 : 1'b0;//进行计数器计数,输出延时信号
always @(posedge sys_clk) beginif(!sys_rst_n) begincs_front_delay_count <= 0;cs_back_delay_count <= 0;cmd_back_ddelay_count <= 0;state_done <= 1'b0;endelse begincase(current_state)IDLE_STATE: begincs_front_delay_count <= 0;cs_back_delay_count <= 0;cmd_back_ddelay_count <= 0;state_done <= 1'b0;endRDID_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= 4) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endWREN_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= 1) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endSSE_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= 4) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endBE_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= 1) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endREAD_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= (4 + rd_data_len)) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endPP_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= (4 + wr_data_len)) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;endRDSR_STATE: begin//片选拉低后延时计数if(cs_front_delay_count < (SPI_CS_DELAY - 1))cs_front_delay_count <= cs_front_delay_count + 1;//片选拉高前延时计数if(rx_count >= 2) beginif(cs_back_delay_count < (SPI_CS_DELAY - 1))cs_back_delay_count <= cs_back_delay_count + 1;end//指令操作延时if(cs_back_delay_count >= (SPI_CS_DELAY - 1)) beginif(cmd_back_ddelay_count < (500 - 1))cmd_back_ddelay_count <= cmd_back_ddelay_count + 1;end//状态结束判断if(cmd_back_ddelay_count >= (500 - 1))state_done <= 1'b1;enddefault: begincs_front_delay_count <= 0;cs_back_delay_count <= 0;cmd_back_ddelay_count <= 0;state_done <= 1'b0;endendcaseend
end//控制SPI传输
always @(posedge sys_clk)beginif(!sys_rst_n) beginspi_ctrl_cs <= 1'b1;spi_tx_data <= 8'b0;spi_tr_start <= 1'b0;tx_count <= 16'b0;endelse begincase(current_state)IDLE_STATE: beginspi_ctrl_cs <= 1'b1;spi_tx_data <= 8'b0;spi_tr_start <= 1'b0;tx_count <= 16'b0;endRDID_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h9f;spi_tr_start <= 1'b1;endelse if(tx_count < 4) beginspi_tx_data <= 8'hFF;spi_tr_start <= 1'b1;end//发送计数if(tx_count < 4)tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;endWREN_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h06;spi_tr_start <= 1'b1;end//发送计数if(tx_count < 1)tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;endSSE_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h20;spi_tr_start <= 1'b1;endelse if(tx_count == 1) beginspi_tx_data <= flash_addr[23:16];spi_tr_start <= 1'b1;endelse if(tx_count == 2) beginspi_tx_data <= flash_addr[15:8];spi_tr_start <= 1'b1;endelse if(tx_count == 3) beginspi_tx_data <= flash_addr[7:0];spi_tr_start <= 1'b1;end//发送计数if(tx_count < 4)tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;endBE_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'hc7;spi_tr_start <= 1'b1;end//发送计数if(tx_count < 1)tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;endREAD_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h03;spi_tr_start <= 1'b1;endelse if(tx_count == 1) beginspi_tx_data <= flash_addr[23:16];spi_tr_start <= 1'b1;endelse if(tx_count == 2) beginspi_tx_data <= flash_addr[15:8];spi_tr_start <= 1'b1;endelse if(tx_count == 3) beginspi_tx_data <= flash_addr[7:0];spi_tr_start <= 1'b1;endelse if(tx_count < (4 + rd_data_len)) beginspi_tx_data <= 8'hFF;spi_tr_start <= 1'b1;end//发送计数if(tx_count < (4 + rd_data_len))tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;endPP_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h02;spi_tr_start <= 1'b1;endelse if(tx_count == 1) beginspi_tx_data <= flash_addr[23:16];spi_tr_start <= 1'b1;endelse if(tx_count == 2) beginspi_tx_data <= flash_addr[15:8];spi_tr_start <= 1'b1;endelse if(tx_count == 3) beginspi_tx_data <= flash_addr[7:0];spi_tr_start <= 1'b1;endelse if(tx_count < (4 + wr_data_len)) beginspi_tx_data <= wr_data;spi_tr_start <= 1'b1;end//发送计数if(tx_count < (4 + wr_data_len))tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endendRDSR_STATE: begin//控制片选if(cs_back_delay_count >= (SPI_CS_DELAY - 1))spi_ctrl_cs <= 1'b1;elsespi_ctrl_cs <= 1'b0;//控制传输if(cs_front_delay_count >= (SPI_CS_DELAY - 1)) beginif((spi_almost_tr_idle == 1'b1) && (spi_tr_start == 1'b0)) begin//启动spi传输if(tx_count == 0) beginspi_tx_data <= 8'h05;spi_tr_start <= 1'b1;endelse if(tx_count < 2) beginspi_tx_data <= 8'hFF;spi_tr_start <= 1'b1;end//发送计数if(tx_count < 2)tx_count <= tx_count + 16'b1;endelse if(spi_almost_tr_idle == 1'b0)spi_tr_start <= 1'b0;endelsespi_tr_start <= 1'b0;enddefault: beginspi_ctrl_cs <= 1'b1;spi_tx_data <= 8'b0;spi_tr_start <= 1'b0;endendcaseend
end//处理读取ID和状态寄存器
always @(posedge sys_clk)beginif(!sys_rst_n) beginflash_id <= 24'b0;flash_id_flag <= 1'b0;flash_sr_reg <= 8'b0;flash_sr_reg_flag <= 1'b0;endelse begincase(current_state)RDID_STATE: beginif(spi_tr_done == 1'b1) beginif(rx_count == 1)flash_id[23:16] <= spi_rx_data;else if(rx_count == 2)flash_id[15:8] <= spi_rx_data;else if(rx_count == 3)flash_id[7:0] <= spi_rx_data;if(rx_count == 3)flash_id_flag <= 1'b1;elseflash_id_flag <= 1'b0;endelseflash_id_flag <= 1'b0;endRDSR_STATE: beginif(spi_tr_done == 1'b1) beginif(rx_count == 1)flash_sr_reg <= spi_rx_data;if(rx_count == 1)flash_sr_reg_flag <= 1'b1;elseflash_sr_reg_flag <= 1'b0;endelseflash_sr_reg_flag <= 1'b0;enddefault: beginflash_id_flag <= 1'b0;flash_sr_reg_flag <= 1'b0;endendcaseend
end//SPI控制器接收计数
always @(posedge sys_clk)beginif(!sys_rst_n)rx_count <= 0;else if((current_state != IDLE_STATE) && (spi_tr_done == 1'b1))rx_count <= rx_count + 1;else if(current_state == IDLE_STATE)rx_count <= 0;
end//flash数据输出
assign rd_data = ((current_state == READ_STATE) &&(rx_count >= 4)) ? spi_rx_data : 0'b0;
assign rd_data_flag = ((current_state == READ_STATE) &&(rx_count >= 4)) ? spi_tr_done : 0'b0;//数据请求输出
assign wr_data_req = ((current_state == PP_STATE) &&(tx_count >= 4) && (tx_count < (4 + wr_data_len))) ? spi_almost_tr_done : 0'b0;spi_driver #(.SPI_CS_MAX(1),.SPI_BITS(8),.SPI_CLK_PERIOD(4),.SPI_MODE(0),//因为将外部数据转发到spi_driver需要一个时钟,所以这里加1个时钟周期.ALMOST_DONE_ADVANCE(DATA_REQ_ADVANCE + 1),//此模块准备数据需要一个时钟周期,所以为1.ALMOST_IDLE_ADVANCE(1)
)
spi_driver_inst0(.sys_rst_n(sys_rst_n),.sys_clk(sys_clk),.tx_data(spi_tx_data),.tr_start(spi_tr_start),.rx_data(spi_rx_data),.tr_done(spi_tr_done),.almost_tr_done(spi_almost_tr_done),.tr_idle(spi_tr_idle),.almost_tr_idle(spi_almost_tr_idle),.sel_cs(spi_ctrl_cs),.spi_clk(spi_clk),.spi_mosi(spi_mosi),.spi_miso(spi_miso),.spi_cs(spi_cs)
);endmodule

FLASH 读写测试模块

module spi_flash_rw_test #(parameter ALARM_LED_PERIOD = 25'd25_000_000
)
(input sys_rst_n,input sys_clk,output spi_mosi,input spi_miso,output spi_sclk,output spi_cs,output reg alarm_led
);//flash指令集
localparam RDID_CMD = 8'h9f;			//读ID指令
localparam WREN_CMD = 8'h06;			//写使能指令
localparam SSE_CMD = 8'h20;				//子扇区擦除指令
localparam BE_CMD = 8'hc7;				//全擦除指令
localparam READ_CMD = 8'h0b;			//读指令
localparam PP_CMD = 8'h02;				//写指令(页编程)
localparam RDSR_CMD = 8'h05;			//读状态寄存器指令//状态机的状态
localparam IDLE_STATE = 16'h0001;			//空闲状态localparam RDID_STATE = 16'h0002;			//读FLASH ID状态localparam BE_WREN_STATE = 16'h0004;		//全擦除前写使能状态
localparam BE_STATE = 16'h0008;				//全擦除状态
localparam BE_WAIT_STATE = 16'h0010;		//全擦除后等待flash空闲状态localparam PP_WREN_STATE = 16'h0020;		//页编程前写使能状态
localparam PP_STATE = 16'h0040;				//写状态(页编程)
localparam PP_WAIT_STATE = 16'h0080;		//页编程后等待flash空闲状态localparam READ1_STATE = 16'h0100;			//第一次读状态,验证写入数据是否成功localparam SSE_WREN_STATE = 16'h0200;		//子扇区擦除前写使能状态
localparam SSE_STATE = 16'h0400;			//子扇区擦除状态
localparam SSE_WAIT_STATE = 16'h0800;		//子扇区擦除后等待flash空闲状态localparam READ2_STATE = 16'h1000;			//第二次读状态,验证擦除是否成功//错误指示
reg error_flag;
//警示LED闪烁计数器
reg [31:0] led_count;//flash擦除或写入忙标志
reg flash_ew_busy;//写入计数
reg [8:0] write_count;//读取计数
reg [8:0] read_count;//当前状态
reg [15:0] current_state;
//下一刻的状态
reg [15:0] next_state;
//对应状态结束标志,应切换到下一个状态,一个bit对应一个状态
reg [15:0] state_done;
//状态启动标志
reg [15:0] state_start;//flash操作开始信号
reg flash_start;
//flash操作命令
reg [7:0] flash_cmd;
//flash操作地址
reg [24:0] flash_addr;//flash写入长度
reg [8:0] wr_data_len;
//写入flash的数据,需要在收到wr_data_req后更新
reg [7:0] wr_data;
//flash写入数据请求
wire wr_data_req;//flash读长度
reg [8:0]rd_data_len;
//flash中读取到的数据
wire [7:0] rd_data;
//flash读取数据有效标志
wire rd_data_flag;//flash ID
wire [23:0] flash_id;
//flash ID有效标志
wire flash_id_flag;//flash 状态寄存器0
wire [7:0] flash_sr_reg;
//flash 状态寄存器0有效标志
wire flash_sr_reg_flag;//flash空闲标志
wire flash_driver_idle;//根据错误标志控制led闪烁或常亮
//操作未完成熄灭
//操作完成常亮
//发生错误时闪烁
always @(posedge sys_clk) beginif(!sys_rst_n) beginalarm_led <= 1'b0;led_count <= 32'b0;endelse if(error_flag == 1'b1) beginif(led_count >= (ALARM_LED_PERIOD - 1)) beginalarm_led <= ~alarm_led;led_count <= 32'b0;endelseled_count <= led_count + 32'b1;endelse if((current_state == IDLE_STATE) && (state_done != 16'h0)) beginalarm_led <= 1'b1;led_count <= 32'b0;endelse begin alarm_led <= 1'b0;led_count <= 32'b0;end
end//状态跳转
always @(posedge sys_clk)beginif(!sys_rst_n)current_state <= IDLE_STATE;elsecurrent_state <= next_state;
end//根据当前状态确定下一刻状态
always @(*)begincase(current_state)IDLE_STATE: beginif((state_done == 16'h0) && (error_flag == 1'b0))next_state = RDID_STATE;elsenext_state = IDLE_STATE;endRDID_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & RDID_STATE)next_state = BE_WREN_STATE;elsenext_state = RDID_STATE;endBE_WREN_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & BE_WREN_STATE)next_state = BE_STATE;elsenext_state = BE_WREN_STATE;endBE_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & BE_STATE)next_state = BE_WAIT_STATE;elsenext_state = BE_STATE;endBE_WAIT_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & BE_WAIT_STATE)next_state = PP_WREN_STATE;elsenext_state = BE_WAIT_STATE;endPP_WREN_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & PP_WREN_STATE)next_state = PP_STATE;elsenext_state = PP_WREN_STATE;endPP_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & PP_STATE)next_state = PP_WAIT_STATE;elsenext_state = PP_STATE;endPP_WAIT_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & PP_WAIT_STATE)next_state = READ1_STATE;elsenext_state = PP_WAIT_STATE;endREAD1_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & READ1_STATE)next_state = SSE_WREN_STATE;elsenext_state = READ1_STATE;endSSE_WREN_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & SSE_WREN_STATE)next_state = SSE_STATE;elsenext_state = SSE_WREN_STATE;endSSE_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & SSE_STATE)next_state = SSE_WAIT_STATE;elsenext_state = SSE_STATE;endSSE_WAIT_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & SSE_WAIT_STATE)next_state = READ2_STATE;elsenext_state = SSE_WAIT_STATE;endREAD2_STATE: beginif(error_flag == 1'b1)next_state = IDLE_STATE;else if(state_done & READ2_STATE)next_state = IDLE_STATE;elsenext_state = READ2_STATE;enddefault:next_state = IDLE_STATE;endcase
end//控制flash擦除、读写等
always @(posedge sys_clk)beginif(!sys_rst_n) beginflash_start <= 1'b0;flash_cmd <= 8'h0;flash_addr <= 24'h0;wr_data_len <= 9'h0;rd_data_len <= 9'h0;state_done <= 16'h0;state_start <= 16'h0;endelse begincase(current_state)RDID_STATE: begin//启动读ID操作if((flash_driver_idle == 1'b1) && (!(state_start & RDID_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= RDID_CMD;state_start <= state_start | RDID_STATE;end//读ID操作完成if((flash_driver_idle == 1'b1) && (state_start & RDID_STATE) && (flash_start == 1'b0)) beginif(!(state_done & RDID_STATE))state_done <= state_done | RDID_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endBE_WREN_STATE: begin//启动写使能操作if((flash_driver_idle == 1'b1) && (!(state_start & BE_WREN_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= WREN_CMD;state_start <= state_start | BE_WREN_STATE;end//写使能操作完成if((flash_driver_idle == 1'b1) && (state_start & BE_WREN_STATE) && (flash_start == 1'b0)) beginif(!(state_done & BE_WREN_STATE))state_done <= state_done | BE_WREN_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endBE_STATE: begin//启动全擦除操作if((flash_driver_idle == 1'b1) && (!(state_start & BE_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= BE_CMD;state_start <= state_start | BE_STATE;end//全擦除操作完成if((flash_driver_idle == 1'b1) && (state_start & BE_STATE) && (flash_start == 1'b0)) beginif(!(state_done & BE_STATE))state_done <= state_done | BE_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endBE_WAIT_STATE: begin//启动读状态寄存器操作if((flash_driver_idle == 1'b1) && (!(state_start & BE_WAIT_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= RDSR_CMD;state_start <= state_start | BE_WAIT_STATE;end//读状态寄存器操作完成if((flash_driver_idle == 1'b1) && (state_start & BE_WAIT_STATE) && (flash_start == 1'b0)) begin//检查擦除是否结束,若未结束则再次轮询状态寄存器if(flash_ew_busy == 1'b1)state_start <= state_start & ~BE_WAIT_STATE;else if(!(state_done & BE_WAIT_STATE))state_done <= state_done | BE_WAIT_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endPP_WREN_STATE: begin//启动写使能操作if((flash_driver_idle == 1'b1) && (!(state_start & PP_WREN_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= WREN_CMD;state_start <= state_start | PP_WREN_STATE;end//写使能操作完成if((flash_driver_idle == 1'b1) && (state_start & PP_WREN_STATE) && (flash_start == 1'b0)) beginif(!(state_done & PP_WREN_STATE))state_done <= state_done | PP_WREN_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endPP_STATE: begin//启动写flash操作if((flash_driver_idle == 1'b1) && (!(state_start & PP_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= PP_CMD;flash_addr <= 24'h1000;wr_data_len <= 9'd256;state_start <= state_start | PP_STATE;end//写操作完成if((flash_driver_idle == 1'b1) && (state_start & PP_STATE) && (flash_start == 1'b0)) beginif(!(state_done & PP_STATE))state_done <= state_done | PP_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endPP_WAIT_STATE: begin//启动读状态寄存器操作if((flash_driver_idle == 1'b1) && (!(state_start & PP_WAIT_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= RDSR_CMD;state_start <= state_start | PP_WAIT_STATE;end//读状态寄存器操作完成if((flash_driver_idle == 1'b1) && (state_start & PP_WAIT_STATE) && (flash_start == 1'b0)) begin//检查擦除是否结束,若未结束则再次轮询状态寄存器if(flash_ew_busy == 1'b1)state_start <= state_start & ~PP_WAIT_STATE;else if(!(state_done & PP_WAIT_STATE))state_done <= state_done | PP_WAIT_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endREAD1_STATE: begin//启动读flash操作if((flash_driver_idle == 1'b1) && (!(state_start & READ1_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= READ_CMD;flash_addr <= 24'h1000;rd_data_len <= 9'd256;state_start <= state_start | READ1_STATE;end//读操作完成if((flash_driver_idle == 1'b1) && (state_start & READ1_STATE) && (flash_start == 1'b0)) beginif(!(state_done & READ1_STATE))state_done <= state_done | READ1_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endSSE_WREN_STATE: begin//启动写使能操作if((flash_driver_idle == 1'b1) && (!(state_start & SSE_WREN_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= WREN_CMD;state_start <= state_start | SSE_WREN_STATE;end//写使能操作完成if((flash_driver_idle == 1'b1) && (state_start & SSE_WREN_STATE) && (flash_start == 1'b0)) beginif(!(state_done & SSE_WREN_STATE))state_done <= state_done | SSE_WREN_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endSSE_STATE: begin//启动子扇区擦除操作if((flash_driver_idle == 1'b1) && (!(state_start & SSE_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= SSE_CMD;flash_addr <= 24'h1000;state_start <= state_start | SSE_STATE;end//子扇区擦除操作完成if((flash_driver_idle == 1'b1) && (state_start & SSE_STATE) && (flash_start == 1'b0)) beginif(!(state_done & SSE_STATE))state_done <= state_done | SSE_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endSSE_WAIT_STATE: begin//启动读状态寄存器操作if((flash_driver_idle == 1'b1) && (!(state_start & SSE_WAIT_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= RDSR_CMD;state_start <= state_start | SSE_WAIT_STATE;end//读状态寄存器操作完成if((flash_driver_idle == 1'b1) && (state_start & SSE_WAIT_STATE) && (flash_start == 1'b0)) begin//检查擦除是否结束,若未结束则再次轮询状态寄存器if(flash_ew_busy == 1'b1)state_start <= state_start & ~SSE_WAIT_STATE;else if(!(state_done & SSE_WAIT_STATE))state_done <= state_done | SSE_WAIT_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;endREAD2_STATE: begin//启动读flash操作if((flash_driver_idle == 1'b1) && (!(state_start & READ2_STATE)) && (flash_start == 1'b0)) beginflash_start <= 1'b1;flash_cmd <= READ_CMD;flash_addr <= 24'h1000;rd_data_len <= 9'd256;state_start <= state_start | READ2_STATE;end//读操作完成if((flash_driver_idle == 1'b1) && (state_start & READ2_STATE) && (flash_start == 1'b0)) beginif(!(state_done & READ2_STATE))state_done <= state_done | READ2_STATE;end//flash操作已经启动,复位启动标志if((flash_driver_idle == 1'b0) && (flash_start == 1'b1))flash_start <= 1'b0;enddefault: beginflash_start <= 1'b0;flash_cmd <= 8'h0;flash_addr <= 24'h0;endendcaseend
end//处理flash driver返回的数据,包括ID、读取数据、状态寄存器
always @(posedge sys_clk)beginif(!sys_rst_n) beginerror_flag <= 1'b0;flash_ew_busy <= 1'h0;read_count <= 9'h0;endelse begincase(current_state)RDID_STATE: begin//检查ID是否正确if(flash_id_flag == 1'b1) beginif(flash_id != 24'hef4018)error_flag <= 1'b1;endendBE_WAIT_STATE: begin//检查擦除是否完成if(flash_sr_reg_flag == 1'b1)flash_ew_busy <= flash_sr_reg[0];endPP_WAIT_STATE: begin//检查写是否完成if(flash_sr_reg_flag == 1'b1)flash_ew_busy <= flash_sr_reg[0];endREAD1_STATE: beginif(rd_data_flag == 1'b1) begin//检查接收的数据if(rd_data != read_count[7:0])error_flag <= 1'b1;//接收计数read_count <= read_count + 9'h1;endendSSE_WAIT_STATE: begin//检查子扇区擦除是否完成if(flash_sr_reg_flag == 1'b1)flash_ew_busy <= flash_sr_reg[0];endREAD2_STATE: beginif(rd_data_flag == 1'b1) begin//检查接收的数据if(rd_data != 8'hff)error_flag <= 1'b1;endenddefault: beginread_count <= 9'h0;endendcaseend
end//生成写入的数据
always @(posedge sys_clk)beginif(!sys_rst_n) beginwr_data <= 8'h0;write_count <= 9'h0;endelse if(current_state == PP_STATE) beginif(wr_data_req == 1'b1) begin//生成写入数据wr_data <= write_count[7:0];//写入计数write_count <= write_count + 9'h1;endendelse beginwr_data <= 0;write_count <= 0;end
endflash_driver #(.SPI_CLK_PERIOD(4),.SPI_CS_DELAY(500),.DATA_REQ_ADVANCE(1)
)
flash_driver_inst0(.sys_rst_n(sys_rst_n),.sys_clk(sys_clk),.flash_start(flash_start),.flash_cmd(flash_cmd),.flash_addr(flash_addr),.wr_data_len(wr_data_len),.wr_data(wr_data),.wr_data_req(wr_data_req),.rd_data_len(rd_data_len),.rd_data(rd_data),.rd_data_flag(rd_data_flag),.flash_id(flash_id),.flash_id_flag(flash_id_flag),.flash_sr_reg(flash_sr_reg),.flash_sr_reg_flag(flash_sr_reg_flag),.flash_idle(flash_driver_idle),.spi_clk(spi_sclk),.spi_mosi(spi_mosi),.spi_miso(spi_miso),.spi_cs(spi_cs)
);endmodule

仿真激励

`timescale 1ns / 1psmodule tb_spi_flash_rw_test( );reg sys_rst_n;
reg sys_clk;wire spi_mosi;
reg spi_miso;
wire spi_clk;
wire spi_cs;wire alarm_led;reg [7:0] miso_data;
reg signed [7:0] loop_cnt1;
reg [16:0] loop_cnt2;initial beginsys_clk = 1'b0;sys_rst_n = 1'b0;#200sys_rst_n = 1'b1;
end//产生MISO
initial beginspi_miso = 1'b1;//模拟读flash IDwait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'hef;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h40;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h18;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写使能wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟全擦除wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟全擦除忙wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h01;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟全擦除空闲wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h00;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写使能wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写wait(spi_cs == 1'b0);for(loop_cnt2 = 0; loop_cnt2 < 4; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendfor(loop_cnt2 = 0; loop_cnt2 < 256; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写忙wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h01;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写空闲wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h00;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟读wait(spi_cs == 1'b0);for(loop_cnt2 = 0; loop_cnt2 < 5; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendfor(loop_cnt2 = 0; loop_cnt2 < 256; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = loop_cnt2[7:0];for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟写使能wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟子扇区擦除wait(spi_cs == 1'b0);for(loop_cnt2 = 0; loop_cnt2 < 4; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟子扇区擦除忙wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h01;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟子扇区擦除空闲wait(spi_cs == 1'b0);miso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endmiso_data = 8'h00;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endwait(spi_cs == 1'b1);spi_miso = 1'b1;//模拟读wait(spi_cs == 1'b0);for(loop_cnt2 = 0; loop_cnt2 < 5; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'h0;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendfor(loop_cnt2 = 0; loop_cnt2 < 256; loop_cnt2 = loop_cnt2 + 1) beginmiso_data = 8'hff;for(loop_cnt1 = 7; loop_cnt1 >= 0; loop_cnt1 = loop_cnt1 - 1) beginwait((spi_clk == 1'b0) && (spi_cs == 1'b0));spi_miso = miso_data[loop_cnt1];wait((spi_clk == 1'b1) && (spi_cs == 1'b0));endendwait(spi_cs == 1'b1);spi_miso = 1'b1;
end//产生时钟
always #10 sys_clk = ~sys_clk;spi_flash_rw_test #(.ALARM_LED_PERIOD(25'd250)
)
tb_spi_flash_rw_test_inst0(.sys_rst_n(sys_rst_n),.sys_clk(sys_clk),.spi_mosi(spi_mosi),.spi_miso(spi_miso),.spi_sclk(spi_clk),.spi_cs(spi_cs),.alarm_led(alarm_led)
);endmodule

引脚约束

create_clock -period 20.000 -name sys_clk -waveform {0.000 10.000} [get_ports sys_clk]set_property IOSTANDARD LVCMOS15 [get_ports sys_clk]
set_property PACKAGE_PIN R4 [get_ports sys_clk]set_property IOSTANDARD LVCMOS15 [get_ports sys_rst_n]
set_property PACKAGE_PIN U7 [get_ports sys_rst_n]set_property IOSTANDARD LVCMOS15 [get_ports alarm_led]
set_property PACKAGE_PIN V9 [get_ports alarm_led]set_property IOSTANDARD LVCMOS33 [get_ports spi_cs]
set_property PACKAGE_PIN N14 [get_ports spi_cs]set_property IOSTANDARD LVCMOS33 [get_ports spi_miso]
set_property PACKAGE_PIN N13 [get_ports spi_miso]set_property IOSTANDARD LVCMOS33 [get_ports spi_mosi]
set_property PACKAGE_PIN R14 [get_ports spi_mosi]set_property IOSTANDARD LVCMOS33 [get_ports spi_sclk]
set_property PACKAGE_PIN P14 [get_ports spi_sclk]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

crossover24中文破解版百度云免费下载 crossover永久免激活汉化包安装使用教程 crossover24激活码分享

原则上&#xff0c;我们不提倡各位使用破解版&#xff0c;这是处于对知识产权的保护&#xff0c;也是为了各位的长远利益。使用正版你可以获得更优质的服务和完善的产品功能。 但仍然有部分用户由于预算、使用习惯等原因&#xff0c;需要破解版。所以本文不讲原则&#xff0c;…

有没有适合女生或者宝妈下班后可以做的副业?

宝妈与上班族女生的新篇章&#xff1a;水牛社副业兼职之旅 在繁忙的职场和温馨的家庭之间&#xff0c;不少女性渴望找到一种既能兼顾家庭又能实现自我价值的兼职方式。对于上班族女生和宝妈们来说&#xff0c;水牛社这样的线上任务平台为她们提供了一个全新的选择。 上班族女…

【备战软考(嵌入式系统设计师)】12 - 嵌入式系统总线接口

我们嵌入式系统的总线接口可以分为两类&#xff0c;一类是并行接口&#xff0c;另一类是串行接口。 并行通信就是用多个数据线&#xff0c;每条数据线表示一个位来进行传输数据&#xff0c;串行接口就是一根数据线可以来一位一位地传递数据。 从上图也可以看出&#xff0c;并行…

SR3-05S电机保护器 施耐德 EOCR-SR3

EOCR-SR3电机保护器 施耐德 EOCR主要产品有电子式电动机保护继电器&#xff0c;电子式过电流继电器&#xff0c;电子式欠电流继电器&#xff0c;电子式欠电压继电器&#xff0c;其它保护和监视装置&#xff0c;电流互感器。 系列型号&#xff1a; EOCR-SR3-05S EOCR-SR3-30…

VR全景技术在养老院的应用优势浅析

随着时代的快速发展&#xff0c;人口老龄化越来越严重&#xff0c;如何利用VR技术提升养老服务的质量&#xff0c;成为了社会各界关注的焦点。为养老院拍摄制作VR全景&#xff0c;不仅能够为养老院的老人子女们跨越空间限制&#xff0c;实现与家人的情感连接&#xff0c;还可以…

Vditor集成于VUE笔记

文章目录 前言一、安装Vditor二、渲染markdown三、options3.1 自建CDN3.2 outline大纲不显示、不跳转问题3.3 upload 图片/视频上传3.4 toolbar提示位置点击事件more中文字 3.5 sv分屏渲染模式隐藏编辑框3.6 after中的insertValue或者setValue 前言 Vditor是一款易于使用的 Ma…

带你手撕红黑树! c++实现 带源码

目录 一、概念 二、特性 三、接口实现 1、插入 情况一&#xff1a;p为黑&#xff0c;结束 情况二&#xff1a;p为红 1&#xff09;叔叔存在且为红色 2&#xff09;u不存在/u存在且为黑色 &#xff08;1&#xff09;p在左&#xff0c;u在右 &#xff08;2&#xff09;…

MySQL中JOIN连接的实现算法

目录 嵌套循环算法&#xff08;NLJ&#xff09; 简单嵌套循环&#xff08;SNLJ&#xff09; 索引嵌套循环&#xff08;INLJ&#xff09; 块嵌套循环&#xff08;BNLJ&#xff09; 三种算法比较 哈希连接算法&#xff08;Hash Join&#xff09; 注意事项&#xff1a; 工…

异常处理/CC++ 中 assert 断言 应用实践和注意事项

文章目录 概述assert 本质浅析Release版本下的assert是否生效默认设置下 QtCreator环境 assert 过程默认配置下 VS环境 assert 过程配置VS发布模式下的断言生效VS环境Release版本的UI程序Release下请当我不生效 请勿滥用assert导致逻辑错误再强调不要在assert内执行逻辑功能怎敢…

【UnityRPG游戏制作】Unity_RPG项目_PureMVC框架应用

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…

Vue-watch监听器

监听器 watch侦听器&#xff08;监视器&#xff09;简单写法完整写法 watch侦听器&#xff08;监视器&#xff09; 作用&#xff1a;监视数据变化&#xff0c;执行一些业务逻辑或异步操作 语法&#xff1a; watch同样声明在跟data同级的配置项中简单写法&#xff1a; 简单类型…

C++ 中的 lambda 表达式

1.概念 lambda表达式实际上是一个匿名类的成员函数&#xff0c;该类由编译器为lambda创建&#xff0c;该函数被隐式地定义为内联。因此&#xff0c;调用lambda表达式相当于直接调用匿名类的operator()函数&#xff0c;这个函数可以被编译器内联优化&#xff08;建议&#xff0…

地图涟漪效果

参考API echarts图表集 useEcharts.js import { onBeforeUnmount, onDeactivated } from "vue"; // import * as echarts from "echarts";/*** description 使用 Echarts (只是为了添加图表响应式)* param {Element} myChart Echarts实例 (必传)* param …

AcWing-168生日蛋糕-搜索/剪枝

题目 思路 表面积和体积公式&#xff1a;以下分析参考自&#xff1a;AcWing 168. 生日蛋糕【图解推导】 - AcWing&#xff1b;AcWing 168. 关于四个剪枝的最清楚解释和再次优化 - AcWing 代码 #include<iostream> #include<cmath> using namespace std;const in…

【爬虫基础1.1课】——requests模块上

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

C语言学习(九)多文件编程 存储类型 结构体

目录 一、多文件编程&#xff08;一&#xff09;不写头文件的方方式进行多文件编程 &#xff08;二&#xff09;通过头文件方式进行多文件编程&#xff08;1&#xff09;方法&#xff08;2&#xff09;头文件守卫 &#xff08;三&#xff09; 使用多文件编程实现 - * / 功能 二…

HC-06 蓝牙串口从机 AT 命令详解

HC-06 蓝牙串口从机 AT 命令详解 要使用 AT 命令&#xff0c;首先要知道 HC-06 的波特率&#xff0c;然后要进入 AT 命令模式。 使用串口一定要知道三要素&#xff0c;一是波特率&#xff0c;二是串口号&#xff0c;三是数据格式, HC-06只支持一种数据格式: 数据位8 位&#…

HTTP 连接详解

概述 世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的&#xff0c;客户端可以打开一条TCP/IP连接&#xff0c;连接到任何地方的服务器。一旦连接建立&#xff0c;客户端和服务器之间交换的报文就永远不会丢失、受损或失序 TCP&#xff08;Transmission Control Protocol&…

97. 交错字符串-----回溯、动态规划

题目链接 97. 交错字符串 - 力扣&#xff08;LeetCode&#xff09; 解答 递归回溯 题目所述为两个字符串交替组成第三个字符串&#xff0c;之前好像做过相似的题目&#xff0c;直接联想到可以考虑使用递归回溯的做法&#xff0c;让字符串s1和字符串s2分别作为起始字符串&…

Mybatis-Plus大批量插入数据到MySQL

MyBatis-Plus的saveBatch方法 GetMapping("/save1") public void save1() {// 数据准备List<MallOrder> orderList getMallOrderList();// mybatis-pluslong start System.currentTimeMillis();mallOrderService.saveBatch(orderList);System.out.println(&…