07_flash full erase experiment
1. SPI protocol
1.1 SPI protocol
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
One master multi slave SPI communication device connection diagram
1.3 SPI protocol layer
1.3.1 Timing diagram of SPI communication mode
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.
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.
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)
3.2 Schematic diagram of onboard Flash
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)
full erase timing
3.3.2 Write enable timing
Write enable (Write Enable) command, referred to as WREN, the operation command is 8'b0000_0110 (06h).
Write enable instruction detailed introduction and operation timing
3.3.3 Serial input timing diagram
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:
- write enable
- The device enters a write-latched state
- Full Erase Write BE
- When writing, pull down the s chip select signal
- Write complete pulls s
- Wait for a certain period of time to complete after completion
4. Block Diagram
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
Detailed Waveforms
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.
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