1.SPI FLASH的基本特征

本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV。支持3种通信方式,SPI、Dual SPI和Quad SPI。FLASH的存储单元无法写入bit 1,只能写入bit 0,所以写入数据之前要将原来的数据擦除(FFh),遇到写入bit 1的情况不作处理。W25Q128BV的特征为如下图所示:

2.SPI FLASH的基本结构

W25Q128BV由Block0~Block255共256个Block组成,容量大小为256*64KB=256*64*1024*8bit/1024/1024=128M-bit=16M-Byte。每个Block由Sector0~Sector15共16个Sector组成,每个Sector大小为4KB,由16个Page组成。以第一个Sector为例,第1个Page地址从xx0000h~xx00FF开始,第2个Page地址从xx0100~xx01FF开始,第3个Page地址从xx0200~xx02FF开始,以此类推...,第16个Page地址从xx0F00~xx0FFF开始。每个Page的大小为256个Byte组成,后面会看到Page Programd最大支持256个Byte。

3.SPI FLASH的状态寄存器

W25Q128BV有两个状态寄存器:状态寄存器1和状态寄存器2。这些状态寄存器的标志位在后面指令操作的时候可以用来判断指令是否完成。

4.SPI FLASH的指令

W25Q128BV的指令可以分为指令码后面没有地址和数据、指令码后面只有地址没有数据、指令码后面只有数据没有地址、指令码后面既有地址又有数据的情况。

4.1 Read Manufacturer / Device ID (90h)

90h指令用来读取厂商ID和设备ID,指令先发一个指令码90h,紧接着是24bit的地址码000000h,最后读取出来的第1个字节是厂商ID,第2个字节是设备ID。如果发送的24bit地址码是000001h,则第1个字节是设备ID,第2个字节才是厂商ID。由此可见,该指令后面既有地址又有读数据。

4.2 Write Enable (06h)

写使能指令用来置位状态寄存器1里面的WEL位。在每次Page Program、Sector Erase、Block Erase、Chip Erase指令前必须发写使能指令。写使能指令后面不带地址和数据。

4.3 Read Status Register-1 (05h) and Read Status Register-2 (35h)

读状态寄存器1的指令码位05h,读状态寄存器2的指令码为35h。读状态寄存器可以用来检测一些指令是否完成,比如上面的写使能指令发送完成后,可以读取状态寄存器1里面的WEL位,看是否为1,以此确定写使能是否成功。读状态寄存器指令码后面只有数据没有地址。

4.4 Write Disable (04h)

写不使能指令用来复位状态寄存器1的WEL位,指令码04h发送完成后不发送地址和数据。WEL位在Power-up以及Write Status Register、Erase/Program Security Registers、Page Program、Quad Page Program、Sector Erase、Block Erase、Chip Erase完成后自动复位。

4.5 Sector Erase (20h)

扇区擦除指令用来把一个指定扇区(4K-bytes)置位到擦除状态(FFh),在发送Sector Erase之前必须先执行Write Enable (06h)指令(置位WEL位)。

4.6 Page Program (02h)

Page Program指令支持1~256个bytes的数据在先前擦除过的位置写入,在Page Program之前必须执行写使能指令(置位WEL)。该指令先发送指令码02h,紧接着是24bit的地址码A23~A0,然后是至少1个数据字节。如果写入的是一个完整256字节数据的Page,则地址的最低8位必须是0。如果最后一个地址字节不是0,时钟数超过了剩余page长度,地址将跳到该page的开始位置。如果一次写入超过256个字节的数据,地址将跳到page的起始地址,并且覆盖先前写入的数据。尽管Page Program指令还没有执行完,读状态寄存器指令仍然能够检查BUSY位,如果BUSY位为1,则Page Program指令还在执行,否则Page Program已经执行完毕,设备可以执行其他指令了。在Page Program指令完成后,状态寄存器的WEL位被复位。

4.7 Read Data (03h)

读数据指令支持读取多个数据,先发送指令码03h,然后是24bit地址码,指令码和地址码在flash端的上升沿锁存,读出的数据字节在DO引脚的下降沿移出。当正在进行Erase、Program或者Write时进行读指令,这时读指令将被忽略,但对当前正在执行的这几个操作没有影响。

5 程序设计

程序里面的状态机根据指令后面是否带数据或者地址的情况跳转,操作流程如下:

1.Read Manufacturer / Device ID (90h),读取厂商ID和设备ID;

2.Write Enable (06h),置位WEL位;

3.Read Status Register-1 (05h),判断WEL位是否置位;

4.Sector Erase (20h),擦除第一个Sector(4KB);

5.Read Status Register-1 (05h),判断Sector Erase是否完成(轮询BUSY位,直到BUSY位为0,表示完成);

6.Write Disable (04h),复位WEL位;

7.Read Status Register-1 (05h),判断WEL是否复位;

8.Write Enable (06h),置位WEL位;

9.Read Status Register-1 (05h),判断WEL位是否置位;

10.Page Program (02h),将1个Page(256个字节)的数据写入flash;

11.Read Status Register-1 (05h),判断.Page Program是否完成(轮询BUSY位,直到BUSY位为0,表示完成);

12.Write Disable (04h),复位WEL位;

13.Read Status Register-1 (05h),判断WEL是否复位;

14.Read Data (03h),读出写入的256个字节。

程序清单如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2019/04/04 09:42:45
// Design Name:
// Module Name: flash_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////// module flash_top
(
input i_rst_n ,
input i_clk , //50MHz output o_flash_cs ,
output o_flash_clk ,
output o_flash_din ,
input i_flash_dout
); reg [ :] r_cmd_type ;
reg [ :] r_flash_cmd ;
reg [:] r_falsh_addr;
reg [ :] r_flash_wdata;
reg [ :] r_data_num ; wire r_op_done ;
wire r_flash_done;
wire [ :] r_flash_rdata; reg [ :] r_cnt ; reg r_clk_25MHz ; always @(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_clk_25MHz <= 'b0;
else
r_clk_25MHz <= !r_clk_25MHz;
end always @(posedge r_clk_25MHz or negedge i_rst_n)
begin
if(!i_rst_n)
begin
r_cmd_type <= 'd0;
r_flash_cmd <= 'd0;
r_falsh_addr<= 'd0;
r_flash_wdata<= 'd0;
r_data_num <= 'd0;
r_cnt <= 'd0;
end
else
begin
case(r_cnt)
'd0: //Read Manufacturer / Device ID (90h)
begin
if(r_op_done)
begin
if(r_flash_rdata == 'h17)
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1110; //命令类型
r_flash_cmd <= 'h90; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd1; //数据字节敿
end
end
'd1: //Write Enable (06h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1001; //命令类型
r_flash_cmd <= 'h06; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd2: //Read Status Register-1 (05h)
begin
if(r_op_done && r_flash_rdata[] == 'b1) //轮询WEL位,直到WEL置位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd3: //Sector Erase (20h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
//r_cnt <= 'd0;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1100; //命令类型
r_flash_cmd <= 'h20; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd4: //Read Status Register-1 (05h)
begin
if(r_op_done && r_flash_rdata[] == 'b0) //轮询BUSY位,直到BUSY复位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd5: //Write Disable (04h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1001; //命令类型
r_flash_cmd <= 'h04; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd6: //Read Status Register-1 (05h)
begin
if(r_flash_done && r_flash_rdata[] == 'b0) //轮询WEL位,直到WEL复位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd7: //Write Enable (06h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1001; //命令类型
r_flash_cmd <= 'h06; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd8: //Read Status Register-1 (05h)
begin
if(r_flash_done && r_flash_rdata[] == 'b1) //轮询WEL位,直到WEL置位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd9: //Page Program (02h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1101; //命令类型
r_flash_cmd <= 'h02; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h59; //数据
r_data_num <= 'd255; //数据字节敿
end
end
'd10: //Read Status Register-1 (05h)
begin
if(r_flash_done && r_flash_rdata[] == 'b0) //轮询BUSY位,直到BUSY复位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd11: //Write Disable (04h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1001; //命令类型
r_flash_cmd <= 'h04; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd12: //Read Status Register-1 (05h)
begin
if(r_flash_done && r_flash_rdata[] == 'b0) //轮询WEL位,直到WEL复位
//if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1011; //命令类型
r_flash_cmd <= 'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd0; //数据字节敿
end
end
'd13: //Read Data (03h)
begin
if(r_op_done)
begin
r_cnt <= r_cnt + 'b1;
r_cmd_type <= 'b0;
r_flash_cmd <= 'b0;
r_falsh_addr<= 'b0;
r_flash_wdata<= 'b0;
r_data_num <= 'b0;
end
else
begin
r_cmd_type <= 'b1110; //命令类型
r_flash_cmd <= 'h03; //命令砿 r_falsh_addr<= 24'h000000; //地址
r_falsh_addr<= 'b0;
r_flash_wdata<= 'h0; //数据
r_data_num <= 'd255; //数据字节敿
end
end
'd14:
begin
r_cnt <= 'd0;
end
endcase
end
end wire [:] o_rd_cnt ;
wire [:] o_flash_cstate; flash_dri flash_dri_inst
(
.i_rst_n (i_rst_n ),
.i_clk (r_clk_25MHz ), .i_cmd_type (r_cmd_type ),
.i_flash_cmd (r_flash_cmd ),
.i_falsh_addr (r_falsh_addr ),
.i_flash_data (r_flash_wdata ),
.i_data_num (r_data_num ), .o_op_done (r_op_done ), .o_flash_done (r_flash_done ),
.o_flash_data (r_flash_rdata ), .o_flash_cs (o_flash_cs ),
.o_flash_clk (o_flash_clk ),
.o_flash_din (o_flash_din ),
.i_flash_dout (i_flash_dout )
); endmodule
module flash_dri
(
input i_rst_n ,
input i_clk , //25MHz input [ :] i_cmd_type ,
input [ :] i_flash_cmd ,
input [:] i_falsh_addr,
input [ :] i_flash_data,
input [ :] i_data_num , output o_op_done , output o_flash_done,
output [ :] o_flash_data, output o_flash_cs ,
output o_flash_clk ,
output o_flash_din ,
input i_flash_dout
); parameter FLASH_IDLE = ;
parameter FLASH_SEND_CMD = ;
parameter FLASH_SEND_ADDR = ;
parameter FLASH_WR_DATA = ;
parameter FLASH_RD_DATA = ;
parameter FLASH_END = ; reg [:] flash_cstate ;
reg [:] flash_nstate ; reg [:] r_cmd_cnt ;
reg [:] r_addr_cnt ;
reg [:] r_data_cnt ; reg r_op_done ;
reg r_flash_done;
reg [:] r_flash_data; reg r_flash_cs ;
reg r_flash_din ; reg [:] r_wr_num ;
reg [:] r_rd_num ; reg r_busy ; reg r_rd_valid ;
reg [:] r_rd_cnt ;
reg r_wr_valid ;
reg [:] r_wr_cnt ; assign o_op_done = r_op_done ; assign o_flash_done= r_flash_done;
assign o_flash_data= r_flash_data; assign o_flash_cs = r_flash_cs ;
assign o_flash_clk = r_busy? i_clk:'b0 ;
assign o_flash_din = r_flash_din ; always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
flash_cstate <= FLASH_IDLE ;
else
flash_cstate <= flash_nstate ;
end always @(*)
begin
case(flash_cstate)
FLASH_IDLE:
begin
if(i_cmd_type[])
flash_nstate <= FLASH_SEND_CMD ;
else
flash_nstate <= FLASH_IDLE ;
end
FLASH_SEND_CMD:
begin
if(r_cmd_cnt == 'd7)
begin
if(i_cmd_type[:] == 'b001) //命令后不带地坿Ҍ数据
flash_nstate <= FLASH_END ;
else if(i_cmd_type[:] == 'b010) //命令后带写数据,无地坿
flash_nstate <= FLASH_WR_DATA ;
else if(i_cmd_type[:] == 'b011) //命令后带读数据,无地坿
flash_nstate <= FLASH_RD_DATA ;
else if(i_cmd_type[] == 'b1) //命令后带地址
flash_nstate <= FLASH_SEND_ADDR ;
else
flash_nstate <= FLASH_IDLE ;
end
else
begin
flash_nstate <= FLASH_SEND_CMD ;
end
end
FLASH_SEND_ADDR:
begin
if(r_addr_cnt == 'd23)
begin
if(i_cmd_type[:] == 'b00) //不带数据
flash_nstate <= FLASH_END ;
else if(i_cmd_type[:] == 'b01) //写数捿
flash_nstate <= FLASH_WR_DATA;
else if(i_cmd_type[:] == 'b10) //读数捿
flash_nstate <= FLASH_RD_DATA;
else
flash_nstate <= FLASH_IDLE ;
end
else
flash_nstate <= FLASH_SEND_ADDR ;
end
FLASH_WR_DATA:
begin
if(r_data_cnt == 'd7 && r_wr_num == 'd0)
flash_nstate <= FLASH_END ;
else
flash_nstate <= FLASH_WR_DATA;
end
FLASH_RD_DATA:
begin
if(r_data_cnt == 'd7 && r_rd_num == 'd0)
flash_nstate <= FLASH_END ;
else
flash_nstate <= FLASH_RD_DATA;
end
FLASH_END:
begin
flash_nstate <= FLASH_IDLE ;
end
default:
begin
flash_nstate <= FLASH_IDLE ;
end
endcase
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_rd_num <= 'd0;
else if(r_rd_cnt == 'd7)
begin
if(r_rd_num == 'd0)
r_rd_num <= 'd0;
else
r_rd_num <= r_rd_num - 'b1;
end
else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[:] == 'b1110)
r_rd_num <= i_data_num ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_wr_num <= 'd0;
else if(r_wr_cnt == 'd7)
begin
if(r_wr_num == 'd0)
r_wr_num <= 'd0;
else
r_wr_num <= r_wr_num - 'b1;
end
else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[:] == 'b1101)
r_wr_num <= i_data_num ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_op_done<= 'b0 ;
else if(flash_cstate == FLASH_IDLE)
r_op_done<= 'b0 ;
else if(flash_cstate == FLASH_END)
r_op_done<= 'b1 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_cmd_cnt <= 'd0 ;
else if(flash_cstate == FLASH_SEND_CMD)
begin
if(r_cmd_cnt == 'd7)
r_cmd_cnt <= 'd0 ;
else
r_cmd_cnt <= r_cmd_cnt + 'b1;
end
else
r_cmd_cnt <= 'd0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_addr_cnt <= 'd0 ;
else if(flash_cstate == FLASH_SEND_ADDR)
begin
if(r_addr_cnt == 'd23)
r_addr_cnt <= 'd0 ;
else
r_addr_cnt <= r_addr_cnt + 'b1;
end
else
r_addr_cnt <= 'd0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_data_cnt <= 'd0 ;
else if(flash_cstate == FLASH_RD_DATA || flash_cstate == FLASH_WR_DATA)
begin
if(r_data_cnt == 'd7)
r_data_cnt <= 'd0 ;
else
r_data_cnt <= r_data_cnt + 'b1;
end
else
r_data_cnt <= 'd0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_flash_din <= 'b1 ;
else if(flash_cstate == FLASH_SEND_CMD)
r_flash_din <= i_flash_cmd[ - r_cmd_cnt];
else if(flash_cstate == FLASH_SEND_ADDR)
r_flash_din <= i_falsh_addr[ - r_addr_cnt];
else if(flash_cstate == FLASH_WR_DATA)
r_flash_din <= i_flash_data[ - r_data_cnt];
else
r_flash_din <= 'b1 ;
end always @(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_flash_data <= 'd255 ;
else if(r_rd_valid)
r_flash_data[ - r_rd_cnt] <= i_flash_dout;
else if(flash_cstate == FLASH_IDLE)
r_flash_data <= 'd255 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_wr_valid <= 'b0 ;
else if(flash_cstate == FLASH_WR_DATA)
r_wr_valid <= 'b1;
else
r_wr_valid <= 'b0;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_wr_cnt <= 'd0 ;
else if(flash_cstate == FLASH_WR_DATA && r_wr_valid)
r_wr_cnt <= r_wr_cnt + 'b1;
else
r_wr_cnt <= 'd0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_rd_valid <= 'b0 ;
else if(flash_cstate == FLASH_RD_DATA)
r_rd_valid <= 'b1;
else
r_rd_valid <= 'b0;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_rd_cnt <= 'd0 ;
else if(flash_cstate == FLASH_RD_DATA && r_rd_valid)
r_rd_cnt <= r_rd_cnt + 'b1;
else
r_rd_cnt <= 'd0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_flash_done <= 'b0 ;
else if(r_rd_cnt == 'd7)
r_flash_done <= 'b1 ;
else
r_flash_done <= 'b0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_flash_cs <= 'b1 ;
//else if(flash_cstate == FLASH_IDLE && i_cmd_type[3] == 1'b1)
// r_flash_cs <= 1'b0 ;
else if(flash_cstate == FLASH_END)
r_flash_cs <= 'b1 ;
else if(flash_cstate == FLASH_SEND_CMD)
r_flash_cs <= 'b0 ;
end reg [:] r_num_cnt ; always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_num_cnt <= 'b0 ;
else if(r_flash_done)
r_num_cnt <= r_num_cnt + 'b1 ;
else if(flash_cstate == FLASH_IDLE)
r_num_cnt <= 'b0 ;
end always @(negedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
r_busy <= 'b0 ;
else if(flash_cstate == FLASH_END)
r_busy <= 'b0 ;
else if(flash_cstate == FLASH_SEND_CMD)
r_busy <= 'b1 ;
end endmodule

该程序代码已在Spartan-6 xc6slx16csg324-2硬件平台上调试通过,可以正常写入和读出数据。

5 完结

基于FPGA的SPI FLASH控制器设计已经介绍完毕,如有不妥之处望批评指正,谢谢!

基于FPGA的SPI FLASH控制器设计的更多相关文章

  1. 基于FPGA的XPT2046触摸控制器设计

    基于FPGA的XPT2046触摸控制器设计 小梅哥编写,未经许可,文章内容和所涉及代码不得用于其他商业销售的板卡 本实例所涉及代码均可通过向 xiaomeige_fpga@foxmail.com  发 ...

  2. 012 基于FPGA的网口通信实例设计【转载】

    一.网口通信设计分类 通过上面其他章节的介绍,网口千兆通信,可以使用TCP或者UDP协议,可以外挂PHY片或者不挂PHY片,总结下来就有下面几种方式完成通信: 图8‑17基于FPGA的网口通信实例设计 ...

  3. 基于FPGA的1553B通信模块的设计(转)

    reference:http://www.21ic.com/app/eda/201808/798483.htm https://www.milstd1553.com/ [导读] 摘 要: 提出一种将F ...

  4. verilog实验2:基于FPGA的59秒计时器设计

    一.实验任务 利用四个数码管显示59秒计时器. 二.代码实现 将开发板的48M晶振分频出1M,然后计数器累加,将计数器结果显示在数码管上.低位逢十进一,第二位逢五进一,依次构成59秒计时器. 部分代码 ...

  5. 基于FPGA的Cordic算法实现

    CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲线.指数.对数的 ...

  6. 基于反熔丝FPGA、QSPI FLASH的高可靠程序存储、启动控制系统

    1      涉及术语解释 1.1     三模冗余 三模冗余系统简称TMR(Triple Modular Redundancy),是最常用的一种容错设计技术.三个模块同时执行相同的操作,以多数相同的 ...

  7. 优化基于FPGA的深度卷积神经网络的加速器设计

    英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...

  8. 基于FPGA的DDS设计(一)

    最近在学习基于FPGA的DDS设计,借此机会把学习过程记录下来,当作自己的学习笔记也希望能够帮助到学习DDS的小伙伴. DDS(Direct Digital Synthesizer)直接数字合成器,这 ...

  9. RTThread DFS文件系统使用: 基于使用SFUD驱动的SPI FLASH之上的ELM FATFS文件系统

    参考博文: 博文很长,但是实际要操作的步骤没几下. http://m.elecfans.com/article/730878.html  为了防止几年后文章链接找不到,我把文章复制过来了 /***** ...

随机推荐

  1. phpexecl

    <?phpnamespace Admin\Controller;use Think\Controller;class InoutController extends Controller { p ...

  2. shell 解析json

    未完待续 ### 解析api json文件为csv文件 cd /api ` do id=$(echo ${i}|sed 's/.html//') echo -n "${id}|" ...

  3. Mybatis Generator配置文件完整配置详解

    完整的Mybatis Generator(简称MBG)的最完整配置文件,带详解,再也不用去看EN的User Guide了 可以搭配着mybatis generator的中文文档看:http://mbg ...

  4. Data Flow-File Read-基本过程

  5. 6 获取请求头和URL信息

    @app.route("/req",methods=['GET','POST'])def req(): print(request.headers) #请求头的信息全部在这里面 p ...

  6. hdu 3662 3D Convex Hull

    Problem - 3662 题意很简单,构造三维凸包,求凸包有多少个面. 代码如下: #include <cstdio> #include <iostream> #inclu ...

  7. H3C 代理ARP

  8. 【BestCoder Round #93 1004】MG loves set

    [题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=6022 [题意] 让你求一个集合的子集数目; 这个子集有要求; 即: 它所有元素的平方的和小于它所有 ...

  9. 54个提高PHP程序运行效率的方法

    1.在可以用file_get_contents替代file.fopen.feof.fgets等系列方法的情况下,尽量用 file_get_contents,因为他的效率高得多!但是要注意file_ge ...

  10. canvas+js实现验证码功能

    转载自:https://blog.csdn.net/qq_42463851/article/details/90755734<!DOCTYPE html> <html> < ...