FPGA implements Flash read and write operations

1. Introduction

The Flash model used in this article is M25P16, which is a model of ST Company (there seems to be another model with the same name, which is from another company). The capacity is 16Mbit, SPI interface, the clock rate can reach 50Mhz. To complete the read, write and erase operations on Flash, you only need to understand two points: SPI timing and Flash operation instructions. Additional details and some concepts can be supplemented during the learning process.

2. SPI

SPI has four modes in total, controlled by two variables CPOL and CPHA respectively. The following two modes of this Flash chip

  1. The first one is all 0, the clock is low when the clock is idle, the data is output on the falling edge of the clock, and input on the rising edge. What needs to be noted in this mode is that the output of the first bit has no falling edge, and it has to be output manually in advance.
  2. The second is all 1, the clock is high when the clock is idle, the data is output on the falling edge of the clock, and input on the rising edge.

Generally speaking, the second mode is used to read and write Flash.

Please add a picture description

3. Flash command

All the instructions of Flash are as follows, not too many, and some of them will not be used. The instructions that must be used are described below

Please add a picture description

(1). WREN 和 WRDI

One of these two instructions is to enable writing, and the other is to disable writing. In general, only WREN can be used to read. As for why, the related writing instructions will give the answer later.
Please add a picture description

(2) RDIDs

The device ID number used to read Flash is usually used to test whether the SPI bus is correct. After writing the SPI protocol, you can use this command to test.

Please add a picture description

(3) READ

After sending the instruction and the first address to read, the next step is to read the data. Every time a data is read, the address will be incremented by one. When the address boundary is reached, the address will automatically jump to address 0 for reading. The amount of data is not limited and can be read indefinitely. After reading the last byte, you only need to pull the S signal high to end the reading operation of this part.
Please add a picture description

(4) PP

Write operation to flash, in page unit, each page has 256bytes, a total of 8192 pages, the details are as follows.

Please add a picture description

Then the timing diagram is as follows,

Please add a picture description

Then comes the important point, which means that when executing the PP command, you need to execute the WREN command first and lock it. Maybe after the PP command ends, the WREN command will become invalid. According to the last line , it is indeed true during the test.

Please add a picture description

Please add a picture description

(5) SE

The erase command is very simple. After the instruction and address are given, it will be ok. After the erase is completed, when it is read again, it should be all FF. Similarly, the erase command is the same as the PP command, and the WREN command needs to be sent first. The rest is gone.

Please add a picture description

Please add a picture description

4. Other matters needing attention

Tcsh: Indicates the time interval between two instructions,

Tpp: After the page program, you need to wait for the Tpp time before proceeding to the next instruction

Tse: Same as Tpp.
Please add a picture description

Please add a picture description

Five. Verilog implementation

Write process: WREN -> SE -> WREN -> PP (PP can only write 1 to 0, so it must be erased before PP)

Read process: READ

SPI module

`timescale 1ns/1ps



//MODE : CPOL=1 CPHA=1
//spi_clk = sys_clk / 2
module SPI_Master (
    //system interface
    input   wire            sys_clk,
    input   wire            sys_rstn,

    //user inferface
    //user read
    input   wire            read_req,
    output  wire[7:0]       read_data,
    output  wire            read_ack,

    //user write
    input   wire            write_req,
    input   wire[7:0]       write_data,
    output  wire            write_ack,


    //spi to external flash
    output  wire            spi_clk,
    output  wire            spi_mosi,
    input   wire            spi_miso
    //output  wire            spi_csn

);
    

localparam SPI_IDLE         = 4'b0001;
localparam SPI_DATA         = 4'b0010;
localparam SPI_END          = 4'b0100;
localparam SPI_END2         = 4'b1000;  

reg[3:0]    state , next_state;

reg         spi_clk_reg;
reg         spi_mosi_reg;
reg         spi_csn_reg;

reg         spi_clk_inverse_cnt;
reg[3:0]    spi_rev_send_bit_cnt;
reg[7:0]    write_data_reg;

reg[7:0]    read_data_reg;

assign      spi_clk           = spi_clk_reg;
assign      spi_mosi          = spi_mosi_reg;
assign      spi_csn           = spi_csn_reg;
assign      read_data         = read_data_reg;

assign      read_ack          = (state == SPI_END) ? 1'b1 : 1'b0;
assign      write_ack         = (state == SPI_END) ? 1'b1 : 1'b0;

always @(posedge sys_clk or negedge sys_rstn) begin
    if (sys_rstn == 1'b0)
        state <= SPI_IDLE;
    else
        state <= next_state;
end


always@(*)begin
    case (state)
        SPI_IDLE: 
            if( write_req == 1'b1  || read_req == 1'b1)
                next_state <= SPI_DATA;
            else
                next_state <= SPI_IDLE;
        SPI_DATA:
            if( spi_rev_send_bit_cnt == 'd7 && spi_clk_inverse_cnt == 1'b1)
                next_state <= SPI_END;
            else
                next_state <= SPI_DATA;
        SPI_END:
            next_state <= SPI_END2;
        SPI_END2:
            next_state <= SPI_IDLE;
        default: next_state <= SPI_IDLE; 
    endcase
end


always @(posedge sys_clk or negedge sys_rstn) begin
    if( sys_rstn == 1'b0)
        spi_clk_inverse_cnt <= 1'b0;
    else if( state == SPI_DATA)
        spi_clk_inverse_cnt <= spi_clk_inverse_cnt + 1'b1;
    else
        spi_clk_inverse_cnt <= 1'b0;       
end

//spi csn out
// always @(posedge sys_clk or negedge sys_rstn) begin
//     if( sys_rstn == 1'b0 )
//         spi_csn_reg <= 1'b1;
//     else if( state == SPI_IDLE && (write_req == 1'b1  || read_req == 1'b1))
//         spi_csn_reg <= 1'b0;
//     else if( state == SPI_DATA)
//         spi_csn_reg <= 1'b0;
//     else
//         spi_csn_reg <= 1'b1;
// end

//spi write/read data bit cnt
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        spi_rev_send_bit_cnt <= 4'd0;
    else if( spi_clk_inverse_cnt == 1'b1)
        spi_rev_send_bit_cnt <= spi_rev_send_bit_cnt + 1'b1;
    else if( state == SPI_DATA)
        spi_rev_send_bit_cnt <= spi_rev_send_bit_cnt;
    else
        spi_rev_send_bit_cnt <= 4'd0;
end

//mosi data shift
always @(posedge sys_clk or negedge sys_rstn) begin
    if( sys_rstn == 1'b0)
        write_data_reg <= 8'd0;
    else if( state == SPI_IDLE && (write_req == 1'b1  || read_req == 1'b1))
        write_data_reg <= write_data;
    else if( state == SPI_DATA && spi_clk_inverse_cnt == 1'b1)
        write_data_reg <= {
    
    write_data_reg[6:0],write_data_reg[7]};
    else
        write_data_reg <= write_data_reg;
end

//spi_clk_gen
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        spi_clk_reg <= 1'b1;
    else if(state == SPI_DATA)
        spi_clk_reg <= ~spi_clk_reg;
    else   
        spi_clk_reg <= 1'b1;
end

//mosi data out
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        spi_mosi_reg <= 1'b1;
    else if(state == SPI_DATA  && write_req == 1'b1)
        spi_mosi_reg <= write_data_reg[7];
    else    
        spi_mosi_reg <= 1'b1;
end


//miso data in
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        read_data_reg <= 1'b0;
    else if(state == SPI_DATA && spi_clk_inverse_cnt == 1'b1)
        read_data_reg <= {
    
    read_data_reg[6:0] , spi_miso};
    else
        read_data_reg <= read_data_reg;
end


endmodule

Flash module





//flash  write/read control   : M25P16
module flash_contorl (
    //system interface
    input   wire            sys_clk,
    input   wire            sys_rstn,


    //user interface
    //read identification
    input   wire            read_id_req,
    output  wire[23:0]      flash_id,
    output  wire            read_id_end,

    //read data
    input   wire            read_req,
    input   wire[23:0]      read_addr,
    input   wire[9:0]       read_size,
    output  wire[7:0]       read_data,    
    output  wire            read_ack,
    output  wire            read_end,

    //write enalbe
    input   wire            write_enable_req,
    output  wire            write_enable_end,

    //write disalbe
    //write data
    input   wire            write_req, 
    input   wire[23:0]      write_page,     //write num page
    input   wire[8:0]       write_size,     //max equal 256

    input   wire[7:0]       write_data,
    output  wire            write_ack,
    output  wire            write_end,
    
    //erase sector
    input   wire            erase_sector_req,
    input   wire[23:0]      erase_sector_addr,
    output  wire            erase_sector_end,

    //erase bulk
    input   wire            erase_bulk_req,
    output  wire            erase_bulk_end,

    //spi to external flash
    output  wire            spi_clk,
    output  wire            spi_mosi,
    input   wire            spi_miso,
    output  wire            spi_csn
);



//control flash instruction    
`define Write_Enable                    8'h06
`define Write_Disable                   8'h04
`define Read_Identification             8'h9F
`define Read_Status_Reg                 8'h05
`define Write_Status_Reg                8'h01
`define Read_Data_Bytes                 8'h03
`define Read_Data_At_Higher_Speed       8'h0B
`define Page_Program                    8'h02
`define Sector_Erase                    8'hD8
`define Bulk_Erase                      8'hC7               


`define Read_Identification_Bytes       4'd4
`define Sector_Erase_Bytes              4'd4
`define Bulk_Erase_Bytes                4'd1

