07_flash full erase experiment

1. SPI protocol

1.1 SPI protocol

insert image description here
insert image description here

1.2 SPI physical layer

For the physical layer of the SPI protocol, what needs to be explained is the connection method of the SPI communication device and the function description of the device pin. The communication mode of the SPI communication device is a master-slave communication mode, and the communication parties are divided into master-slave. According to the number of slave devices, the connection mode between SPI communication devices can be divided into one master-slave and one master-multi-slave.
One master one slave SPI communication device connection diagram
insert image description here
One master multi slave SPI communication device connection diagram
insert image description here
insert image description here

insert image description here

1.3 SPI protocol layer

1.3.1 Timing diagram of SPI communication mode

insert image description here
CPOL = 0, SCK is low level in idle state
CPOL = 1, SCK is high level in idle state
CPHA = 0, data sampling is on the odd edge of SCK clock, even number is updated.
CPHA = 1, data sampling is on the even edge of SCK clock, odd edge is updated.

1.3.2 SPI communication mode when CPHA=0

CPHA = 0, data sampling is on odd edge of SCK clock, even edge is updated.
insert image description here

1.3.3 SPI communication mode when CPHA=1

CPHA = 1, data sampling is on the even edge of SCK clock, odd edge is updated.
insert image description here

2. Experimental objectives

Burn the running lamp program in the Flash chip in advance, power on the FPGA to execute the running lamp program, download the flash chip full erase program to the FPGA internal SRAM and execute it, erase the running lamp program burned in the Flash chip, and after the FPGA is powered on again, no program will be executed.

3. SPI-Flash chip

3.1 Hardware resources

The Flash model is W25Q16 and the storage capacity is 16Mbit (2M bytes)
insert image description here

3.2 Schematic diagram of onboard Flash

insert image description here

3.3 Operation Timing

3.3.1 Full Erase Timing

Full erase (Bulk Erase) operation, referred to as BE, the operation instruction is 8'b1100_0111 (C7h)
insert image description here
full erase timing
insert image description here

3.3.2 Write enable timing

Write enable (Write Enable) command, referred to as WREN, the operation command is 8'b0000_0110 (06h).
insert image description here
Write enable instruction detailed introduction and operation timing
insert image description here

3.3.3 Serial input timing diagram

insert image description here
SPI-Flash is calculated by taking 12.5MHZ. A clock cycle is 80ns, and it takes 640ns to transmit 8 bits.
Also need to pay attention to the t_slch delay, >=5ns, here is 640ns for convenience.

For full erasing, all bit bits should be changed to 1 (BE operation becomes all 1). Before this, there must be a write enable command.
Achieving a full erase requires the following six steps:

  1. write enable
  2. The device enters a write-latched state
  3. Full Erase Write BE
  4. When writing, pull down the s chip select signal
  5. Write complete pulls s
  6. Wait for a certain period of time to complete after completion

4. Block Diagram

insert image description here

5. Waveform diagram

The input of the board is 50MHZ, but the protocol is based on 12.5MHZ. Note here.
640ns requires 32 50MHZ clock cycles. SPI is 0/0 mode.
When cnt_byte == 1 and cnt_sck == 2, because in 0/0 mode, even edge updates are performed. Odd edge
sampling even edge updates data.
Approximate waveform

Counter Specifications
insert image description here
Detailed Waveforms
insert image description here
insert image description here
insert image description here
insert image description here

6. RTL

6.1 flash_be_ctrl

`timescale  1ns/1ns




module  flash_be_ctrl
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            BE      =   4'b1000 ;   //全擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
            BE_INST     =   8'b1100_0111;   //全擦除指令

//reg   define
reg     [2:0]   cnt_byte;   //字节计数器
reg     [3:0]   state   ;   //状态机状态
reg     [4:0]   cnt_clk ;   //系统时钟计数器
reg     [1:0]   cnt_sck ;   //串行时钟计数器
reg     [2:0]   cnt_bit ;   //比特计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  3'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))
        cnt_byte    <=  3'd0;
    else    if(cnt_clk == 31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == BE) && (cnt_byte == 3'd5))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == BE))
        cs_n    <=  1'b1;

//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
                state   <=  BE;
        BE:     if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd2))
        mosi    <=  1'b0;
    else    if((state == BE) && (cnt_byte == 3'd6))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令
    else    if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
        mosi    <=  BE_INST[7 - cnt_bit];       //全擦除指令

endmodule

6.2 spi_flash_be

`timescale  1ns/1ns




