首页 > 其他分享 >SPI控制Flash读写

SPI控制Flash读写

时间:2024-04-01 20:33:03浏览次数:18  
标签:wire tx 读写 Flash rx SPI 指令 rst data

一、SPI协议简介

1.SPI简介

1.SPI(Serial Peripheral Interface)是一种高速、全双工、同步串行通信总线,由摩托摩拉公司推出。

2.优缺点:全双工通信,通信方式简单,相对数据传输速度较快;SPI没有应答机制确认数据是否接收,数据可靠性上有一定缺陷。(相对IIC协议)。

2.SPI原理

2.1 主要端口:

SCK :时钟信号线,用于同步通讯数据。

MOSI:主设备输出/从设备输入引脚。

MISO:主设备输入/从设备输出引脚。

CS :片选信号。也称CS_N(片选信号低电平有效)。片选信号线独立,有多少个从机就有多少个CS。

2.2 通信模式(四种)

CPOL(时钟极性) : 片选信号处于空闲状态时,时钟的电平

CPHA(时钟相位):采样的时钟边沿(奇数沿/偶数沿);CPHA=0,采样的时钟边沿为奇数沿;CPHA=1,采样的时钟边沿为偶数沿;采样沿保持数据稳定,另一沿改变数据。

2.3数据传输时序

二、Flash简介(M25P16)

1.基本属性

1.1 储存属性:16Mbit,一共32个扇区,每个扇区256页,每页256字节,Flash断电后数据不会擦除

1.2 SPI模式支持:M25P16支持SPI模式0和模式3

1.3 引脚

2.关键时间参数

3.指令

3.1指令集

指令传输时是MSB传输

3.2 使用到的各个指令详解
3.2.1写使能指令(WREN)

WREN( Write Enable)指令通过驱动芯片选择(S)低,发送指令代码,然后驱动芯片选择(S)高。必须在每个页程序(PP)、扇区擦除(SE)、批量擦除(BE)和写入状态寄存器(WRSR)指令之前,输入WREN指令以设置写入启用锁存器(WEL)位。

3.2.2 扇区擦除指令(SE)

扇区擦除(SE)指令将所选扇区内的所有位设置为1 (FFh)。扇区内的任何地址都是扇区擦除(SE)指令的有效地址。指令序列如下所示。芯片选择(S)必须在最后一个地址字节的第八位被锁存后驱动为高电平,否则扇区擦除(SE)指令不执行。一旦芯片选择(S)驱动高,自定时扇区擦除周期(其持续时间为tse)被启动

地址例子:

当扇区擦除周期正在进行时,可以读取状态寄存器以检查正在进行的写入(WIP)位的值。在自定时扇区擦除周期中,WIP位为1,擦除完成后为0。在周期完成之前的某个未指定时间,写使能锁存(WEL)位被重置。一个扇区擦除(SE)指令,应用于由块保护(BP2, BP1, BPO)位保护的页(见表2和表3)不执行。

3.2.3 全擦除指令 BE

全擦除指令将flash内的所有位都置1

3.2.4 读状态寄存器 RDSR

WIP位:Write In Progress (WIP)位表示内存是否正忙于写状态寄存器、程序或擦除周期。当设置为1时,这样的周期正在进行,当重置为0时,没有这样的周期在进行。

WEL位:写使能锁存(WEL)位表示内部写使能锁存的状态。当设置为1时,内部写使能锁存设置,当设置为0时,内部写使能锁存复位,不接受写状态寄存器,程序或擦除指令。

BP2、BP1、BPO位:块保护(BP2、BP1、BPO)位是非易失性的。它们定义了被软件保护以防止程序和擦除指令的区域的大小。这些位是用WRSR指令写入的。当块保护位(BP2、BP1、BPO)中的一个或两个位设置为1时,相关的内存区域(如下图中定义的)将受到保护,不受页程序(PP)和扇区擦除(SE)指令的影响。未设置“硬件保护”模式时,可以写入块保护位(BP2、BP1、BO)。当且仅当块保护(BP2, BP1, BPO)位都为0时,执行Bulk Erase (BE)指令。

SRWD位:状态寄存器写禁止(SRWD)位与写保护(W)信号一起工作。状态寄存器写禁止(SRWD)位和写保护(W)信号允许设备进入硬件保护模式(当状态寄存器写禁止(SRWD)位被置为1,写保护(W)被置为Low)。在这种模式下,状态寄存器(SRWD, BP2, BP1, BPO)的非易失位变成只读位,写状态寄存器(WRSR)指令不再被执行。