localparam  Page_Size                   =   9'd256;
localparam  Write_Enable_Wait           =   'd10;
localparam  Read_Data_Bytes_Wait        =   'd10;               //wait      60ns
localparam  Sector_Erase_Wait           =   32'd35_000_000;     //wait      640ms
localparam  Page_Program_Wait           =   32'd350_000;        //wait       640us
localparam  Bulk_Erase_Wait             =   32'd650_000_000;    //wait      13s


localparam  Flash_Idle                  =   13'b0_0000_0000_0001;
localparam  Flash_Write_Enable          =   13'b0_0000_0000_0010;
localparam  Flash_Write_Disable         =   13'b0_0000_0000_0100;
localparam  Flash_Read_Identification   =   13'b0_0000_0000_1000;
localparam  Flash_Read_Status_Reg       =   13'b0_0000_0001_0000;
localparam  Flash_Write_Status_Reg      =   13'b0_0000_0010_0000;
localparam  Flash_Read_Data_Bytes       =   13'b0_0000_0100_0000;
localparam  Flash_Read_Data_At_hSpeed   =   13'b0_0000_1000_0000;
localparam  Flash_Page_Program          =   13'b0_0001_0000_0000;
localparam  Flash_Sector_Erase          =   13'b0_0010_0000_0000;
localparam  Flash_Bulk_Erase            =   13'b0_0100_0000_0000;
localparam  Flash_Wait                  =   13'b0_1000_0000_0000;
localparam  Flash_End                   =   13'b1_0000_0000_0000;



reg[12:0]   state , next_state;

reg[12:0]   state_ts;

//spi read
reg         spi_read_req;
wire[7:0]   spi_read_data;
wire        spi_read_ack;

//spi write
reg         spi_write_req;
reg[7:0]    spi_write_data;
wire        spi_write_ack;

reg         spi_csn_reg;

reg[9:0]    spi_wr_byte_cnt;


reg[23:0]   flash_id_reg;

reg[32:0]   pp_erase_wait_cnt;

assign      spi_csn             =   spi_csn_reg;   //to spi flash csn

