[Evaluation of domestic FPGA Gaoyun GW1N-4 series development board] FLASH operation
[Copy link]
This post was last edited by 怀穿少年梦 on 2021-12-28 14:12
1. Purpose
Use Gaoyun GW1N-4 series development board to erase and write FLASH . The FLASH model used is W25Q 64JV .
2. Features of W25Q64JVSSIQ
1. Supports 3 communication modes: SPI , Dual SPI and Quad SPI . The storage unit of FLASH cannot write bit 1 , only bit 0 , so the original data must be erased (FFh) before writing data , and no processing is performed when writing bit 1. The characteristics of W25QQ 64JV are shown in the following figure :
2. FLASH storage structure
W25Q64JV consists of 128 blocks from Block0 to Block127 , with a capacity of 64M-bit, that is, 64M =128*64*1024*8bit/1024/1024 . Each Block consists of 16 sectors from Sector0 to Sector15 , each sector is 4KB in size and consists of 16 pages . Taking the first sector as an example, the first page address starts from xx0000h~xx00FF , the second page address starts from xx0100~xx01FF , the third page address starts from xx0200~xx02FF , and so on ... The 16th page address starts from xx0F00~xx0FFF . The size of each page is 256 bytes , and you will see later that Page Programd supports a maximum of 256 bytes . The explanation block diagram is as follows :
- FLASH related operation timing diagram
1 ) Write enable timing
2 ) Full Erase Timing
3 ) Page write timing
Draw a waveform according to the above timing, which is the SPI simulation timing waveform. Because the FPGA clock is 50MHz , 50M is divided into four, and 32 clock cycles are delayed by 640ns . It can be used as the delay period between two data intervals.
3. Programming
1. Full Erase Procedure
1) Full erase waveform drawing
2) Write code
//`timescale1ns/1ns
/*
Module Name : flash_ce
Project Name: flash
Description : flash全擦除模块
把主时钟四分频成12.5MHz,作为spi的输入时钟。
写一次数据与下一次数据之间必须等待50ns,每个时钟周期只能写入1bit数据,写一个字节即需要8个spi时钟周期,即32个系统时钟周期,说明一条完整指令需要640ns
*/
module flash_ce(
input wire sys_clk, //系统时钟,频率50MHz
input wire sys_rst_n, //复位信号,低电平有效
input wire key, //按键输入信号
output reg spi_cs_n, //片选信号
output reg spi_clk, //串行时钟
output reg spi_mosi //主输出从输入数据
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter IDLE = 4'b0001 , //初始状态 状态机四个状态
WR_EN = 4'b0010 , //写状态
DELAY = 4'b0100 , //等待状态
CE = 4'b1000 ; //全擦除状态
parameter WR_EN_INST= 8'b0000_0110, //写使能指令06
CE_INST = 8'b1100_0111; //全擦除指令C7
//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 ; //比特计数器,产生高低位,控制MOSI输出
//除了在空闲状态,其他状态都进行系统时钟计数器,如果只使用cnt_clk作为状态跳转的约束条件不充分,
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
cnt_clk <= 5'd0;
else if(state != IDLE)
cnt_clk <= cnt_clk + 1'b1;
end
//由于状态跳转的约束条件不充分,所以使用cnt_byte 计数器对cnt_clk的计数周期进行计数,同时使用cnt_byte增加一次表示延时640ns
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
cnt_byte <= 3'd0;
else if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))//当写完擦除指令后,等待640ns,片选拉高,此时cnt_byte = 6,并且刚好cnt_clk进行一个计数循环,就清零
cnt_byte <= 3'd0;
else if(cnt_clk == 5'd31)//cnt_clk每完成一个循环周期cnt_byte就加1,其他不变
cnt_byte <= cnt_byte + 1'b1;
end
//在进行写使能指令以及全擦除指令,并且已经发送一个字节时,记录系统时钟的个数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
cnt_sck <= 2'd0;
else if((state == WR_EN) && (cnt_byte == 1'b1)) // 当进行写使能时,先等待640ns,即cnt_byte =1
cnt_sck <= cnt_sck + 1'b1;
else if((state == CE) && (cnt_byte == 3'd5)) //当写完使能信号,等待640ns,再片选拉高,继续等待640ns,再片选拉低,继续等待640ns,此时cnt_byte =5,此时又开始写擦除指令
cnt_sck <= cnt_sck + 1'b1;
end
//进行四分频,产生12.5MHz时钟.根据cnt_sck 记录系统时钟的个数,2个cnt_sck为低,2个cnt_sck为高
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
spi_clk <= 1'b0;
else if(cnt_sck == 2'd0)
spi_clk <= 1'b0;
else if(cnt_sck == 2'd2)
spi_clk <= 1'b1;
end
//片选信号输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
spi_cs_n <= 1;
else if(key == 1'b1)
spi_cs_n <= 0;
else if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
spi_cs_n <= 1;
else if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
spi_cs_n <= 0;
else if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == CE))
spi_cs_n <= 1;
end
//cnt_bit 高低位对调,控制MOSI输出。用于输出MOSI的哪一位
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
cnt_bit <= 3'd0;
else if(cnt_clk == 2'd2)
cnt_bit <= cnt_bit + 1'b1;
end
//state 状态机跳转
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
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 <= CE;
CE:if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
state <= IDLE;
default:
state <= IDLE;
endcase
end
//MOSI输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(!sys_rst_n)
spi_mosi <= 1'b0;
else if((state == WR_EN) && (cnt_byte == 3'd2))
spi_mosi <= 1'b0;
else if((state == CE) && (cnt_byte == 3'd6))
spi_mosi <= 1'b0;
else if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
spi_mosi <= WR_EN_INST[7-cnt_bit];
else if((state == CE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
spi_mosi <= CE_INST[7-cnt_bit];
end
endmodule
3) Co-simulation with Modelsim . (For the co-simulation video, please refer to the B station Gaoyun Semiconductor video tutorial Gaoyun
Create a project file, add related files to the project, and cancel optimization. This simulation uses the M25P16 simulation module for simulation. The simulation waveform is as follows:
Write Enable
Erase instruction
Erase Complete
4 ) Online logic analyzer waveform
a . When setting the pin, the MSPI pin is used , so please right-click on the Process page and select configuration , as shown in the figure, and select MSPI as the regular pin in Dual-purpose . Otherwise, an error will appear during compilation.
Compile Errors
b . Create a GAO file; refer to the tutorial of the forum master for detailed steps. The link is as follows:
[Evaluation of domestic FPGA Gaoyun GW1N-4 series development board]——4. Use of embedded logic analyzer - Domestic chip exchange - Electronic Engineering World Forum (eeworld.com.cn)
c . On the online logic analysis page, I made a mistake. I jumped the jumper cap to JTAG mode in advance, but I still used USB download. As a result, the program could not be downloaded and it prompted that the code did not match. In fact, the GWIN1-4B MINI development board already has JTAG download, which uses the FT232H chip to support the JTAG download mode. This mistake made me stagnant for a few days. Thanks to the forum leader @gs001588 for his help . Thank you very much.
d . The online logic waveform is not uploaded because there is no screenshot
2. Page code writing
1 ) Page write waveform drawing, reference timing drawing
2 ) Write code according to the waveform diagram
moduleflash_pp_ctrl(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire key , //按键输入信号
outputreg cs_n , //片选信号
outputreg sck , //串行时钟
outputreg mosi //主输出从输入数据
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter IDLE = 4'b0001 , //初始状态
WR_EN = 4'b0010 , //写状态
DELAY = 4'b0100 , //等待状态
PP = 4'b1000 ; //页写状态
parameter WR_EN_INST = 8'b0000_0110, //写使能指令
PP_INST = 8'b0000_0010; //页写指令
parameter SECTOR_ADDR = 8'b0000_0000, //扇区地址
PAGE_ADDR = 8'b0000_0100, //页地址
BYTE_ADDR = 8'b0010_0101; //字节地址
parameter NUM_DATA = 8'd100 ; //页写数据个数(0-99)
//reg define
reg [7:0] cnt_byte ; //字节计数器
reg [3:0] state ; //状态机状态
reg [4:0] cnt_clk ; //系统时钟计数器
reg [1:0] cnt_sck ; //串行时钟计数器
reg [2:0] cnt_bit ; //比特计数器
reg [7:0] data ; //页写入数据
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk ornegedge 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 ornegedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_byte <=8'd0;
else if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 8'd9))
cnt_byte <=8'd0;
else if(cnt_clk == 5'd31)
cnt_byte <=cnt_byte + 1'b1;
//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk ornegedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_sck <=2'd0;
else if((state == WR_EN) && (cnt_byte == 8'd1))
cnt_sck <=cnt_sck + 1'b1;
else if((state == PP) && (cnt_byte >= 8'd5)
&& (cnt_byte <= NUM_DATA + 8'd9 - 1'b1))
cnt_sck <=cnt_sck + 1'b1;
//cs_n:片选信号
always@(posedge sys_clk ornegedge 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 == 8'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
cs_n <=1'b1;
else if((cnt_byte == 8'd3) && (cnt_clk == 5'd31) && (state == DELAY))
cs_n <=1'b0;
else if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31) && (state == PP))
cs_n <=1'b1;
//sck:输出串行时钟
always@(posedge sys_clk ornegedge 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 ornegedge 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;
//data:页写入数据
always@(posedge sys_clk ornegedge sys_rst_n)
if(sys_rst_n == 1'b0)
data <=8'd0;
else if((cnt_clk == 5'd31) && ((cnt_byte >= 8'd9)
&& (cnt_byte < NUM_DATA + 8'd9 - 1'b1)))
data <=data + 1'b1;
//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk ornegedge 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 == 8'd2) && (cnt_clk == 5'd31))
state <=DELAY;
DELAY:if((cnt_byte == 8'd3) && (cnt_clk == 5'd31))
state <=PP;
PP: if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31))
state <=IDLE;
default: state <=IDLE;
endcase
//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk ornegedge sys_rst_n)
if(sys_rst_n == 1'b0)
mosi <=1'b0;
else if((state == WR_EN) && (cnt_byte== 8'd2))
mosi <=1'b0;
else if((state == PP) && (cnt_byte == NUM_DATA + 8'd9))
mosi <=1'b0;
else if((state == WR_EN) && (cnt_byte == 8'd1) && (cnt_sck == 5'd0))
mosi <=WR_EN_INST[7 - cnt_bit];//写使能指令
else if((state == PP) && (cnt_byte == 8'd5) && (cnt_sck == 5'd0))
mosi <=PP_INST[7 - cnt_bit]; //页写指令
else if((state == PP) && (cnt_byte == 8'd6) && (cnt_sck == 5'd0))
mosi <=SECTOR_ADDR[7 - cnt_bit];//扇区地址
else if((state == PP) && (cnt_byte == 8'd7) && (cnt_sck == 5'd0))
mosi <=PAGE_ADDR[7 - cnt_bit]; //页地址
else if((state == PP) && (cnt_byte == 8'd8) && (cnt_sck == 5'd0))
mosi <=BYTE_ADDR[7 - cnt_bit]; //字节地址
else if((state == PP) && ((cnt_byte >= 8'd9)
&& (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) && (cnt_sck == 5'd0))
mosi <=data[7 - cnt_bit];//页写入数据
endmodule
3 ) Online logic diagram waveform
Write Enable
Write instruction
Write Address
|