基於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的特征為如下圖所示:

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        [ 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控制器設計已經介紹完畢,如有不妥之處望批評指正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM