当前位置: 首页 > news >正文

FPGA学习笔记——SPI读写FLASH

目录

一、任务

二、需求分析

三、Visio图

四、具体分析

五、IP核配置

六、代码

七、实验现象


一、任务

实验任务:
1.按下按键key1,开启读ID操作,将读出来的ID,通过串口发送至PC端显示,显示格式为“读ID:XX-XX-XX”;
2.按下按键key2,开启读数据操作,连续读3个字节,将读出来的数据,通过串口发送至PC端显示,显示格式为“读数据:XX XX XX”;
3.PC端通过串口助手向FPGA发送数据,每传输3字节数据,开启1次页编程操作;
  按键按键key4,执行1次扇区擦除操作。

需要用到的指令

WREN:0x06

RDID:0x9F

RDSR:0x05 和 状态寄存器

READ:0x03

PP:0x02

SE:0xD8

以上就是指令的时序。


二、需求分析

根据任务,可以知道要用哪些模块:按键消抖模块串口收发模块Flash控制模块,在Flash控制模块,我们可以细分成读控制模块写控制模块SPI接口模块

数据流向:

读Flash时按键按下,发送读操作指令,从Flash接收3个字节数据,然后将这个三个数据缓存到读FIFO中(FIFO IP核),当读FIFO存有3个数据时,开启串口发送,将这三个数据发送到PC端的串口助手进行显示。(读ID与之类似)

写Flash时,从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,开始将三个数据写入Flash中,如果发送完三个数据,写FIFO中,还有3个数据及以上,又开启一次将三个数据写入Flash中,直到写FIFO中,数据少于3个。

擦除操作:当按键按下,进行扇区擦除操作。


三、Visio图

以下就是,根据各个模块来化的Visio图。(系统框架)


四、具体分析

串口接收和发送模块:串口接收发送模块介绍

按键消抖模块:这里不做介绍;

读操作模块:根据Flash器件数据手册(M25P16数据手册——Flash)可以知道,要从Flash中读出数据,首先,要将读指令发送给Flash,然后接收Flash发送过来的数据(MSB),在把数据缓存到读FIFO中,然后通过串口发送模块,发送给串口助手显示。

因此状态可以划成:

IDLE

RD_ID

RD_DATA

DONE

当按键1按下,状态从IDLE跳到RD_ID状态(Flash的ID有三个字节),当Flash中的三个字节读完,跳到DONE状态,随后跳到IDLE状态等待下一次按键触发;当按键2按下,状态从IDLE跳到RD_DATA状态,将Flash的三个字节读出来后,跳到DONE状态,随后跳到IDLE状态。

写操作模块:从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,发送WREN指令,然后发PP(页编程)指令,将三个数据写入到Flash中,然后进行轮询操作,等待Flash内部的写操作完成,如果FIFO中剩下数据超过三个,继续发送PP指令,进行数据的写入;当按键按下,发送WREN指令,然后在发送SE(扇区擦除)指令,将对应的扇区全部置为1。然后进行轮询操作,等待Flash内部的擦除操作完成。

因此状态可以划分成:

IDLE

WREN

DELAY1

SE

PP

DELAY2

RDSR

从PC端的串口助手发送3个或者3个以上的数据,串口接收模块,将接收到的数据缓存到写FIFO中(FIFO IP核),当写FIFO中存有三个数据时,状态由IDLE跳到WREN状态,然后延时1us(这里要将CS_N拉高,进行指令的切换),状态由DELAY1跳到PP状态,发送PP指令,然后延时1us(这里要将CS_N拉高,进行指令的切换),状态由DELAY2跳到RDSR状态,进行轮询,等待Flash中的状态寄存器的最低为为0。

当按键按下,进行擦除操作,这里只有指令的不同,其他都是一样的。

SPI接口模块:这里要了解SPI协议的通讯流程SPI协议简介

这里的SPI接口就不做状态机,写一个分频器,自己产生一个串行时钟周期,比特计数器用来计数8个时钟周期(SPI是以8bit或者16bit传输),在对应的模式下,在对应得奇数沿和偶数沿进行输出和采样。


五、IP核配置

这里需要用到FIFO来缓存数据。(可以新建一个FIFO或者两个FIFO),我这里是新建了两个FIFO,一个读FIFO和一个写FIFO。

读FIFO IP核配置

写FIFO IP核配置


六、代码

sys_top.v