3.2.5 页编程指令 PP

如果数据超过256字节,那么前面的数据会被覆盖,只有最后的256字节数据会被保留。如果小于256字节,则正常写入,且不会对本页其他字节数据造成影响

如果页编程的字节地址不是全零的话,就会在本页内循环写入数据(到了本页最后一字节,下一字节会回到本页起始地址编程,即字节地址全零的地方开始

3.2.6 读取指令 READ

可以任意地址读,读完该地址后,地址自加1;

写指令和擦除指令执行时,读取指令不会执行,且不会影响正在执行的指令。

3.2.7 读ID RDID

S拉低 + 读ID指令 + 3字节ID(设备ID、存储类型、存储容量)+ S拉高

读取识别(RDID)指令在数据输出期间的任何时间通过驱动芯片选择(S)高来终止。

当擦除或程序周期正在进行时,任何读标识(RDID)指令都不会被解码,并且对正在进行的周期没有影响。

四、工程实践

1.系统框图

2.程序设计

2.1SPI接口设计
/*SPI主机接口模块,采用模式3*/
module spi_master(
    input               clk         ,
    input               rst_n       ,
//user
    input   [7:0]       din         ,
    input               req         ,//主机传输数据请求 加uart 改为din_vld
    output  [7:0]       dout        ,//读出的数据
    output              done    ,   
//spi_slave
    output              sclk        ,//串行时钟信号
    output              cs_n        ,//片选信号
    output              mosi        ,//主机输出从机输入信号
    input               miso         //主机输入从机输入信号
);							 

//参数定义
    parameter   SCLK_PERIOD = 16, //16分频 3.125M 320ns
                SCLK_LOW    = 4 ,
                SCLK_HIGH   = 12,
                BIT_NUM     = 8 ; //bit数参数

    parameter   IDLE  = 4'b0001,  //空闲状态   
                READY = 4'b0010,  //准备传输状态  
                DATA  = 4'b0100,  //传输过程状态  
                DONE  = 4'b1000;  //传输完成状态  

//中间信号
    reg     [3:0]       state_c     ; //状态机
    reg     [3:0]       state_n     ;

    reg     [3:0]       cnt_sclk    ;//串行时钟计数器
    wire                add_cnt_sclk;
    wire                end_cnt_sclk;

    reg     [2:0]       cnt_bit     ; //比特计数器
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;

    reg                 spi_sclk    ;
    reg                 spi_mosi    ;
    
    reg     [7:0]       rd_data     ; //读取的数据

    wire                idle2ready  ; //状态机跳转条件
    wire                ready2data  ;
    wire                data2done   ;
    wire                done2idle   ;

//状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            state_c <= IDLE;
        end
        else begin 
            state_c <= state_n;
        end
    end

    always @(*)begin
        case (state_c)
            IDLE  :begin
                    if(idle2ready)begin
                        state_n = READY;
                    end 
                    else 
                        state_n = state_c;
            end
            READY :begin
                    if(ready2data )begin
                        state_n = DATA ;
                    end 
                    else 
                        state_n = state_c;
            end
            DATA  :begin
                    if(data2done )begin
                        state_n = DONE;
                    end 
                    else 
                        state_n = state_c;
            end
            DONE  :begin
                    if(done2idle)begin
                        state_n = IDLE;
                    end 
                    else 
                        state_n = state_c;
            end       
            default:  state_n = IDLE;
        endcase       
        end

    assign idle2ready  = (state_c == IDLE ) && (req        );        
    assign ready2data  = (state_c == READY) && (1'b1       );
    assign data2done   = (state_c == DATA ) && (end_cnt_bit);
    assign done2idle   = (state_c == DONE ) && (1'b1       );

//cnt_sclk 
    always @(posedge clk or negedge rst_n) begin 
        if (!rst_n) begin
            cnt_sclk <= 'd0;
        end
        else if(add_cnt_sclk)begin 
            if (end_cnt_sclk) begin
                cnt_sclk <= 'd0;
            end
            else begin 
                cnt_sclk <= cnt_sclk + 1'b1;
            end
        end
    end
    
    assign  add_cnt_sclk = (req);
    assign  end_cnt_sclk = add_cnt_sclk && cnt_sclk == (SCLK_PERIOD - 1);

//比特计数器cnt_bit
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_bit <= 'd0;
        end
        else if(add_cnt_bit)begin 
            if (end_cnt_bit) begin
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end
        end
    end
    
    assign  add_cnt_bit = (state_c == DATA ) && end_cnt_sclk;
    assign  end_cnt_bit = add_cnt_bit && cnt_bit == BIT_NUM - 1;

//spi_sclk  生成一个时钟,低电平开始,可画图
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            spi_sclk <= 1'b1;
        end
        else if(cnt_sclk == SCLK_LOW - 1)begin 
            spi_sclk <= 1'b0;
        end
        else if(cnt_sclk == SCLK_HIGH - 1)begin 
            spi_sclk <= 1'b1;
        end
    end

//spi_csn
    
//spi_mosi   主机输出 模式3  前沿(下降沿)输出  高位传输
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            spi_mosi <= 1'b0;
        end
        else if(cnt_sclk == SCLK_LOW - 1)begin 
            spi_mosi <= din[7-cnt_bit];
        end
    end

// rd_data
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            rd_data <= 'd0;
        end
        else if(cnt_sclk == SCLK_HIGH - 1)begin 
            rd_data[7-cnt_bit] <= miso;
        end
    end

//输出描述
    assign sclk = spi_sclk  ;
    assign cs_n = ~req   ;
    assign mosi = spi_mosi  ;
    assign dout = rd_data   ;
    assign done = end_cnt_bit;

endmodule
2.2 SPI读写Flash控制模块设计
2.2.1 控制模块状态跳转

2.2.2 控制模块代码
module spi_contrl #(parameter READ_MAX = 5,WRITE_MAX=5,WAIT_MAX=150000000,DONE_MAX=250000)(
    input                clk                ,
    input                rst_n              ,
    input                wr_en              ,
    input                rd_en              ,
    input                done               ,
    input                busy               ,
    input      [7:0]     rx_data            ,//页写输入数据
    input                rx_data_vld        ,//输入数据寄存信号
    input      [7:0]     din_data           ,//读取数据
    output     [7:0]     tx_data            ,
    output               tx_data_vld        ,//读数据发送到tx模块
    output reg [7:0]     dout_data          ,//输出给接口模块的数据
    output     reg       req                                 
);

/**************************************************************
                          信号定义       
**************************************************************/
reg	 [30:0]   cnt_delay                   ;
reg           delay_flag                  ;
wire		  add_cnt_delay               ;
wire          end_cnt_delay               ;	

reg	 [9:0]    cnt_byte                    ;
reg  [9:0]    byte_max                    ;
wire		  add_cnt_byte                ;
wire          end_cnt_byte                ;

wire          idle2wren1                  ;
wire          idle2read                   ;
wire          wren12se                    ;
wire          se2wait                     ;
wire          wait2wren2                  ;
wire          wren22pp                    ;
wire          pp2done                     ;
wire          done2idle                   ;
wire          read2done                   ;
reg  [2:0]    state                       ;

wire          wfifo_rd                    ;
wire          wfifo_wr                    ;
wire          wfifo_empty                 ;
wire          wfifo_full                  ;
wire  [7:0]   wfifo_qout                  ;
wire  [5:0]   wfifo_usedw                 ;

wire          rfifo_rd                    ;
wire          rfifo_wr                    ;
wire          rfifo_empty                 ;
wire          rfifo_full                  ;
wire  [7:0]   rfifo_qout                  ;
wire  [5:0]   rfifo_usedw                 ;

reg   [7:0]   dout_data_r                 ;
reg           dout_data_vld_r             ;

reg   [7:0]   din_data_r                  ;


reg  [7:0]    tx_data_r                   ;
parameter     IDLE   = 0                  ,
              WREN_1 = 1                  ,
              SE     = 2                  ,
              WAIT   = 3                  ,
              PP     = 5                  ,
              WREN_2 = 4                  ,
              DONE   = 7                  ,
              READ   = 6                  ;

/**************************************************************
                               命令码定义  
**************************************************************/
parameter         READ_ADDR_SECTOR  = 8'b0000_0000,//地址
                  READ_ADDR_PAGE    = 8'b0000_0000,
                  READ_ADDR_WORD    = 8'b0000_0000,
                  WRITE_ADDR_SECTOR = 8'b0000_0000,
                  WRITE_ADDR_PAGE   = 8'b0000_0000,
                  WRITE_ADDR_WORD   = 8'b0000_0000;

parameter         READ_CMD          = 8'b0000_0011,//指令
                  READ_ID_CMD       = 8'b1001_1111,
                  WREN_CMD          = 8'b0000_0110,
                  SE_CMD            = 8'b1101_1000,
                  PP_CMD            = 8'b0000_0010;

/**************************************************************
                           状态机      
**************************************************************/              
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        state <= IDLE;
    else case(state)
        IDLE		:	if(idle2wren1)
                            state <=WREN_1	;
                        else if(idle2read)
                            state <=READ	;
        WREN_1		:	if(wren12se 	 )
                            state <=SE	    ;
        SE	     	:	if(se2wait	     )
                            state <=WAIT	;
        WAIT	    :	if(wait2wren2 	 )
                            state <=WREN_2	;
        WREN_2	    :	if(wren22pp 	 )
                            state <=PP	    ;
        PP	     	:	if(pp2done	     )
                            state <=DONE	;                    
        DONE	    :	if(done2idle	 )
                            state <=IDLE	;
        READ	    :	if(read2done	 )
                            state <=DONE	;
        default     :	    state <=IDLE    ;
    endcase

assign     idle2wren1   =   state ==IDLE    &&  wr_en            ;               
assign     idle2read    =   state ==IDLE    &&  rd_en            ;               
assign     wren12se     =   state ==WREN_1  &&  end_cnt_delay    ;                           
assign     se2wait      =   state ==SE      &&  end_cnt_delay    ;               
assign     wait2wren2   =   state ==WAIT    &&  end_cnt_delay    ;               
assign     wren22pp     =   state ==WREN_2  &&  end_cnt_delay    ;               
assign     pp2done      =   state ==PP      &&  end_cnt_byte     ;
assign     done2idle    =   state ==DONE    &&  end_cnt_delay    ;
assign     read2done    =   state ==READ    &&  end_cnt_byte     ; 

/**************************************************************
                             延迟计数器    
**************************************************************/
//延迟计数器开启信号在数据传输完成后打开
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        delay_flag<='d0;
    else if (wren12se  || wren22pp  || se2wait || wait2wren2  || pp2done || done2idle)
        delay_flag<='d0;
    else if ((state == WREN_1 || state == WREN_2 || state == SE ) && end_cnt_byte)
        delay_flag<='d1;
    else if (state == WAIT ||  state == DONE)
        delay_flag<='d1;

//计数器 wait-3s  done-5ms  other-120ns
always@(posedge clk or negedge rst_n)	
    if(!rst_n)								
        cnt_delay <= 'd0;						
    else    if(add_cnt_delay) begin				
        if(end_cnt_delay)						
            cnt_delay <= 'd0;  				
        else									
            cnt_delay <= cnt_delay + 1'b1;		
    end											
assign add_cnt_delay = delay_flag ;
assign end_cnt_delay = add_cnt_delay && cnt_delay == ((state==WAIT)?(WAIT_MAX-1):(state==DONE?(DONE_MAX-1):5)) ;

/**************************************************************
                              字节计数器   
**************************************************************/	
//字节计数最大值控制
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        byte_max<=0;
    else if (state == READ)
        byte_max<=READ_MAX-1;
    else if (state == SE)
        byte_max<=3;
    else if (state == WREN_1||state == WREN_2) 
        byte_max<=0;
    else if (state == PP)
        byte_max<=WRITE_MAX-1;

//字节计数器   //接口模块传输一字节后返回done信号开启
always@(posedge clk or negedge rst_n)	
    if(!rst_n)								
        cnt_byte <= 'd0;
    else if (wren12se  || wren22pp  || se2wait || wait2wren2  || pp2done || done2idle)	
        cnt_byte <= 'd0;					
    else    if(add_cnt_byte) begin				
        if(end_cnt_byte)						
            cnt_byte <= 'd0;  				
        else									
            cnt_byte <= cnt_byte + 1'b1;		
    end											
assign add_cnt_byte =( (state == READ) || (state == SE) ||(state == PP)||(state == WREN_1)||(state == WREN_2) )&& done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_max ;

/**************************************************************
                               数据控制逻辑  
**************************************************************/
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        dout_data<='d0;
    else if (state == READ) begin
        case(cnt_byte)
            0              :          dout_data = READ_CMD         ;
            1              :          dout_data = READ_ADDR_SECTOR ;
            2              :          dout_data = READ_ADDR_PAGE   ;
            3              :          dout_data = READ_ADDR_WORD   ;
            READ_MAX-1     :          tx_data_r = din_data         ;
            default        :          tx_data_r = din_data         ;
        endcase
    end
    else if (state == SE) begin
        case(cnt_byte)
            0              :          dout_data = SE_CMD           ;
            1              :          dout_data = WRITE_ADDR_SECTOR;
            2              :          dout_data = WRITE_ADDR_PAGE  ;
            3              :          dout_data = WRITE_ADDR_WORD  ;
            default        :          dout_data = 8'h0             ; 
        endcase
    end
    else if (state == PP) begin
        case(cnt_byte)
            0              :          dout_data = PP_CMD           ;
            1              :          dout_data = WRITE_ADDR_SECTOR;
            2              :          dout_data = WRITE_ADDR_PAGE  ;
            3              :          dout_data = WRITE_ADDR_WORD  ;
            WRITE_MAX-1    :          dout_data = wfifo_qout       ;
            default        :          dout_data = wfifo_qout       ; 
    endcase
    end
    else if (state == WREN_1||state == WREN_2) begin
        case(cnt_byte)
            0              :          dout_data = WREN_CMD         ;
            default        :          dout_data = 8'h0             ; 
        endcase
    end

/**************************************************************
                              req控制逻辑   
**************************************************************/
always @(posedge clk or negedge rst_n) 
    if(!rst_n)
        req <= 1'd0;
    else if(end_cnt_byte || delay_flag)
        req <= 1'b0; 
    else if((state == READ) || (state == SE) || (state == PP) || (state == WREN_1) || (state == WREN_2))
        req <= 1'b1;

/**************************************************************
                        fifo控制逻辑 
**************************************************************/
fifo_8x256	fifo_wr (
	.aclr   ( ~rst_n      ),
	.clock  ( clk         ),
	.data   ( rx_data     ),
	.rdreq  ( wfifo_rd    ),
	.wrreq  ( wfifo_wr    ),
	.empty  ( wfifo_empty ),
	.full   ( wfifo_full  ),
	.q      ( wfifo_qout  ),
	.usedw  ( wfifo_usedw )
	);
assign        wfifo_rd    =  (state == PP) && done && (cnt_byte > 3) ;
assign        wfifo_wr    =  ~wfifo_full && rx_data_vld              ;

fifo_8x256	fifo_rd (
	.aclr   ( ~rst_n      ),
	.clock  ( clk         ),
	.data   ( din_data_r  ),
	.rdreq  ( rfifo_rd    ),
	.wrreq  ( rfifo_wr    ),
	.empty  ( rfifo_empty ),
	.full   ( rfifo_full  ),
	.q      ( rfifo_qout  ),
	.usedw  ( rfifo_usedw )
	);
assign        rfifo_wr    =  ~rfifo_full && (state == READ) && (cnt_byte > 3)  && done ;                      
assign        rfifo_rd    =  (~rfifo_empty && busy) || read2done                       ;

/**************************************************************
                             返回数据寄存    
**************************************************************/
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        din_data_r<='d0;
    else
        din_data_r<=din_data;

/**************************************************************
                      读取数据寄存    
**************************************************************/
always@(posedge clk or negedge rst_n)
    if(!rst_n)begin
        dout_data_r     <=8'b0 ;   
        dout_data_vld_r <=1'b0 ;
    end
    else begin
        dout_data_r     <=rfifo_qout ;
        dout_data_vld_r <=rfifo_rd   ;    
    end

//输出端口    
assign tx_data       = dout_data_r    ;
assign tx_data_vld   = dout_data_vld_r;

endmodule
2.3 顶层模块设计
module top(
    input           clk           ,
    input           rst_n         ,
    input           rx            ,
    output          tx            ,
    input    [1:0]  key_in        ,
    output          sclk          ,
    output          cs_n          ,
    output          mosi          ,
    input           miso         
);
wire     [1:0]      key_out    ;
wire     [7:0]      rx_dout    ;
wire                rx_dout_vld;
wire     [7:0]      tx_din     ;
wire                tx_din_vld ;
wire     [7:0]      dout_data  ;
wire                req        ;
wire     [7:0]      din_data   ;
wire                done       ;
wire                tx_done    ;
spi_contrl  #(.READ_MAX (20),.WRITE_MAX(20),.WAIT_MAX(150000000),.DONE_MAX(250000)) u_spi_contrl(
    .clk         (clk         ),
    .rst_n       (rst_n       ),
    .wr_en       (key_out[0]  ),
    .rd_en       (key_out[1]  ),
    .done        (done        ),
    .busy        (tx_done     ),
    .rx_data     (rx_dout     ),
    .rx_data_vld (rx_dout_vld ),
    .din_data    (din_data    ),
    .tx_data     (tx_din      ),
    .tx_data_vld (tx_din_vld  ),
    .dout_data   (dout_data   ),
    .req         (req         )
);
spi_master u_spi_master(
    .clk   (clk       ),
    .rst_n (rst_n     ),
    .din   (dout_data ),
    .req   (req       ),
    .dout  (din_data  ),
    .done  (done      ),
    .sclk  (sclk      ),
    .cs_n  (cs_n      ),
    .mosi  (mosi      ),
    .miso  (miso      )
);

key_filter #(.KEY_W (2 ) ,.CNT_MAX_20MS(5 )  ) u_key_filter(
    .clk     (clk     ),
    .rst_n   (rst_n   ),
    .key_in  (key_in  ),
    .key_out (key_out )
);

