SPI interface explanation based on FPGA -- flash M25P28 as an example

flash M25P128 page write

Precautions for M25P128 PP operation

The content of our experiment is to write a byte of data to flash:

The hardware and software environments used are:
Hardware: Zircon A4plus development board
Software: quartus II 13.1
From the technical manual, we can get the following information:


From the above information, we extract the following information:
1. Before PP operation, write enable operation must be executed;
2. PP operation continuous write can only write to one page, and only the last 256 data will be saved to the page if there are more than 256 data;
3. The whole operation obeys the SPI protocol

Sequence diagram design

From the timing chart in the manual, we can see that the timing is almost the same as that of flash sector erasure instruction, except that there are eight SCKs in the erasure instruction, so the designed timing chart is as follows:

I believe that the students have learned the sector erase instruction, and the instruction of PP operation can be easily understood.

The writing of flash write module

The tradition here doesn't talk nonsense. It goes directly to the code:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : flash_write.v
// Create Time  : 2020-01-09 11:14:30
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module flash_write(
    input                   sclk            ,
    input                   rst_n           ,
    input                   key_flag        ,
    output  reg             cs_n            ,
    output  reg             sck             ,
    output  reg             sdi             ,
    input           [ 7:0]  data_in         
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter   IDLE        =   4'b0001         ;
parameter   WREN        =   4'b0010         ;
parameter   DELAT       =   4'b0100         ;
parameter   PP          =   4'b1000         ;
parameter   WREN_INST   =   8'h06           ;
parameter   WRITE_INST  =   8'h02           ;
parameter   WRITE_ADDR  =   24'h00_03_21    ;

reg                 [ 3:0]  state           ;
reg                 [ 4:0]  cnt_32          ;
reg                 [ 2:0]  cnt_state       ;
reg                 [ 1:0]  cnt_4           ;
reg                 [ 5:0]  bit_cnt         ;
reg                 [ 7:0]  data_in_reg     ;

 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        data_in_reg     <=      7'd0;
    else if(key_flag == 1'b1)
        data_in_reg     <=      data_in;
    else
        data_in_reg     <=      data_in_reg;    

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cs_n            <=      1'b1;
    else if(key_flag == 1'b1)
        cs_n            <=      1'b0;
    else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2) 
         cs_n            <=     1'b1;
    else if(state == DELAT && cnt_32  == 'd31)
        cs_n            <=      1'b0;
    else if(state == PP && cnt_32 == 'd31 && cnt_state == 'd6)
        cs_n            <=      1'b1;
    else
        cs_n            <=      cs_n;        

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      IDLE;
    else case(state)
        IDLE    :   if(key_flag == 1'b1)
                        state           <=      WREN;
                    else
                        state           <=      state;                                    
        WREN    :   if(cnt_32 == 'd31 && cnt_state == 'd2)
                        state           <=      DELAT;
                    else
                        state           <=      state;                        
        DELAT   :   if(cnt_32  == 'd31)
                        state           <=      PP;
                    else
                        state           <=      state;                        
        PP      :   if(cnt_32 == 'd31 && cnt_state == 'd6)
                        state           <=      IDLE;
                    else
                        state           <=      state;
        default :   state           <=      IDLE;
    endcase
                        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_32          <=      5'd0;
    else if(cnt_32 == 'd31)
        cnt_32          <=      5'd0;
    else if(state != IDLE)
        cnt_32          <=      cnt_32 + 1'b1;
    else
        cnt_32          <=      5'd0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_state       <=      3'd0;
    else if(state == IDLE)
        cnt_state       <=      3'd0;
    else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2) 
        cnt_state       <=      3'd0;
    else if(state == DELAT && cnt_32 == 'd31)
        cnt_state       <=      3'd0;
    else if(state == PP && cnt_32 == 'd31 && cnt_state == 'd6)
        cnt_state       <=      3'd0;
    else if(cnt_32 == 'd31)
        cnt_state       <=      cnt_state + 1'b1;
    else
        cnt_state       <=      cnt_state;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_4           <=      'd0;
    else if(state == WREN && cnt_state == 'd1)
        cnt_4           <=      cnt_4 + 1'b1;
    else if(state == PP && cnt_state >= 'd1 && cnt_state <= 'd5)
        cnt_4           <=      cnt_4 + 1'b1;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sck             <=      1'b0;
    else if(cnt_4 == 'd0)
        sck             <=      1'b0;
    else if(cnt_4 == 'd2)
        sck             <=      1'b1;
    else
        sck             <=      sck;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sdi             <=      1'b0;
    else if(state == WREN && cnt_state == 'd1 && cnt_4 == 'd0)
        sdi             <=      WREN_INST[7-bit_cnt];
    else if(state == PP && cnt_state == 'd1 && cnt_4 == 'd0)
        sdi             <=      WRITE_INST[7-bit_cnt];
    else if(state == PP && cnt_4 == 'd0 && cnt_state > 'd1 && cnt_state <= 'd4)
        sdi             <=      WRITE_ADDR[31-bit_cnt];
    else if(state == PP && cnt_4 == 'd0 && cnt_state == 'd5)
        sdi             <=      data_in_reg[39-bit_cnt]; 
    else if(state == PP && cnt_32 == 'd31 && cnt_state == 'd5)
        sdi             <=      1'b0;
    else
        sdi             <=      sdi;
      
    
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        bit_cnt         <=      5'd0;
    else if(state == WREN && bit_cnt == 'd7 && cnt_4 == 'd2)
        bit_cnt         <=      5'd0;
    else if(state == PP && bit_cnt == 'd39 && cnt_4 == 'd2)
        bit_cnt         <=      5'd0;
    else if(cnt_4 == 'd2)
        bit_cnt         <=      bit_cnt + 1'b1;
    else
        bit_cnt         <=      bit_cnt;

endmodule

The above code is based on the flash erase module.

The code of flash write test module

In order to facilitate the students to debug the code, the test code of the module is directly given here:

`timescale 1ns / 1ps
`define     CLOCK   20
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : flash_earse_tb.v
// Create Time  : 2020-01-08 19:57:13
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module flash_write_tb();

reg                     sclk             ;
reg                     rst_n            ;
reg                     key_flag         ;
wire                    cs_n             ;
wire                    sck              ;
wire                    sdi              ;

initial begin
    sclk                <=          1'b0;
    rst_n               <=          1'b0;
    key_flag            <=          1'b0;
    #(100*`CLOCK)
    rst_n               <=          1'b1;
    #(100*`CLOCK)
    key_flag            <=          1'b1;
    #(`CLOCK)
    key_flag            <=          1'b0;
    #(1000*`CLOCK)
    key_flag            <=          1'b1;
    #(`CLOCK)
    key_flag            <=          1'b0;
end
always      #(`CLOCK/2)     sclk    <=      ~sclk;



flash_write flash_write_inst(
    .sclk               (sclk               ),
    .rst_n              (rst_n              ),
    .key_flag           (key_flag           ),
    .cs_n               (cs_n               ),
    .sck                (sck                ),
    .sdi                (sdi                ),
    .data_in            (8'haa              )
);

endmodule

Code for other modules

In order to facilitate the students to use the code directly, here we copy all the codes as follows, and the whole project can also be taken by the group itself:
key module code

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : key.v
// Create Time  : 2020-01-05 13:49:36
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module key(
    input                       sclk            ,
    input                       rst_n           ,
    input                       key             ,
    output  reg                 key_o       
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter       IDLE        =       4'b0001     ;    
parameter       S1          =       4'b0010     ;
parameter       S2          =       4'b0100     ;
parameter       S3          =       4'b1000     ;

reg                 [ 3:0]      state           ;
reg                 [ 9:0]      cnt             ;
reg                             key_r1          ;
reg                             key_r2          ;
reg                             key_r3          ;
reg                             nege_flag       ;
reg                             pose_flag       ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk)
    key_r1          <=      key;

always @(posedge sclk)
    key_r2          <=      key_r1;

always @(posedge sclk)
    key_r3          <=      key_r2;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        nege_flag       <=      1'b0;
    else if(key_r3 == 1'b1 && key_r2 == 1'b0)
        nege_flag       <=      1'b1;
    else
        nege_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        pose_flag       <=      1'b0;
    else if(key_r3 == 1'b0 && key_r2 == 1'b1) 
        pose_flag       <=      1'b1;
    else
        pose_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      IDLE;
    else case(state)
        IDLE    :   if(nege_flag == 1'b1)
                        state           <=      S1;
                    else
                        state           <=      IDLE;                        
        S1      :   if(cnt == 10'd999)
                        state           <=      S2;
                    else if(pose_flag == 1'b1)
                        state           <=      IDLE;
                    else
                        state           <=      S1;                        
        S2      :   if(pose_flag == 1'b1)
                        state           <=      S3;
                    else
                        state           <=      S2;                        
        S3      :   if(cnt == 10'd999)
                        state           <=      IDLE;
                    else if(nege_flag == 1'b1)
                        state           <=      S2;
                    else
                        state           <=      S3;
                        
        default :   state           <=      IDLE;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt             <=      10'd0;
    else if(state != S1 && state != S3)
        cnt             <=      10'd0;
    else
        cnt             <=      cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_o           <=      1'b0;
    else if(state == S1 && cnt == 10'd999) 
        key_o           <=      1'b1;
    else
        key_o           <=      1'b0;

endmodule

Code of the tip module:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : top.v
// Create Time  : 2020-01-08 21:18:52
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module top(
    input                   sclk            ,
    input                   rst_n           ,
    input                   key             ,
    output  wire            cs_n            ,
    output  wire            sck             ,
    output  wire            sdi             
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
wire                        key_flag        ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/

key key_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key                    (~key                   ),
    .key_o                  (key_flag               )
);

flash_write flash_write_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key_flag               (key_flag               ),
    .cs_n                   (cs_n                   ),
    .sck                    (sck                    ),
    .sdi                    (sdi                    ),
	.data_in			    (8'h30 					)
);


endmodule 

experimental result

Since there is no phenomenon here, we will verify the correctness of the module together in the flash reading operation of the next article. In debugging, I also use the logic analyzer to debug, and the timing results are correct.

Concluding remarks

Creation is not easy. Students who think the article is helpful can collect some praise and support. (projects are also in the group) students who have any opinions on the article or need to communicate further can join the following group:

Published 10 original articles, won praise 2, visited 304
Private letter follow

Tags: sublime

Posted on Fri, 10 Jan 2020 21:23:03 -0800 by stuartc1