module sys_top( input               sys_clk  ,input               sys_rst_n,input               uart_rxd ,output              uart_txd ,input       [2:0]   key_in   ,output              spi_sclk ,output              spi_mosi ,input               spi_miso ,output              spi_cs_n
);wire    [2:0]   key_down   ;
wire    [7:0]   rx_data    ;
wire            rx_data_vld;
wire            ready      ;       
wire    [7:0]   tx_data    ;     
wire            tx_data_vld; key_filter u_key_filter_1(.clk      (sys_clk     ),.rst_n    (sys_rst_n   ),.key_in   (key_in[0]   ),.key_down (key_down[0] )
);key_filter u_key_filter_2(.clk      (sys_clk     ),.rst_n    (sys_rst_n   ),.key_in   (key_in[1]   ),.key_down (key_down[1] )
);    key_filter u_key_filter_4(.clk      (sys_clk     ),.rst_n    (sys_rst_n   ),.key_in   (key_in[2]   ),.key_down (key_down[2] )
);uart_rx #( .CLOCK_FRQ(50_000_000),.BAUD(115200),.DATA_LENTH(8)     ,.CHECKBIT_SELECT(0),.CHECK_TYPE(0)   //偶校验:0  奇校验:1
)uart_rx_inst( /*input               */.clk      (sys_clk    ),/*input               */.rst_n    (sys_rst_n  ),/*input               */.uart_rxd (uart_rxd   ),/*output  reg [7:0]   */.dout     (rx_data    ),/*output              */.dout_vld (rx_data_vld)
);flash_ctrl u_flash_ctrl(.clk         (sys_clk     ),.rst_n       (sys_rst_n   ),.rx_data     (rx_data     ),.rx_data_vld (rx_data_vld ),.ready       (ready       ),.tx_data     (tx_data     ),.tx_data_vld (tx_data_vld ),.key_down    (key_down    ),.sclk        (spi_sclk    ),.mosi        (spi_mosi    ),.miso        (spi_miso    ),.cs_n        (spi_cs_n    )
);uart_tx #(  .CLOCK_FRQ(50_000_000),.BAUD(115200),.DATA_LENTH(8)     ,.CHECKBIT_SELECT(0),.CHECK_TYPE(0)   //偶校验:0  奇校验:1)uart_tx_inst( /*input                           */.clk      (sys_clk    ),/*input                           */.rst_n    (sys_rst_n  ),/*input       [DATA_LENTH-1:0]    */.tx_din   (tx_data    ),/*input                           */.tx_enable(tx_data_vld),/*output  reg                     */.uart_txd (uart_txd   ),/*output  reg                     */.ready    (ready      ) //忙闲指示信号
);endmodule

uart_tx.v

module uart_tx #(parameter CLOCK_FRQ  = 50_000_000,BAUD       = 9600      ,DATA_LENTH = 8         ,CHECKBIT_SELECT = 0    ,CHECK_TYPE = 0           //偶校验:0  奇校验:1
)( input                           clk      ,input                           rst_n    ,input       [DATA_LENTH-1:0]    tx_din   ,input                           tx_enable,output  reg                     uart_txd ,output  reg                     ready     //忙闲指示信号
);
//---------<参数定义>------------------------------------------------//状态机参数定义localparam  IDLE  = 5'b00001,START = 5'b00010,DATA  = 5'b00100,CHECK = 5'b01000,STOP  = 5'b10000;localparam  BUAD_MAX = CLOCK_FRQ/BAUD;//---------<内部信号定义>--------------------------------------------reg     [4:0]   state_c     ;reg     [4:0]   state_n     ;reg		[15:0]	cnt_baud	;//波特率计数器wire			add_cnt_baud;wire			end_cnt_baud;reg		[2:0]	cnt_bit	    ;//数据传输的比特计数器wire			add_cnt_bit ;wire			end_cnt_bit ;reg     [7:0]   tx_din_r    ;//状态转移条件定义wire            idle2start  ;wire            start2data  ;wire            data2check  ;wire            data2stop   ;wire            check2stop  ;wire            stop2idle   ;//*******************************************************************
//--tx_din_r
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_din_r <= 'd0;end else if(tx_enable)begin tx_din_r <= tx_din;end end//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE  : beginif(idle2start)state_n = START; else state_n = state_c;endSTART : beginif(start2data)state_n = DATA;else state_n = state_c;endDATA  : beginif(data2check)state_n = CHECK;else if(data2stop)state_n = STOP;else state_n = state_c;endCHECK : beginif(check2stop)state_n = STOP;else state_n = state_c;endSTOP  : beginif(stop2idle)state_n = IDLE; else state_n = state_c;enddefault : state_n = IDLE;endcaseendassign  idle2start = (state_c == IDLE ) && tx_enable; assign  start2data = (state_c == START) && end_cnt_baud;assign  data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;assign  data2stop  = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;assign  check2stop = (state_c == CHECK) && end_cnt_baud;assign  stop2idle  = (state_c == STOP ) && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));//*******************************************************************
//--cnt_bit
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = state_c == DATA && end_cnt_baud;assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;//*******************************************************************
//--uart_txd
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_txd <= 1'b1;end else begin case (state_c)IDLE  : uart_txd <= 1'b1;START : uart_txd <= 1'b0;DATA  : uart_txd <= tx_din_r[cnt_bit];//并转串,LSB// CHECK : uart_txd <= ^tx_din_r + CHECK_TYPE;CHECK : uart_txd <= CHECK_TYPE ? (^tx_din_r + 1'b1) : (^tx_din_r); STOP  : uart_txd <= 1'b1;default: uart_txd <= 1'b1;endcaseend end//*******************************************************************
//--ready
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginready <= 'd0;end else begin ready <= state_n == IDLE;end endendmodule

uart_rx.v

module uart_rx #(parameter CLOCK_FRQ  = 50_000_000,BAUD       = 9600      ,DATA_LENTH = 8         ,CHECKBIT_SELECT = 0    ,CHECK_TYPE = 0           //偶校验:0  奇校验:1
)( input               clk      ,input               rst_n    ,input               uart_rxd ,output  reg [7:0]   dout     ,output              dout_vld 
);
//---------<参数定义>------------------------------------------------//状态机参数定义localparam  IDLE  = 5'b00001,START = 5'b00010,DATA  = 5'b00100,CHECK = 5'b01000,STOP  = 5'b10000;localparam  BUAD_MAX = CLOCK_FRQ/BAUD;//---------<内部信号定义>--------------------------------------------reg     [4:0]   state_c     ;reg     [4:0]   state_n     ;reg		[15:0]	cnt_baud	;//波特率计数器wire			add_cnt_baud;wire			end_cnt_baud;reg		[2:0]	cnt_bit	   ;//接收bit数量的计数器wire			add_cnt_bit;wire			end_cnt_bit;reg     [2:0]   uart_rxd_r  ;wire            rxd_n_edge  ;//状态转移条件定义wire            idle2start  ;wire            start2data  ;wire            data2check  ;wire            data2stop   ;wire            check2stop  ;wire            stop2idle   ;//*******************************************************************
//--打三拍(前两拍同步,后一拍获得边沿)
//*******************************************************************//uart_rxd_ralways @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_rxd_r <= 3'b111;end else begin uart_rxd_r <= {uart_rxd_r[1:0],uart_rxd};end end//rxd_n_edgeassign  rxd_n_edge = ~uart_rxd_r[1] & uart_rxd_r[2];//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE  : beginif(idle2start)state_n = START; else state_n = state_c;endSTART : beginif(start2data)state_n = DATA;else state_n = state_c;endDATA  : beginif(data2check)state_n = CHECK;else if(data2stop)state_n = STOP;else state_n = state_c;endCHECK : beginif(check2stop)state_n = STOP;else state_n = state_c;endSTOP  : beginif(stop2idle)state_n = IDLE; else state_n = state_c;enddefault : state_n = IDLE;endcaseendassign  idle2start = (state_c == IDLE ) && rxd_n_edge;assign  start2data = (state_c == START) && end_cnt_baud;assign  data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;assign  data2stop  = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;assign  check2stop = (state_c == CHECK) && end_cnt_baud;assign  stop2idle  = (state_c == STOP ) && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));//*******************************************************************
//--cnt_bit
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = state_c == DATA && end_cnt_baud;assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;//*******************************************************************
//--dout
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindout <= 'd0;end else if(state_c == DATA && cnt_baud == (BUAD_MAX>>1) - 1)begin dout[cnt_bit] <= uart_rxd_r[2];// dout <= {uart_rxd_r[2],dout[7:1]};end end//*******************************************************************
//--dout_vld
//*******************************************************************assign  dout_vld = stop2idle;endmodule

flash_ctrl.v

module flash_ctrl( input               clk        ,input               rst_n      ,//uart_rxinput       [7:0]   rx_data    ,input               rx_data_vld,//uart_txinput               ready      ,output      [7:0]   tx_data    ,output              tx_data_vld,//key_filterinput       [2:0]   key_down   ,//M25P16  output              sclk      ,output              mosi      ,input               miso      ,output              cs_n      
);wire            start_en;  
wire    [7:0]   wr_data ;   
wire    [7:0]   rd_data ;   
wire            trans_done;rw_ctrl u_rw_ctrl(.clk         (clk         ),.rst_n       (rst_n       ),.rx_data     (rx_data     ),.rx_data_vld (rx_data_vld ),.ready       (ready       ),.tx_data     (tx_data     ),.tx_data_vld (tx_data_vld ),.key_down    (key_down    ),.start_en    (start_en    ),.wr_data     (wr_data     ),.rd_data     (rd_data     ),.trans_done  (trans_done  )
);spi_intf u_spi_intf(.clk        (clk        ),.rst_n      (rst_n      ),.start_en   (start_en   ),.wr_data    (wr_data    ),.rd_data    (rd_data    ),.trans_done (trans_done ),.sclk       (sclk       ),.mosi       (mosi       ),.miso       (miso       ),.cs_n       (cs_n       )
);endmodule

rw_ctrl.v

module rw_ctrl( input               clk        ,input               rst_n      ,//uart_rxinput       [7:0]   rx_data    ,input               rx_data_vld,//uart_txinput               ready      ,output      [7:0]   tx_data    ,output              tx_data_vld,//key_filterinput       [2:0]   key_down   ,//spi_intfoutput              start_en   ,output      [7:0]   wr_data    ,input       [7:0]   rd_data    ,input               trans_done 
);wire            wr_start_en ;
wire    [7:0]   wr_wr_data  ;wire            rd_start_en ;
wire    [7:0]   rd_wr_data  ;write_op u_write_op(.clk         (clk         ),.rst_n       (rst_n       ),.key_down    (key_down[2] ),.rx_data     (rx_data     ),.rx_data_vld (rx_data_vld ),.start_en    (wr_start_en ),.wr_data     (wr_wr_data  ),.rd_data     (rd_data     ),.trans_done  (trans_done  )
);read_op u_read_op(.clk         (clk          ),.rst_n       (rst_n        ),.key_down    (key_down[1:0]),.ready       (ready        ),.tx_data     (tx_data      ),.tx_data_vld (tx_data_vld  ),.start_en    (rd_start_en ),.wr_data     (rd_wr_data  ),.rd_data     (rd_data     ),.trans_done  (trans_done  )
);assign start_en = wr_start_en | rd_start_en; 
assign wr_data = ({8{wr_start_en}} & wr_wr_data) | ({8{rd_start_en}} & rd_wr_data);endmodule

write_op.v

`include "param.vh"module write_op#(parameter WR_LENTH = 2'd3)( //Userinput               clk        ,input               rst_n      ,input               key_down   ,input       [7:0]   rx_data    ,input               rx_data_vld,//spi_intfoutput  reg         start_en   ,output  reg [7:0]   wr_data    ,input       [7:0]   rd_data    ,input               trans_done 
);localparam  IDLE   = 7'b000_0001,WREN   = 7'b000_0010,DELAY1 = 7'b000_0100,SE     = 7'b000_1000,PP     = 7'b001_0000,DELAY2 = 7'b010_0000,RDSR   = 7'b100_0000;reg     [6:0]   state_c ;
reg     [6:0]   state_n ;
reg		[5:0]	cnt_delay	 ;//指令时序间隔(片选取消选择时间)延时计数器,t>=100ns ,取1us
wire			add_cnt_delay;
wire			end_cnt_delay;
reg		[3:0]	cnt_byte	 ;//传输字节计数器
wire			add_cnt_byte ;
wire			end_cnt_byte ;
reg		[23:0]	wr_addr	     ;//写地址计数器
wire			add_wr_addr  ;
wire			end_wr_addr  ;reg             op_flag      ;//操作的标志信号 0:SE 1:PP
reg             rdsr_wr_flag ;wire            idle2wren       ;
wire            wren2delay1     ;
wire            delay1se        ;
wire            delay1pp        ;
wire            se2delay2       ;
wire            pp2delay2       ;
wire            delay22rdsr     ;
wire            rdsr2idle       ;wire            full  ;
wire            rdreq ;
wire            empty ;
wire    [7:0]   fifo_q;
wire    [7:0]   usedw ;//---------<FIFO例化:缓存>------------------------------------------------- 
wr_fifo u_wr_fifo(.aclr  (~rst_n              ),.clock (clk                 ),.data  (rx_data             ),.rdreq (rdreq               ),.wrreq (~full && rx_data_vld),.empty (empty               ),.full  (full                ),.q     (fifo_q              ),.usedw (usedw               )
);assign rdreq = ~empty && state_c == PP && cnt_byte >= 4 && trans_done;//---------<State Machine>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end 
endalways @(*) begincase(state_c)IDLE   : beginif(idle2wren)state_n = WREN;else state_n = state_c;endWREN   : beginif(wren2delay1)state_n = DELAY1;else state_n = state_c;endDELAY1 : beginif(delay1se)state_n = SE;else if(delay1pp)state_n = PP;else state_n = state_c;endSE     : beginif(se2delay2)state_n = DELAY2;else state_n = state_c;endPP     : beginif(pp2delay2)state_n = DELAY2;else state_n = state_c;endDELAY2 : beginif(delay22rdsr)state_n = RDSR;else state_n = state_c;endRDSR   : beginif(rdsr2idle)state_n = IDLE;else state_n = state_c;enddefault : ;endcase
endassign idle2wren   = (state_c == IDLE) && (key_down | usedw >= WR_LENTH);
assign wren2delay1 = (state_c == WREN) && trans_done;
assign delay1se    = (state_c == DELAY1) && end_cnt_delay && ~op_flag;
assign delay1pp    = (state_c == DELAY1) && end_cnt_delay && op_flag;
assign se2delay2   = (state_c == SE) && end_cnt_byte;
assign pp2delay2   = (state_c == PP) && end_cnt_byte;
assign delay22rdsr = (state_c == DELAY2) && end_cnt_delay;
assign rdsr2idle   = (state_c == RDSR) && rd_data[0] == 1'b0;//检测WIP位//---------<cnt_delay>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_delay <= 'd0;end else if(add_cnt_delay)begin if(end_cnt_delay)begin cnt_delay <= 'd0;endelse begin cnt_delay <= cnt_delay + 1'b1;end end
end assign add_cnt_delay = state_c == DELAY1 || state_c == DELAY2;
assign end_cnt_delay = add_cnt_delay && cnt_delay == 50 - 1;//---------<op_flag>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginop_flag <= 'd0;end else if(key_down)begin //SEop_flag <= 1'b0;end else if(usedw >= WR_LENTH)begin //PPop_flag <= 1'b1;end 
end//---------<cnt_byte>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte = trans_done && (state_c == SE || state_c == PP);
assign end_cnt_byte = add_cnt_byte && cnt_byte == (state_c == SE ? 4-1 : 4+WR_LENTH-1);//---------<wr_addr>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(add_wr_addr)begin if(end_wr_addr)begin wr_addr <= 'd0;endelse begin wr_addr <= wr_addr + WR_LENTH;end end
end assign add_wr_addr = pp2delay2;
assign end_wr_addr = add_wr_addr && wr_addr == 24'h1f_ff_ff;//---------<rdsr_wr_flag>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrdsr_wr_flag <= 'd0;end else if(delay22rdsr)begin rdsr_wr_flag <= 1'b1;end else if(state_c == RDSR && trans_done) begin rdsr_wr_flag <= 1'b0;end 
end//---------<输出>------------------------------------------------- 
always @(*)begin case (state_c)WREN : begin start_en = 1'b1;wr_data = `CMD_WREN;endSE : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_SE;end1 : begin start_en = 1'b1;wr_data = wr_addr[23:16];end2 : begin start_en = 1'b1;wr_data = wr_addr[15:8];end3 : begin start_en = 1'b1;wr_data = wr_addr[7:0];enddefault: begin start_en = 1'b0;wr_data = 8'd0;endendcaseendPP : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_PP;end1 : begin start_en = 1'b1;wr_data = wr_addr[23:16];end2 : begin start_en = 1'b1;wr_data = wr_addr[15:8];end3 : begin start_en = 1'b1;wr_data = wr_addr[7:0];enddefault: begin start_en = 1'b1;wr_data = fifo_q;endendcaseendRDSR : beginstart_en = 1'b1;wr_data = rdsr_wr_flag ? `CMD_RDSR : 8'd0;enddefault: begin start_en = 1'b0;wr_data = 8'd0;endendcase
endendmodule

read_op.v

`include "param.vh"module read_op#(parameter RDDA_LENTH = 2'd3)( //USERinput               clk        ,input               rst_n      ,input       [1:0]   key_down   ,input               ready      ,output      [7:0]   tx_data    ,output              tx_data_vld,//spi_intfoutput  reg         start_en   ,output  reg [7:0]   wr_data    ,input       [7:0]   rd_data    ,input               trans_done 
);localparam  IDLE    = 4'h1,RD_ID   = 4'h2, RD_DATA = 4'h4,DONE    = 4'h8;reg     [3:0]   state_c ;
reg     [3:0]   state_n ;
reg		[7:0]	cnt_byte	;
wire			add_cnt_byte;
wire			end_cnt_byte;
reg     [23:0]  rd_addr     ;
reg     [23:0]  temp_data   ;
reg             tx_id_flag ;//读ID操作时,向rd_fifo中写数据标志信号
reg             tx_data_flag;//读数据操作时,向rd_fifo中写数据标志信号
reg		[3:0]	cnt_tx	  ;//需要传输的字节数量计数器
wire			add_cnt_tx;
wire			end_cnt_tx;
reg     [3:0]   max_cnt_tx;    wire            wrreq;
reg     [7:0]   wr_fifo_data;
wire            rdreq;
wire            empty;
wire            full ;
wire    [7:0]   fifo_q;//---------<fifo例化>------------------------------------------------- 
rd_fifo u_rd_fifo(.aclr  (~rst_n      ),.clock (clk         ),.data  (wr_fifo_data),.rdreq (rdreq       ),.wrreq (wrreq       ),.empty (empty       ),.full  (full        ),.q     (fifo_q      )
);assign wrreq = ~full && (tx_id_flag | tx_data_flag);
assign rdreq = ~empty && ready;//---------<State Machine>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end 
endalways @(*) begincase(state_c)IDLE    : state_n = key_down[0] ? RD_ID : (key_down[1] ? RD_DATA : state_c);RD_ID   : state_n = end_cnt_byte ? DONE : state_c;RD_DATA : state_n = end_cnt_byte ? DONE : state_c;DONE    : state_n = IDLE;default : state_n = IDLE;endcase
end//---------<cnt_byte>-------------------------------------------------     
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end end
end assign add_cnt_byte = (state_c == RD_ID || state_c == RD_DATA) && trans_done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == (state_c == RD_ID ? 4 - 1 : RDDA_LENTH + 4 - 1);//---------<rd_addr>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_addr <= 'd0;end else if(rd_addr >= 24'h1f_ff_ff - RDDA_LENTH && end_cnt_byte)beginrd_addr <= 'd0;endelse if(state_c == RD_DATA && end_cnt_byte)begin rd_addr <= rd_addr + RDDA_LENTH;end 
end//---------<start_en wr_data>------------------------------------------------- 
always @(*)begin case (state_c)RD_ID : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_RDID;end default: begin start_en = 1'b1;wr_data = 8'd0;end endcaseendRD_DATA : begincase (cnt_byte)0 : begin start_en = 1'b1;wr_data = `CMD_RDDA;end 1 : begin start_en = 1'b1;wr_data = rd_addr[23:16];end 2 : begin start_en = 1'b1;wr_data = rd_addr[15:8];end 3 : begin start_en = 1'b1;wr_data = rd_addr[7:0];end default: begin start_en = 1'b1;wr_data = 8'd0;end endcaseenddefault: begin start_en = 1'b0;wr_data = 8'd0;end endcase
end//---------<temp_data>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintemp_data <= 'd0;end else if((state_c == RD_ID && trans_done && cnt_byte >= 1) ||state_c == RD_DATA && trans_done && cnt_byte >= 4 )begin temp_data <= {temp_data[15:0],rd_data};end 
end//---------<串口显示>------------------------------------------------- 
//读ID: -->  B6 C1 49 44 A3 BA X X X X X X -->12bytes
//读数据:--> B6 C1 CA FD BE D8 A3 BA X X X X X X -->14bytes//tx_id_flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_id_flag <= 'd0;end else if(state_c == RD_ID && end_cnt_byte)begin tx_id_flag <= 1'b1;end else if(end_cnt_tx)begin tx_id_flag <= 1'b0;end 
end//tx_data_flag
always @(posedge clk or negedge rst_n)begin if(!rst_n)begintx_data_flag <= 'd0;end else if(state_c == RD_DATA && end_cnt_byte)begin tx_data_flag <= 1'b1;end else if(end_cnt_tx)begin tx_data_flag <= 1'b0;end 
end//cnt_tx
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_tx <= 'd0;end else if(add_cnt_tx)begin if(end_cnt_tx)begin cnt_tx <= 'd0;endelse begin cnt_tx <= cnt_tx + 1'b1;end end
end assign add_cnt_tx = tx_id_flag || tx_data_flag;
assign end_cnt_tx = add_cnt_tx && cnt_tx == max_cnt_tx - 1;//max_cnt_tx
always @(*)begin if(tx_id_flag)max_cnt_tx = 12;else if(tx_data_flag)max_cnt_tx = 14;else max_cnt_tx = 0;
end//wr_fifo_data
always @(*)begin if(tx_id_flag)begincase (cnt_tx)0 : wr_fifo_data = 8'hB6;//读1 : wr_fifo_data = 8'hC1;2 : wr_fifo_data = "I";3 : wr_fifo_data = "D";4 : wr_fifo_data = 8'ha3;//中文冒号5 : wr_fifo_data = 8'hba;6 : wr_fifo_data =  (temp_data[23:20] >= 10) ? (temp_data[23:20] + "A" - 8'd10) : (temp_data[23:20] + "0");7 : wr_fifo_data =  (temp_data[19:16] >= 10) ? (temp_data[19:16] + "A" - 8'd10) : (temp_data[19:16] + "0");8 : wr_fifo_data =  (temp_data[15:12] >= 10) ? (temp_data[15:12] + "A" - 8'd10) : (temp_data[15:12] + "0");9 : wr_fifo_data =  (temp_data[11:8]  >= 10) ? (temp_data[11:8]  + "A" - 8'd10) : (temp_data[11:8]  + "0");10 : wr_fifo_data = (temp_data[7:4]   >= 10) ? (temp_data[7:4]   + "A" - 8'd10) : (temp_data[7:4]   + "0");11 : wr_fifo_data = (temp_data[3:0]   >= 10) ? (temp_data[3:0]   + "A" - 8'd10) : (temp_data[3:0]   + "0");default: wr_fifo_data = 0;endcaseendelse if(tx_data_flag)begincase (cnt_tx)0 :  wr_fifo_data = 8'hB6;//读1 :  wr_fifo_data = 8'hC1;2 :  wr_fifo_data = 8'hca;//数3 :  wr_fifo_data = 8'hfd;4 :  wr_fifo_data = 8'hbe;//据5 :  wr_fifo_data = 8'hd8;6 :  wr_fifo_data = 8'ha3;//中文冒号7 :  wr_fifo_data = 8'hba;8 :  wr_fifo_data = (temp_data[23:20] >= 10) ? (temp_data[23:20] + "A" - 4'd10) : (temp_data[23:20] + "0"); 9 :  wr_fifo_data = (temp_data[19:16] >= 10) ? (temp_data[19:16] + "A" - 4'd10) : (temp_data[19:16] + "0"); 10 : wr_fifo_data = (temp_data[15:12] >= 10) ? (temp_data[15:12] + "A" - 4'd10) : (temp_data[15:12] + "0");11 : wr_fifo_data = (temp_data[11:8]  >= 10) ? (temp_data[11:8]  + "A" - 4'd10) : (temp_data[11:8]  + "0");12 : wr_fifo_data = (temp_data[7:4]   >= 10) ? (temp_data[7:4]   + "A" - 4'd10) : (temp_data[7:4]   + "0");13 : wr_fifo_data = (temp_data[3:0]   >= 10) ? (temp_data[3:0]   + "A" - 4'd10) : (temp_data[3:0]   + "0");default: wr_fifo_data = 8'd0;endcaseendelse beginwr_fifo_data = 8'd0;end
end//---------<tx_data tx_data_vld>------------------------------------------------- 
assign tx_data = fifo_q;
assign tx_data_vld = rdreq;endmodule