uart_rx u_uart_rx(
    .clk         (clk         ),
    .rst_n       (rst_n       ),
    .rx_din      (rx          ),
    .rx_dout     (rx_dout     ),
    .rx_dout_vld (rx_dout_vld )
);

uart_tx u_uart_tx(
    .clk        (clk        ),
    .rst_n      (rst_n      ),
    .tx_din     (tx_din     ),
    .tx_din_vld (tx_din_vld ),
    .tx_dout    (tx    ),
    .tx_done    (tx_done    )
);

endmodule
2.4 其他模块设计

UART模块、按键消抖模块;

3.仿真测试

3.1 仿真测试代码
`timescale 1ns/1ps
module  top_tb();

parameter CLK_CYCLE = 20;
defparam u_top.u_spi_contrl.READ_MAX = 4 + 5;   //1字节指令,3字节ID,加上传输的数据量(此处设为5)
defparam u_top.u_spi_contrl.WRITE_MAX= 4 + 5;
defparam u_top.u_spi_contrl.WAIT_MAX=1500;
defparam u_top.u_spi_contrl.DONE_MAX=250;
reg	clk,rst_n;
reg   rx;
reg  [1:0] key_in;
always #(CLK_CYCLE/2) clk = ~clk;
wire   mosi;
wire   miso;
top u_top(
    .clk    (clk    ),
    .rst_n  (rst_n  ),
    .rx     (rx     ),
    .tx     (tx     ),
    .key_in (key_in ),
    .sclk   (sclk   ),
    .cs_n   (cs_n   ),
    .mosi   (mosi   ),
    .miso   (miso   )
);
m25p16 m25p16(sclk,mosi,cs_n,w,hold,miso);

integer  i;
task my_uart_rx;
input [7:0]data_in;
begin
    rx=0;
    #(CLK_CYCLE*434);
    for(i=0;i<=7;i=i+1)begin
        rx = data_in[i];
        #(CLK_CYCLE*434);
    end
   rx = 1;
   #(CLK_CYCLE*434);
end
endtask

initial begin
    clk = 1'b1;
    rst_n = 1'b0;
    rx=1;
    key_in=2'b11;
    #(CLK_CYCLE*2);
    rst_n = 1'b1;
    #(CLK_CYCLE*2);
    my_uart_rx(8'h33);
    #(CLK_CYCLE*2);
    my_uart_rx(8'h22);
    #(CLK_CYCLE*2);
    my_uart_rx(8'h44);
    #(CLK_CYCLE*2);
    my_uart_rx(8'h55);
    #(CLK_CYCLE*2);
    my_uart_rx(8'h66);
    #(CLK_CYCLE*200);
    key_in=2'b10;
    #(CLK_CYCLE*21);
    key_in=2'b11;
    wait(u_top.u_spi_contrl.done2idle);
    #(CLK_CYCLE*200);
        key_in=2'b01;
        #(CLK_CYCLE*21);
        key_in=2'b11;
    wait(u_top.u_spi_contrl.done2idle);
    #(CLK_CYCLE*20000);    
    $stop;
end


endmodule
3.2 仿真测试图

4.上板测试

本次测试采用JCOM上位机,观察下图可见,输入Flash的数据与从中读取到的输出数据一致,至此SPI控制Flash读写代码设计完成。

标签:wire,tx,读写,Flash,rx,SPI,指令,rst,data
From: https://blog.csdn.net/weixin_56481118/article/details/137072272

相关文章

  • jdk的SPI(Service Provider Interface)
    1、定义SPI是Java提供的一种服务发现机制,用于在运行时动态查找和加载实现特定接口的服务提供商。按照字面的意思是服务提供接口将接口与具体业务独立开来。实现调用方与实现方解耦。1.1API与SPI最简单的区别就是接口的属于哪一方,API接口属于实现方,SPI接口属于调用方,S......
  • 文件操作(1)【文件打开和关闭】【文件的顺序读写(各种函数)】【sprintf和sscanf的理解】
    一.什么是文件?在程序设计中我们一般谈的文件有两种:程序文件和数据文件1.程序文件程序文件是包含计算机程序代码的文件。它通常包含一系列指令和算法,用于执行特定的任务或实现特定的功能。程序文件可以由不同的编程语言编写,如C、Java、Python等。程序文件通过编译或解释等过......
  • ActiViz中的读写类Reader/Writer
    文章目录前言一、vtkDataReader类1.vtkDataReader类的概述和基本用法2.不同数据格式的读取方法和示例二、vtkDataWriter类1.vtkDataWriter类的概述和基本用法2.不同数据格式的写入方法和示例三、vtkXMLReader类和vtkXMLWriter类1.vtkXMLReade......
  • GeminiDB Cassandra接口新特性FLASHBACK发布:任意时间点秒级闪回
    本文分享自华为云社区《GeminiDBCassandra接口新特性FLASHBACK发布:任意时间点秒级闪回》,作者:GaussDB数据库。技术背景数据库作为现代信息系统的核心组成部分,承担着存储、管理和检索大量数据的重要任务。然而,在实际的业务运行过程中,由于各种原因,数据库可能会出现异常或者故......
  • SPI通信协议详解
    文章目录介绍SPI硬件电路移位示意图SPI时序开始与结束时序单元交换字节时序单元模式0(最常用)模式1模式2模式3发送时序指定地址写指定地址读介绍SPISPI由时钟线、主机发送线、主机接收线、从机选择线(一个或多个)组成,拥有高速的速率,使用比较简单,但是需要的线更多,更容......
  • I2C Block读写和SMBus Block读写区别
    资料来源:韦东山第三期 SMBusBlock读:对应i2c-tools中的函数:i2c_smbus_read_block_data() SMBusBlock写:对应i2c-tools中的函数:i2c_smbus_write_block_data() I2CBlock读:对应i2c-tools中的函数:i2c_smbus_read_i2c_block_data() I2CBlock写:对应i2c-tools中的......
  • 客快物流大数据项目(八十二):Kudu的读写原理 一般有用 看1
    Kudu的读写原理一、​​​​​​​工作模式Kudu的工作模式如下图,有些在上面的内容中已经介绍了,这里简单标注一下:每个kudutable按照hash或range分区为多个tablet;每个tablet中包含一个MemRowSet以及多个DiskRowSet;每个DiskRowSet包含BaseData以及DeltaStores;Delta......
  • 蓝桥杯嵌入式之AT24C02各种数据的读写
    一、1字节8为的读写u8a=10;u8temp;eeprom_write(0x00,a); temp=eeprom_read(0x00); sprintf(text,"  temp=%d ",temp);      LCD_DisplayStringLine(Line1,(u8*)text);      memset(text,'\0',strlen(text));二、对于uint16_t、int16_t......
  • 启用Flashback Database闪回数据库功能
     若想顺利的使用闪回数据库功能,需要先将数据库置于闪回数据库状态。此文记录开启闪回数据库功能的步骤,注意调整过程需要重启数据库并确保数据库处于归档模式。1.确认数据库是否开启FlashbackDatabase功能若未开启,则继续,若已经开启,请从此小文儿飘过~~SYS@ora11g>selectflashbac......
  • Mysql数据库——主从复制与读写分离
    目录前言一、主从复制1.主从复制的定义2.Mysql主从复制支持的类型3.主从复制的过程4. 主从复制出现的问题5.解决方法二、读写分离1.读写分离的定义2.读写分离的作用3.读写分离作用场景3.1基于程序代码内部实现3.2基于中间代理层实现4.主从复制与读写分离三、搭......