assign      flash_id            =   flash_id_reg;
assign      read_id_end         =   ((spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0;


assign      read_data           =   spi_read_data;
assign      read_ack            =   ((state == Flash_Read_Data_Bytes) && (spi_wr_byte_cnt > 'd3) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0;   //读出数据有效
assign      read_end            =   ((state == Flash_Read_Data_Bytes) &&(spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1) ? 1'b1 : 1'b0;

assign      write_enable_end    =   ((state == Flash_Write_Enable) &&(spi_wr_byte_cnt == 'd0) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0;


assign      write_ack           =   ((state == Flash_Page_Program) && (spi_wr_byte_cnt > 'd3) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0;    //请求下一个数据
assign      write_end           =   ((state == Flash_Page_Program) && (spi_wr_byte_cnt == write_size + 'd1 + 'd3 - 1'b1) && spi_write_ack == 1'b1) ? 1'b1 : 1'b0;

assign      erase_sector_end    =   ((state == Flash_Sector_Erase) && (spi_wr_byte_cnt == `Sector_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1 ) ? 1'b1 : 1'b0; 
assign      erase_bulk_end      =   ((state == Flash_Bulk_Erase) && (spi_wr_byte_cnt == `Bulk_Erase_Bytes - 1'b1)   && spi_write_ack == 1'b1 ) ? 1'b1 : 1'b0; 

always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        state <= Flash_Idle;
    else
        state <= next_state;
end


always@(*)begin
    case (state)
        Flash_Idle: 
            if( read_id_req == 1'b1)
                next_state <= Flash_Read_Identification;
            else if( write_enable_req == 1'b1)
                next_state <= Flash_Write_Enable;
            else if( write_req == 1'b1 )
                next_state <= Flash_Page_Program;
            else if( read_req == 1'b1 )
                next_state <= Flash_Read_Data_Bytes;
            else if( erase_sector_req == 1'b1)
                next_state <= Flash_Sector_Erase;
            else if( erase_bulk_req == 1'b1 )
                next_state <= Flash_Bulk_Erase;
            else
                next_state <= Flash_Idle; 

        Flash_Read_Identification:
            if( (spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1)
                next_state <= Flash_End;
            else
                next_state <= Flash_Read_Identification;
        Flash_Write_Enable:
            if( spi_write_ack == 1'b1)
                next_state <= Flash_Wait;
            else
                next_state <= Flash_Write_Enable;
        Flash_Page_Program:
            if( (spi_wr_byte_cnt == write_size + 'd1 + 'd3 - 1'b1) && spi_write_ack == 1'b1)  //写入指令 + 地址 + 数据
                next_state <= Flash_Wait;
            else
                next_state <= Flash_Page_Program;
        Flash_Read_Data_Bytes:
            if( (spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1)  //写入指令 + 地址 + 数据   
                next_state <= Flash_Wait;
            else
                next_state <= Flash_Read_Data_Bytes;
        Flash_Sector_Erase:
            if( (spi_wr_byte_cnt == `Sector_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1)
                next_state <= Flash_Wait;
            else
                next_state <= Flash_Sector_Erase;
        Flash_Bulk_Erase:
            if( (spi_wr_byte_cnt == `Bulk_Erase_Bytes - 1'b1) && spi_write_ack == 1'b1)
                next_state <= Flash_Wait;
            else
                next_state <= Flash_Bulk_Erase;
        Flash_Wait:
            if( state_ts == Flash_Page_Program && pp_erase_wait_cnt == Page_Program_Wait)
                next_state <= Flash_End;
            else if(state_ts == Flash_Sector_Erase && pp_erase_wait_cnt == Sector_Erase_Wait)
                next_state <= Flash_End;
            else if(state_ts == Flash_Bulk_Erase && pp_erase_wait_cnt == Bulk_Erase_Wait)
                next_state <= Flash_End;
            else if(state_ts == Flash_Read_Data_Bytes && pp_erase_wait_cnt == Read_Data_Bytes_Wait)
                next_state <= Flash_End;
            else if(state_ts == Flash_Write_Enable && pp_erase_wait_cnt == Write_Enable_Wait)
                next_state <= Flash_End;

            else
                next_state <= Flash_Wait;
        Flash_End:
            next_state <= Flash_Idle;
        default:    next_state <= Flash_Idle;
    endcase
end

always@(posedge sys_clk or negedge sys_rstn ) begin
    if( sys_rstn == 1'b0)
        state_ts <= Flash_Idle;
    else if( state == Flash_Idle )
        if( write_req == 1'b1 )
            state_ts <= Flash_Page_Program;
        else if( erase_sector_req == 1'b1)
            state_ts <= Flash_Sector_Erase;
        else if( erase_bulk_req == 1'b1 )
            state_ts <= Flash_Bulk_Erase;
        else if( read_req == 1'b1)
            state_ts <= Flash_Read_Data_Bytes;
        else if( write_enable_req == 1'b1)
            state_ts <= Flash_Write_Enable;
        else
            state_ts <= Flash_Idle; 
    else
        state_ts <= state_ts;
end


always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        pp_erase_wait_cnt <= 'd0;
    else if(state == Flash_Wait)
        pp_erase_wait_cnt <= pp_erase_wait_cnt + 1'b1;
    else    
        pp_erase_wait_cnt <= 'd0;

end

// spi csn
always@(posedge sys_clk or negedge sys_rstn )begin
    if( sys_rstn == 1'b0)
        spi_csn_reg <= 1'b1;
    else if( state == Flash_Idle || state == Flash_End || state == Flash_Wait)
        spi_csn_reg <= 1'b1;
    else
        spi_csn_reg <= 1'b0;
end
//spi read/write byte cnt
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0 )
        spi_wr_byte_cnt <= 'd0;
    else if( state != next_state)   
        spi_wr_byte_cnt <= 'd0;
    else if( spi_read_ack == 1'b1 || spi_write_ack == 1'b1)
        spi_wr_byte_cnt <= spi_wr_byte_cnt + 1'b1;
    else    
        spi_wr_byte_cnt <= spi_wr_byte_cnt;
end


//spi read
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0 )
        spi_read_req <= 1'b0;
    
    else if( state == Flash_Read_Identification && spi_wr_byte_cnt > 'd0)  
        if((spi_wr_byte_cnt == `Read_Identification_Bytes - 1'b1) && spi_read_ack == 1'b1)
            spi_read_req <= 1'b0;
        else
            spi_read_req <= 1'b1;
    else if(state == Flash_Read_Data_Bytes && spi_wr_byte_cnt > 'd3)
        if((spi_wr_byte_cnt == read_size + 'd1 + 'd3 - 1'b1) && spi_read_ack == 1'b1)
            spi_read_req <= 1'b0;
        else
            spi_read_req <= 1'b1;
    else
        spi_read_req <= 1'b0;
end


//read flash identification
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0)
        flash_id_reg <= 'd0;
    else if( state == Flash_Read_Identification && spi_wr_byte_cnt > 'd0)
        if( spi_read_ack == 1'b1)
            flash_id_reg <= {
    
    flash_id_reg[15:0],spi_read_data};
        else
            flash_id_reg <= flash_id_reg;
    else
        flash_id_reg <= flash_id_reg;
end


//spi write req
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0 )
        spi_write_req <= 1'b0;
    else if( state == Flash_Write_Enable)
        spi_write_req <= 1'b1;
    else if( state == Flash_Read_Identification && spi_wr_byte_cnt == 'd0)
        spi_write_req <= 1'b1;
    else if( state == Flash_Page_Program)
        spi_write_req <= 1'b1;
    else if( state == Flash_Read_Data_Bytes && spi_wr_byte_cnt < 'd4)
        spi_write_req <= 1'b1;
    else if( state == Flash_Sector_Erase && spi_wr_byte_cnt < 'd4 )
        spi_write_req <= 1'b1;
    else if( state == Flash_Bulk_Erase )    
        spi_write_req <= 1'b1;
    else
        spi_write_req <= 1'b0;
end

//spi write data
always@(posedge sys_clk or negedge sys_rstn)begin
    if( sys_rstn == 1'b0 )
        spi_write_data <= 8'd0;
    else if( state == Flash_Write_Enable )
        spi_write_data <= `Write_Enable;
    else if( state == Flash_Read_Identification && spi_wr_byte_cnt == 'd0)
        spi_write_data <= `Read_Identification;    
    else if( state == Flash_Page_Program)
        case(spi_wr_byte_cnt)
        'd0:     spi_write_data <= `Page_Program;
        'd1:     spi_write_data <= write_page[23:16];
        'd2:     spi_write_data <= write_page[15:8];
        'd3:     spi_write_data <= write_page[7:0];    
        default: spi_write_data <= write_data;
        endcase
    else if(state == Flash_Read_Data_Bytes)
        if( spi_wr_byte_cnt == 'd0)
            spi_write_data <= `Read_Data_Bytes;
        else if( spi_wr_byte_cnt == 'd1)
            spi_write_data <= read_addr[23:16];
        else if( spi_wr_byte_cnt == 'd2)
            spi_write_data <= read_addr[15:8];
        else
            spi_write_data <= read_addr[7:0];
    else if( state == Flash_Sector_Erase)
        if( spi_wr_byte_cnt == 'd0)
            spi_write_data <= `Sector_Erase;
        else if( spi_wr_byte_cnt == 'd1)
            spi_write_data <= erase_sector_addr[23:16];
        else if( spi_wr_byte_cnt == 'd2)
            spi_write_data <= erase_sector_addr[15:8];
        else
            spi_write_data <= erase_sector_addr[7:0];
    else if( state == Flash_Bulk_Erase)
        spi_write_data <= `Bulk_Erase;
    else
        spi_write_data <= 8'd0;
end




SPI_Master SPI_Master_hp(
    //system interface
    /*input   wire            sys_clk */    .sys_clk    (sys_clk),
    /*input   wire            sys_rstn*/    .sys_rstn   (sys_rstn),

    //user inferface
    //user read
    /*input   wire            read_req */   .read_req   (spi_read_req),
    /*output  wire[7:0]       read_data*/   .read_data  (spi_read_data),
    /* output  wire            read_ack*/   .read_ack   (spi_read_ack),

    //user write
    /*input   wire            write_req */  .write_req  (spi_write_req),
    /*input   wire[7:0]       write_data*/  .write_data (spi_write_data),
    /*output  wire            write_ack */  .write_ack  (spi_write_ack),


    //spi to external flash
    /*output  wire            spi_clk */   .spi_clk     (spi_clk), 
    /*output  wire            spi_mosi*/   .spi_mosi    (spi_mosi),
    /*input   wire            spi_miso*/   .spi_miso    (spi_miso)
    /*output  wire            spi_csn */   //.spi_csn     ()

);

endmodule

The value of the test read ID is given below.
insert image description here
For the complete project, you can pay attention to reply FPGA read and write Flash acquisition

Guess you like

Origin blog.csdn.net/weixin_44678052/article/details/129294010