param.vh

//FLASH指令参数
`define     CMD_RDID    8'h9f
`define     CMD_RDDA    8'h03
`define     CMD_WREN    8'h06
`define     CMD_SE      8'hD8
`define     CMD_PP      8'h02
`define     CMD_RDSR    8'h05

spi_intf.v

module spi_intf#(parameter  SYS_CLOCK = 50_000_000,SPI_CLOCK = 12_500_000,DATA_WIDTH = 8 ,CPOL = 1'b0,CPHA = 1'b0
)( //USERinput                            clk       ,input                            rst_n     ,input                            start_en  ,//读/写使能信号input       [DATA_WIDTH - 1:0]   wr_data   ,//输出的数据output      [DATA_WIDTH - 1:0]   rd_data   ,//读出的数据output                           trans_done,//SPI INTERFACEoutput                           sclk      ,output                           mosi      ,input                            miso      ,output                           cs_n      );//定义一个函数:实现取对数的功能
function integer log2b;input   integer data;begin for(log2b=0;data>0;log2b=log2b+1)  data = data>>1;log2b = log2b - 1;end  
endfunctionlocalparam  DIV_NUM = SYS_CLOCK/SPI_CLOCK;reg                             sclk_r     ;
reg     [DATA_WIDTH-1:0]        rd_data_r  ;
reg                             mosi_r     ;
reg		[log2b(DIV_NUM)-1:0]	cnt_div	   ;//时钟分频计数器
wire			                add_cnt_div;
wire			                end_cnt_div;
reg		[log2b(DATA_WIDTH)-1:0]	cnt_bit	   ;
wire			                add_cnt_bit;
wire			                end_cnt_bit;//---------<cnt_div>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_div <= 'd0;end else if(add_cnt_div)begin if(end_cnt_div)begin cnt_div <= 'd0;endelse begin cnt_div <= cnt_div + 1'b1;end end
end assign add_cnt_div = start_en;
assign end_cnt_div = add_cnt_div && cnt_div == DIV_NUM - 1;//---------<sclk>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsclk_r <= CPOL;end else if(start_en)begin // if(cnt_div < (DIV_NUM>>1))//     sclk_r <= CPOL^CPHA;// else //     sclk_r <= CPOL^~CPHA;if(CPHA == 1)sclk_r <= cnt_div <  (DIV_NUM>>1) ?  ~CPOL : CPOL;else sclk_r <= cnt_div <  (DIV_NUM>>1) ? CPOL : ~CPOL;end else begin sclk_r <= CPOL;end 
end//---------<cnt_bit>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end end
end assign add_cnt_bit = end_cnt_div;
assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_WIDTH - 1;//---------<rd_data_r>------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_data_r <= 'd0;end else if(cnt_div == (DIV_NUM>>1))begin rd_data_r[7-cnt_bit] <= miso;//串转并end 
end
// always @(posedge sclk_r or negedge rst_n)begin 
//     if(!rst_n)begin
//         rd_data_r <= 'd0;
//     end 
//     else if(start_en)begin 
//         rd_data_r[7-cnt_bit] <= miso;
//     end 
// end//---------<mosi_r>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginmosi_r <= 'd0;end else if(start_en)begin mosi_r <= wr_data[7-cnt_bit];//并转串end 
end//---------<输出>------------------------------------------------- 
assign rd_data = rd_data_r;
assign trans_done = end_cnt_bit;
assign sclk = sclk_r;
assign mosi = mosi_r;
assign cs_n = ~start_en;endmodule

key_filter.v

module key_filter#(parameter WIDTH = 1,TIME_20MS = 1000_000)( input				    clk		,input				    rst_n	,input       [WIDTH-1:0]	key_in	,output  reg [WIDTH-1:0] key_down
);								 
//---------<参数定义>--------------------------------------------------------- //状态机参数        独热码编码localparam  IDLE        = 4'b0001,//空闲状态FILTER_DOWN = 4'b0010,//按键按下抖动状态HOLD        = 4'b0100,//按键稳定按下状态FILTER_UP   = 4'b1000;//按键释放抖动状态//---------<内部信号定义>-----------------------------------------------------reg     [3:0]       state_c         ;//现态reg     [3:0]       state_n         ;//次态reg     [WIDTH-1:0] key_r0          ;//同步打拍reg     [WIDTH-1:0] key_r1          ;reg     [WIDTH-1:0] key_r2          ;wire    [WIDTH-1:0] n_edge          ;//下降沿wire    [WIDTH-1:0] p_edge          ;//上升沿   reg	    [19:0]	    cnt_20ms	   	;//延时计数器(20ms)wire				add_cnt_20ms	;wire				end_cnt_20ms	;  //状态转移条件信号wire                idle2filter_down    ;wire                filter_down2idle    ;wire                filter_down2hold    ;wire                hold2filter_up      ;wire                filter_up2hold      ;wire                filter_up2idle      ;  //****************************************************************
//--状态机
//****************************************************************//第一段:时序逻辑描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑描述状态转移规律和状态转移条件always @(*)begin case (state_c)IDLE        : begin if(idle2filter_down)begin state_n = FILTER_DOWN;endelse begin // state_n = IDLE;state_n = state_c;endendFILTER_DOWN : begin if(filter_down2idle)begin state_n = IDLE;endelse if(filter_down2hold)begin state_n = HOLD;endelse begin state_n = state_c;endendHOLD        : begin if(hold2filter_up)begin state_n = FILTER_UP;endelse begin state_n = state_c;endendFILTER_UP   : begin if(filter_up2hold)begin state_n = HOLD;endelse if(filter_up2idle)begin state_n = IDLE;endelse begin state_n = state_c;endenddefault: state_n = IDLE;endcaseendassign idle2filter_down = (state_c == IDLE) && n_edge;assign filter_down2idle = (state_c == FILTER_DOWN) && p_edge;assign filter_down2hold = (state_c == FILTER_DOWN) && end_cnt_20ms && !p_edge;assign hold2filter_up   = (state_c == HOLD) && p_edge;assign filter_up2hold   = (state_c == FILTER_UP) && n_edge;assign filter_up2idle   = (state_c == FILTER_UP) && end_cnt_20ms;//****************************************************************
//--n_edge、p_edge 
//****************************************************************             always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_r0 <= {WIDTH{1'b1}};key_r1 <= {WIDTH{1'b1}};key_r2 <= {WIDTH{1'b1}};end else begin key_r0 <= key_in;key_r1 <= key_r0;key_r2 <= key_r1; end endassign n_edge = ~key_r1 & key_r2;//下降沿assign p_edge = ~key_r2 & key_r1;//上升沿//****************************************************************
//--cnt_20ms
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_20ms <= 'd0;end else if(add_cnt_20ms)begin if(end_cnt_20ms || filter_down2idle || filter_up2hold)begin cnt_20ms <= 'd0;endelse begin cnt_20ms <= cnt_20ms + 1'b1;end endend assign add_cnt_20ms = (state_c == FILTER_DOWN) || (state_c == FILTER_UP);assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;//****************************************************************
//--key_down
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_down <= 'd0;end else begin key_down <= filter_down2hold ? ~key_r2 : 1'b0;endendendmodule

