基于FPGA的SPI FLASH控制器设计

2023-01-05,,,,

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控制器设计的相关教程结束。

《基于FPGA的SPI FLASH控制器设计.doc》

下载本文的Word格式文档,以方便收藏与打印。