基于FPGA的SPI FLASH控制器设计

1.SPI FLASH的基本特征

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

基于FPGA的SPI FLASH控制器设计_第1张图片

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。

基于FPGA的SPI FLASH控制器设计_第2张图片

3.SPI FLASH的状态寄存器

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

基于FPGA的SPI FLASH控制器设计_第3张图片

4.SPI FLASH的指令

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

基于FPGA的SPI FLASH控制器设计_第4张图片

基于FPGA的SPI FLASH控制器设计_第5张图片

基于FPGA的SPI FLASH控制器设计_第6张图片

4.1 Read Manufacturer / Device ID (90h)

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

基于FPGA的SPI FLASH控制器设计_第7张图片

4.2 Write Enable (06h)

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

基于FPGA的SPI FLASH控制器设计_第8张图片

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

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

基于FPGA的SPI FLASH控制器设计_第9张图片

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完成后自动复位。

基于FPGA的SPI FLASH控制器设计_第10张图片

4.5 Sector Erase (20h)

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

基于FPGA的SPI FLASH控制器设计_第11张图片

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位被复位。

基于FPGA的SPI FLASH控制器设计_第12张图片

4.7 Read Data (03h)

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

基于FPGA的SPI FLASH控制器设计_第13张图片

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        [ 3:0]    r_cmd_type    ;
    reg        [ 7:0]    r_flash_cmd    ;
    reg        [23:0]    r_falsh_addr;
    reg        [ 7:0]    r_flash_wdata;
    reg        [ 7:0]    r_data_num    ;
    
    wire            r_op_done    ;
    wire            r_flash_done;
    wire    [ 7:0]    r_flash_rdata;
    
    reg        [ 7:0]    r_cnt        ;
    
    reg                r_clk_25MHz    ;
    
    always @(posedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            r_clk_25MHz    <= 1'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 == 8'h17)
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1110;    //命令类型
                        r_flash_cmd    <= 8'h90;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd1;    //数据字节敿                    
                    end
                end
                'd1:    //Write Enable (06h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1001;    //命令类型
                        r_flash_cmd    <= 8'h06;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd2:    //Read Status Register-1 (05h)
                begin
                    if(r_op_done && r_flash_rdata[1] == 1'b1)    //轮询WEL位,直到WEL置位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                
                    end
                end
                'd3:    //Sector Erase (20h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        //r_cnt         <= 'd0;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1100;    //命令类型
                        r_flash_cmd    <= 8'h20;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd4:    //Read Status Register-1 (05h)
                begin
                    if(r_op_done && r_flash_rdata[0] == 1'b0)    //轮询BUSY位,直到BUSY复位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd5:    //Write Disable (04h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1001;    //命令类型
                        r_flash_cmd    <= 8'h04;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd6:    //Read Status Register-1 (05h)
                begin
                    if(r_flash_done && r_flash_rdata[1] == 1'b0)    //轮询WEL位,直到WEL复位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd7:    //Write Enable (06h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1001;    //命令类型
                        r_flash_cmd    <= 8'h06;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd8:    //Read Status Register-1 (05h)
                begin
                    if(r_flash_done && r_flash_rdata[1] == 1'b1)    //轮询WEL位,直到WEL置位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd9:    //Page Program (02h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1101;    //命令类型
                        r_flash_cmd    <= 8'h02;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h59;    //数据
                        r_data_num    <= 8'd255;    //数据字节敿                    
                    end
                end
                'd10:    //Read Status Register-1 (05h)
                begin
                    if(r_flash_done && r_flash_rdata[0] == 1'b0)    //轮询BUSY位,直到BUSY复位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd11:    //Write Disable (04h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1001;    //命令类型
                        r_flash_cmd    <= 8'h04;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd12:    //Read Status Register-1 (05h)
                begin
                    if(r_flash_done && r_flash_rdata[1] == 1'b0)    //轮询WEL位,直到WEL复位
                    //if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1011;    //命令类型
                        r_flash_cmd    <= 8'h05;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd0;    //数据字节敿                    
                    end
                end
                'd13:    //Read Data (03h)
                begin
                    if(r_op_done)
                    begin
                        r_cnt         <= r_cnt + 1'b1;
                        r_cmd_type    <= 4'b0;
                        r_flash_cmd    <= 8'b0;
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'b0;
                        r_data_num    <= 8'b0;
                    end
                    else
                    begin
                        r_cmd_type    <= 4'b1110;    //命令类型
                        r_flash_cmd    <= 8'h03;    //命令砿                        r_falsh_addr<= 24'h000000;    //地址
                        r_falsh_addr<= 24'b0;
                        r_flash_wdata<= 8'h0;    //数据
                        r_data_num    <= 8'd255;    //数据字节敿                    
                    end
                end
                'd14:
                begin
                    r_cnt    <= 'd0;
                end
            endcase
        end
    end
    
    wire    [7:0]    o_rd_cnt    ;
    wire    [2:0]    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    [ 3:0]    i_cmd_type    ,
        input    [ 7:0]    i_flash_cmd    ,
        input    [23:0]    i_falsh_addr,
        input    [ 7:0]    i_flash_data,
        input    [ 7:0]    i_data_num    ,
        
        output            o_op_done    ,
        
        output            o_flash_done,
        output    [ 7:0]    o_flash_data,
        
        output    o_flash_cs    ,
        output    o_flash_clk    ,
        output    o_flash_din    ,
        input    i_flash_dout
    );
    
    parameter    FLASH_IDLE        =    0    ;
    parameter    FLASH_SEND_CMD    =    1    ;
    parameter    FLASH_SEND_ADDR    =    2    ;
    parameter    FLASH_WR_DATA    =    3    ;
    parameter    FLASH_RD_DATA    =    4    ;
    parameter    FLASH_END        =    5    ;
    
    reg    [2:0]    flash_cstate    ;
    reg    [2:0]    flash_nstate    ;
    
    reg    [2:0]    r_cmd_cnt    ;
    reg    [4:0]    r_addr_cnt    ;
    reg    [2:0]    r_data_cnt    ;
    
    reg            r_op_done    ;
    reg            r_flash_done;
    reg    [7:0]    r_flash_data;
            
    reg            r_flash_cs    ;
    reg            r_flash_din    ;
    
    reg    [7:0]    r_wr_num    ;
    reg    [7:0]    r_rd_num    ;
    
    reg            r_busy        ;
    
    
    reg            r_rd_valid    ;
    reg    [2:0]    r_rd_cnt    ;
    reg            r_wr_valid    ;
    reg    [2:0]    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:1'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[3])
                    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[2:0] == 3'b001)    //命令后不带地坿Ҍ数据
                        flash_nstate    <= FLASH_END        ;
                    else if(i_cmd_type[2:0] == 3'b010)    //命令后带写数据,无地坿
                        flash_nstate    <= FLASH_WR_DATA    ;
                    else if(i_cmd_type[2:0] == 3'b011)    //命令后带读数据,无地坿
                        flash_nstate    <= FLASH_RD_DATA    ;
                    else if(i_cmd_type[2] == 1'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[1:0] == 2'b00)    //不带数据
                        flash_nstate    <= FLASH_END    ;
                    else if(i_cmd_type[1:0] == 2'b01)    //写数捿
                        flash_nstate    <= FLASH_WR_DATA;
                    else if(i_cmd_type[1:0] == 2'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 - 1'b1;
        end
        else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'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 - 1'b1;
        end
        else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'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<= 1'b0    ;
        else if(flash_cstate == FLASH_IDLE)
            r_op_done<= 1'b0    ;
        else if(flash_cstate == FLASH_END)
            r_op_done<= 1'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 + 1'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 + 1'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 + 1'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    <= 1'b1    ;
        else if(flash_cstate == FLASH_SEND_CMD)
            r_flash_din    <= i_flash_cmd[7 - r_cmd_cnt];
        else if(flash_cstate == FLASH_SEND_ADDR)
            r_flash_din    <= i_falsh_addr[23 - r_addr_cnt];
        else if(flash_cstate == FLASH_WR_DATA)
            r_flash_din    <= i_flash_data[7 - r_data_cnt];
        else
            r_flash_din    <= 1'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[7 - 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    <= 1'b0    ;
        else if(flash_cstate == FLASH_WR_DATA)
            r_wr_valid    <= 1'b1;
        else
            r_wr_valid    <= 1'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 + 1'b1;
        else
            r_wr_cnt    <= 'd0    ;
    end
    
    always @(negedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            r_rd_valid    <= 1'b0    ;
        else if(flash_cstate == FLASH_RD_DATA)
            r_rd_valid    <= 1'b1;
        else
            r_rd_valid    <= 1'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 + 1'b1;
        else
            r_rd_cnt    <= 'd0    ;
    end
    
    always @(negedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            r_flash_done    <= 1'b0    ;
        else if(r_rd_cnt == 'd7)
            r_flash_done    <= 1'b1    ;
        else
            r_flash_done    <= 1'b0    ;
    end
    
    always @(negedge i_clk or negedge i_rst_n)
    begin
        if(!i_rst_n)
            r_flash_cs    <= 1'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    <= 1'b1    ;
        else if(flash_cstate == FLASH_SEND_CMD)
            r_flash_cs    <= 1'b0    ;
    end
    
    reg    [8:0]    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 + 1'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    <= 1'b0    ;
        else if(flash_cstate == FLASH_END)
            r_busy    <= 1'b0    ;
        else if(flash_cstate == FLASH_SEND_CMD)
            r_busy    <= 1'b1    ;
    end

endmodule

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

5 完结

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

转载于:https://www.cnblogs.com/lai-jian-tao/p/10659797.html

你可能感兴趣的:(基于FPGA的SPI FLASH控制器设计)