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        ,    //50MHzoutput            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)beginif(!i_rst_n)r_clk_25MHz    <= 1'b0;elser_clk_25MHz    <= !r_clk_25MHz;endalways @(posedge r_clk_25MHz or negedge i_rst_n)beginif(!i_rst_n)beginr_cmd_type    <= 'd0;r_flash_cmd    <= 'd0;r_falsh_addr<= 'd0;r_flash_wdata<= 'd0;r_data_num    <= 'd0;r_cnt        <= 'd0;endelsebegincase(r_cnt)'d0:    //Read Manufacturer / Device ID (90h)beginif(r_op_done)beginif(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;endelsebeginr_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;    //数据字节敿                    endend'd1:    //Write Enable (06h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd2:    //Read Status Register-1 (05h)beginif(r_op_done && r_flash_rdata[1] == 1'b1)    //轮询WEL位,直到WEL置位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                endend'd3:    //Sector Erase (20h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd4:    //Read Status Register-1 (05h)beginif(r_op_done && r_flash_rdata[0] == 1'b0)    //轮询BUSY位,直到BUSY复位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd5:    //Write Disable (04h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd6:    //Read Status Register-1 (05h)beginif(r_flash_done && r_flash_rdata[1] == 1'b0)    //轮询WEL位,直到WEL复位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd7:    //Write Enable (06h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd8:    //Read Status Register-1 (05h)beginif(r_flash_done && r_flash_rdata[1] == 1'b1)    //轮询WEL位,直到WEL置位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd9:    //Page Program (02h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd10:    //Read Status Register-1 (05h)beginif(r_flash_done && r_flash_rdata[0] == 1'b0)    //轮询BUSY位,直到BUSY复位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd11:    //Write Disable (04h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd12:    //Read Status Register-1 (05h)beginif(r_flash_done && r_flash_rdata[1] == 1'b0)    //轮询WEL位,直到WEL复位//if(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd13:    //Read Data (03h)beginif(r_op_done)beginr_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;endelsebeginr_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;    //数据字节敿                    endend'd14:beginr_cnt    <= 'd0;endendcaseendendwire    [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    ,    //25MHzinput    [ 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)beginif(!i_rst_n)flash_cstate    <= FLASH_IDLE    ;elseflash_cstate    <= flash_nstate    ;endalways @(*)begincase(flash_cstate)FLASH_IDLE:beginif(i_cmd_type[3])flash_nstate    <= FLASH_SEND_CMD    ;elseflash_nstate    <= FLASH_IDLE        ;endFLASH_SEND_CMD:beginif(r_cmd_cnt == 'd7)beginif(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    ;elseflash_nstate    <= FLASH_IDLE        ;endelsebeginflash_nstate    <= FLASH_SEND_CMD    ;endendFLASH_SEND_ADDR:beginif(r_addr_cnt == 'd23)beginif(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;elseflash_nstate    <= FLASH_IDLE    ;endelseflash_nstate    <= FLASH_SEND_ADDR    ;endFLASH_WR_DATA:beginif(r_data_cnt == 'd7 && r_wr_num == 'd0)flash_nstate    <= FLASH_END    ;elseflash_nstate    <= FLASH_WR_DATA;endFLASH_RD_DATA:beginif(r_data_cnt == 'd7 && r_rd_num == 'd0)flash_nstate    <= FLASH_END    ;elseflash_nstate    <= FLASH_RD_DATA;endFLASH_END:beginflash_nstate    <= FLASH_IDLE    ;enddefault:beginflash_nstate    <= FLASH_IDLE    ;endendcaseendalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_rd_num    <= 'd0;else if(r_rd_cnt == 'd7)beginif(r_rd_num == 'd0)r_rd_num    <= 'd0;elser_rd_num    <= r_rd_num - 1'b1;endelse if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'b1110)r_rd_num    <= i_data_num    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_wr_num    <= 'd0;else if(r_wr_cnt == 'd7)beginif(r_wr_num == 'd0)r_wr_num    <= 'd0;elser_wr_num    <= r_wr_num - 1'b1;endelse if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'b1101)r_wr_num    <= i_data_num    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_cmd_cnt    <= 'd0    ;else if(flash_cstate == FLASH_SEND_CMD)beginif(r_cmd_cnt == 'd7)r_cmd_cnt    <= 'd0    ;elser_cmd_cnt    <= r_cmd_cnt + 1'b1;endelser_cmd_cnt    <= 'd0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_addr_cnt    <= 'd0    ;else if(flash_cstate == FLASH_SEND_ADDR)beginif(r_addr_cnt == 'd23)r_addr_cnt    <= 'd0    ;elser_addr_cnt    <= r_addr_cnt + 1'b1;endelser_addr_cnt    <= 'd0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_data_cnt    <= 'd0    ;else if(flash_cstate == FLASH_RD_DATA || flash_cstate == FLASH_WR_DATA)beginif(r_data_cnt == 'd7)r_data_cnt    <= 'd0    ;elser_data_cnt    <= r_data_cnt + 1'b1;endelser_data_cnt    <= 'd0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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];elser_flash_din    <= 1'b1    ;endalways @(posedge i_clk or negedge i_rst_n)beginif(!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    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_wr_valid    <= 1'b0    ;else if(flash_cstate == FLASH_WR_DATA)r_wr_valid    <= 1'b1;elser_wr_valid    <= 1'b0;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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;elser_wr_cnt    <= 'd0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_rd_valid    <= 1'b0    ;else if(flash_cstate == FLASH_RD_DATA)r_rd_valid    <= 1'b1;elser_rd_valid    <= 1'b0;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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;elser_rd_cnt    <= 'd0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!i_rst_n)r_flash_done    <= 1'b0    ;else if(r_rd_cnt == 'd7)r_flash_done    <= 1'b1    ;elser_flash_done    <= 1'b0    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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    ;endreg    [8:0]    r_num_cnt    ;always @(negedge i_clk or negedge i_rst_n)beginif(!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    ;endalways @(negedge i_clk or negedge i_rst_n)beginif(!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    ;endendmodule

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

5 完结

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

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

基于FPGA的SPI FLASH控制器设计相关推荐

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

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

  2. 基于FPGA的USB接口控制器设计(VHDL)(中)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第二篇,中篇,USB通信原理.USB 系统开发以及设计实例.话不多说,上货. 之前有关于 Veril ...

  3. 基于FPGA的USB接口控制器设计(VHDL)(上)

    今天给大侠带来基于 FPGA 的 USB 接口控制器设计(VHDL),由于篇幅较长,分三篇.今天带来第一篇,上篇,USB 接口简介 以及 USB 体系结构.话不多说,上货. 之前有关于 Verilio ...

  4. 【工程源码】基于FPGA的XPT2046触摸控制器设计

    本文和设计代码由FPGA爱好者小梅哥编写,未经作者许可,本文仅允许网络论坛复制转载,且转载时请标明原作者. XPT2046是一款设计用于移动电话.个人数字助理.便携式一起.付款中断设备.触摸屏显示器等 ...

  5. imut FPGA课设 基于FPGA的VGA弹球游戏设计 *秋昊

    写在前面的话: 本文主要呈现了一篇IMUT的FPGA课设报告. 课设报告内容(word版),视频演示,程序源码,专业创新实践简介,专业创新实践指导书均已放入下面的百度云链接中,也不大,总共不到20MB ...

  6. 基于FPGA的CAN总线控制器的设计(下)

    今天给大侠带来基于FPGA的CAN总线控制器的设计,由于篇幅较长,分三篇.今天带来第三篇,下篇,程序的仿真与测试以及总结.话不多说,上货. 导读 CAN 总线(Controller Area Netw ...

  7. 基于FPGA的CAN总线控制器的设计(上)

    今天给大侠带来基于FPGA的CAN总线控制器的设计,由于篇幅较长,分三篇.今天带来第一篇,上篇,CAN 总线协议解析以及 CAN 通信控制器程序基本框架.话不多说,上货. 导读 CAN 总线(Cont ...

  8. 串行Flash控制器设计介绍(QSPI)

    目录 1. 概述 2. 整体架构 3. AHB接口单元 3.1. 地址重映射功能 3.2. 写保护功能 3.3. 连续/非连续传输识别 3.4. Burst长度识别 3.5. AHB地址解码功能 4. ...

  9. 基于FPGA实现SPI接口(配置或通信)

    基于FPGA实现SPI接口(配置或通信总线) 1)总线简介 串行外设接口(SPI)是微控制器(FPGA)和外围IC(如传感器.ADC.DAC.移位寄存器.SRAM等)之间使用最广泛的接口之一.主要用于 ...

最新文章

  1. 超实用!图像去畸变矫正及双线性内插法
  2. 解决Python报错UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 658: illegal multibyte
  3. 中国移动问答 赢取幸运卡标准答案
  4. Eclipse基金会
  5. linux 备份mysql并上传_linux备份数据库并上传至远程服务器(定时执行shell进行ftp上传)...
  6. 第15届创新英语大赛范文
  7. python3 模板库 好用_关于3个Python模板库的比较
  8. quartz集成到springboot_springboot系列之02-需要了解的宏观知识点
  9. 类的加载过程详解之过程一:Loading(加载)阶段
  10. FFmpeg入门详解之71:获取ffmpeg转码的实时进度
  11. VS2017离线下载安装包教程
  12. 高效液相色谱分析的基本原理
  13. 1. Hadoop原理简述
  14. 【Codeforces Round #551 (Div. 2) C. Serval and Parenthesis Sequence(Java版)
  15. 微服务项目实战-易买网网页(电商)二、MybatisPlus与微服务注册
  16. 推荐5 个 NeoVim GUI 编辑器
  17. sed命令删除行操作
  18. Linux AHCI驱动
  19. java 文件移动文件夹_java移动文件到另一个文件夹中
  20. linux下迅雷远程下载服务,在 Linux 下使用迅雷的另一种无入侵方式

热门文章

  1. 计算机网络协议分层体系结构
  2. jquery easyui 动态绑定数据列
  3. LA 3507 Keep the Customer Satisfied (Greedy)
  4. URAL 1011 Conductors
  5. “树人杯”暨第三届辽宁科技大学校园程序设计竞赛正赛I 充分利用学习卡(粉)...
  6. 【BZOJ】3771: Triple FTT+生成函数
  7. linxu命令之cp 拷贝整个目录下的所有文件
  8. Spring-boot logback日志处理
  9. PhotoShop算法原理解析系列 - 像素化---》碎片。
  10. C#读写文件:编码和转换(string和byte[]转换为例)