七、实验现象


以上就是SPI读写FLASH。(如果有错误的地方,还请大家指出来,谢谢!)

http://www.xdnf.cn/news/1399897.html

相关文章:

  • 【运维篇第三弹】《万字带图详解分库分表》从概念到Mycat中间件使用再到Mycat分片规则,详解分库分表,有使用案例
  • 小迪Web自用笔记7
  • 【Linux】如何使用 Xshell 登录 Linux 操作系统
  • SC税务 登录滑块 分析
  • 拦截器Intercepter
  • hello算法笔记 01
  • Isaac Lab Newton 人形机器人强化学习 Sim2Real 训练与部署
  • 下一代 AI 交互革命:自然语言对话之外,“意念控制” 离商用还有多远?
  • 在 .NET Core 中实现基于策略和基于角色的授权
  • HarmonyOS应用的多Module设计机制:构建灵活高效的应用程序
  • 【瑞吉外卖】手机号验证码登录(用QQ邮件发送代替)
  • python制作一个股票盯盘系统
  • NV032NV037美光固态闪存NV043NV045
  • 基于开源AI大模型AI智能名片S2B2C商城小程序的产地优势产品销售策略研究
  • 前端代码结构详解
  • 盛最多水的容器,leetCode热题100,C++实现
  • 封装哈希表
  • 基于SpringBoot的流浪动物领养系统【2026最新】
  • macOS 15.6 ARM golang debug 问题
  • Rust Web 模板技术~MiniJinja入门:一款适用于 Rust 语言的轻依赖强大模板引擎
  • Fourier 级数展开(案例:级数展开 AND 求和)
  • Prompt Engineering:高效构建智能文本生成的策略与实践
  • 单例模式的mock类注入单元测试与友元类解决方案
  • Android15适配16kb
  • ros2 foxy没有话题问题解决
  • Axios 实例配置指南
  • Keil5 MDK_541官网最新版下载、安装
  • 从 0 到 1 构建零丢失 RabbitMQ 数据同步堡垒:第三方接口数据零丢失的终极方案
  • comfUI背后的技术——VAE
  • 线性代数理论——状态空间