module  spi_flash_be
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值

//wire  define
wire    po_key  ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//------------- flash_be_ctrl_inst -------------
flash_be_ctrl  flash_be_ctrl_inst
(

    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号

    .sck        (sck        ),  //片选信号
    .cs_n       (cs_n       ),  //串行时钟
    .mosi       (mosi       )   //主输出从输入数据
);

endmodule

7. Testbench

Additional files are required for testing.
insert image description here

7.1 tb_flash_be_ctrl

`timescale  1ns/1ns


module  tb_flash_be_ctrl();

//wire  define
wire            cs_n    ;   //Flash片选信号
wire            sck     ;   //Flash串行时钟
wire            mosi    ;   //Flash主输出从输入信号

//reg   define
reg     sys_clk     ;   //模拟时钟信号
reg     sys_rst_n   ;   //模拟复位信号
reg     key         ;   //模拟全擦除触发信号

//时钟、复位信号、模拟按键信号
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        key <=  1'b0;
        #100
        sys_rst_n   <=  1'b1;
        #1000
        key <=  1'b1;
        #20
        key <=  1'b0;
    end

always  #10 sys_clk <=  ~sys_clk;   //模拟时钟,频率50MHz

//写入Flash仿真模型初始值(全F)
defparam memory.mem_access.initfile = "initmemory.txt";

//------------- flash_be_ctrl_inst -------------
flash_be_ctrl  flash_be_ctrl_inst
(
    .sys_clk    (sys_clk    ),  //输入系统时钟,频率50MHz,1bit
    .sys_rst_n  (sys_rst_n  ),  //输入复位信号,低电平有效,1bit
    .key        (key        ),  //按键输入信号,1bit

    .sck        (sck        ),  //输出串行时钟,1bit
    .cs_n       (cs_n       ),  //输出片选信号,1bit
    .mosi       (mosi       )   //输出主输出从输入数据,1bit
);

//------------- memory -------------
m25p16  memory
(
    .c          (sck    ),  //输入串行时钟,频率12.5Mhz,1bit
    .data_in    (mosi   ),  //输入串行指令或数据,1bit
    .s          (cs_n   ),  //输入片选信号,1bit
    .w          (1'b1   ),  //输入写保护信号,低有效,1bit
    .hold       (1'b1   ),  //输入hold信号,低有效,1bit

    .data_out   (       )   //输出串行数据
);

endmodule

7.2 tb_spi_flash_be

`timescale  1ns/1ns




module  tb_spi_flash_be();

//wire  define
wire    cs_n;
wire    sck ;
wire    mosi ;

//reg   define
reg     clk     ;
reg     rst_n   ;
reg     key     ;

//时钟、复位信号、模拟按键信号
initial
    begin
        clk =   0;
        rst_n   <=  0;
        key <=  0;
        #100
        rst_n   <=  1;
        #1000
        key <=  1;
        #20
        key <=  0;
    end

always  #10 clk <=  ~clk;

defparam memory.mem_access.initfile = "initmemory.txt";

//-------------spi_flash_erase-------------
spi_flash_be    spi_flash_be_inst
(
    .sys_clk    (clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (rst_n  ),  //复位信号,低电平有效
    .pi_key     (key    ),  //按键输入信号

    .sck        (sck    ),  //串行时钟
    .cs_n       (cs_n   ),  //片选信号
    .mosi       (mosi   )   //主输出从输入数据
);

m25p16  memory
(
    .c          (sck    ),  //输入串行时钟,频率12.5Mhz,1bit
    .data_in    (mosi   ),  //输入串行指令或数据,1bit
    .s          (cs_n   ),  //输入片选信号,1bit
    .w          (1'b1   ),  //输入写保护信号,低有效,1bit
    .hold       (1'b1   ),  //输入hold信号,低有效,1bit

    .data_out   (       )   //输出串行数据
);

endmodule

Guess you like

Origin blog.csdn.net/HeElLose/article/details/131627014