基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作

目录

  • 一、M25P16
  • 二、源码
    • 2.1 顶层模块
    • 2.2 按键模块
    • 2.3 SPI 模块
    • 2.4 数码管模块
  • 三、仿真模块
  • 四、管脚配置
  • 五、验证结果

本文内容:基于 SPI 协议控制开发板上的 FLASH 进行数据读写操作

一、M25P16

  • 查看开发板原理图,可以知道 FLASH 使用的是 M25P16 芯片,存储总容量 16 Mbit,采用串行方式传输数据
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第1张图片
  • 找一篇 M25P16 的手册,参照手册上面进行编程
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第2张图片
  • 芯片对应的管脚,其中 W(写保护)、HOLD(保持)用不着,可以忽略掉,其它的管脚后面会讲
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第3张图片
  • 根据 CPOL 和 CPHA 的取值不同,共有四种 SPI 传输模式,这里用的模式 3,也就是时钟 C 空闲状态高电平,在时钟 C 的上升沿对 D / Q 进行数据采样
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第4张图片
  • 存储容量,共 32 个扇区,每个扇区 256 页,每页 256 个字节
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第5张图片
  • 这里只需要用到 WREN、READ、PP、SE 指令即可,进行基本的读写操作,其它指令可以自行拓展
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第6张图片
  • 写指令,片选 S 拉低,表示数据传输阶段,拉高表示空闲状态,C 表示系统时钟,D 表示主机发送数据,FLASH 接收数据,Q 表示 FLASH 发送数据,主机接收数据
  • 写指令只需要发送一个 8’h06 给 FLASH 即可,FLASH 会在时钟的上升沿进行采样
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第7张图片
  • SE 指令(扇区擦除),发送一个 8’hd8 然后跟上 3byte 的地址,指定一个扇区进行擦除,即使指定了页地址或者字地址,它只认扇区地址,因为扇区擦除嘛,擦除的是一个扇区,也就是将一个扇区的数据变为 8’hff
  • 注意!!发送 SE 指令之前,必须先发送一个 WREN 指令,然后片选 S 拉高至少 100ns ,然后再拉低片选 S ,再发送 SE 指令,SE 与 3 byte 地址之间不需要拉低 100ns,发送完后,S 拉高,需要给 FLASH 1s 左右的时间让它进行扇区擦除
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第8张图片
  • PP 指令,发送一个 byte 的指令,然后发送 3byte 的字节,再跟上一个 byte 的数据,就把 S 拉高,这就往这个地址里面写入了一个 byte 的数据
  • 注意!!发送 PP 指令之前,必须先发送一个 WREN 指令,然后片选 S 拉高至少 100ns ,然后再拉低片选 S ,再发送 PP 指令,PP 与 3 byte 地址之间不需要拉低 100ns,发送完后,S 拉高,同时需要给 FLASH 1.4ms 左右的时间让它写数据
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第9张图片
  • 读指令,发送一个读指令,然后跟上一个 3byte 的地址,然后等待 Q 传输 1byte 的数据即可,再把 S 片选拉高即可,如果不拉高,它 Q 就会一直发数据
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第10张图片
  • 其它指令就不讲了,没用到,如果不理解的话,多看图,看着看着就明白了

二、源码

2.1 顶层模块

SPI_top.v

/*========================================*\
    filename        : SPI_top.v
    description     : SPI顶层模块
    up file         : 
    reversion       : 
        v1.0 : 2022-8-25 19:09:45
    author          : 张某某
\*========================================*/

module SPI_top (
    input                               clk             ,
    input                               rst_n           ,
    input           [ 2:0]              key_in          ,
    input                               MISO            ,

    output                              SCK             ,
    output                              CS              ,
    output                              MOSI            ,
    output          [ 5:0]              SEL             ,
    output          [ 7:0]              DIG             
);

// Parameter definition
    

// Signal definition
    wire            [ 7:0]              data            ;
    wire            [ 7:0]              press           ;

// Module calls
    key_filter              U_key_filter(
        /*input                 */  .clk        (clk    ),
        /*input                 */  .rst_n      (rst_n  ),
        /*input           [ 2:0]*/  .key_in     (key_in ),
        /*output  reg     [ 2:0]*/  .press      (press  )
    );

    spi_m25p16              U_spi_m25p16(
        /*input                 */  .clk        (clk    ),
        /*input                 */  .rst_n      (rst_n  ),
        /*input           [ 2:0]*/  .press      (press  ),
        /*input                 */  .MISO       (MISO   ),
        /*output  reg           */  .SCK        (SCK    ),
        /*output  reg           */  .CS         (CS     ),
        /*output  reg           */  .MOSI       (MOSI   ),
        /*output  reg     [ 7:0]*/  .data_out   (data   )
    );

    display                 U_display(
        /*input                 */  .clk        (clk    ), // 50MHz
        /*input                 */  .rst_n      (rst_n  ), // 复位信号
        /*input           [ 7:0]*/  .data       (data   ),
        /*output  reg     [ 5:0]*/  .SEL        (SEL    ), // SEL信号
        /*output  reg     [ 7:0]*/  .DIG        (DIG    )  // DIG信号
    );

// Logic description
    

endmodule

2.2 按键模块

key_filter.v

/*========================================*\
    filename        : key_filter.v
    description     : 按键消抖模块
    up file         : 
    reversion       : 
        v1.0 : 2022-8-25 19:09:59
    author          : 张某某
\*========================================*/

module key_filter #(parameter MS_20 = 20'd1000_000)(
    input                               clk         ,
    input                               rst_n       ,
    input           [ 2:0]              key_in      ,
  
    output  reg     [ 2:0]              press
);
// 全局变量定义

// 信号定义
    reg             [ 2:0]              key_0       ; // 按键信号当前时钟周期电平
    reg             [ 2:0]              key_1       ; // 按键信号下一个时钟周期电平

    wire            [ 2:0]              key_nedge   ; // 下降沿使能信号
    reg                                 add_flag    ; // 计数使能信号
    reg             [19:0]              delay_cnt   ; // 延时计数器

// 模块功能
    //打拍器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            key_0 <= 'b1;
            key_1 <= 'b1;
        end
        else begin
            key_0 <= key_in;
            key_1 <= key_0;
        end
    end

    // 检测下降沿
    assign key_nedge = ~key_0 & key_1;

    // 计数使能信号
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            add_flag <= 'b0;
        end
        else if (key_nedge) begin
            add_flag <= 'b1;
        end
        else if (delay_cnt >= MS_20 - 1) begin
            add_flag <= 'b0;
        end
        else begin
            add_flag <= add_flag;
        end
    end

    // 计数20ms
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            delay_cnt <= 20'd0;
        end
        else if (add_flag) begin
            if (delay_cnt >= MS_20 - 1) begin
                delay_cnt <= 20'd0;
            end
            else begin
                delay_cnt <= delay_cnt + 1;
            end
        end
        else begin
            delay_cnt <= 20'd0;
        end
    end

    // 输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            press <= 'd0;
        end
        else if (delay_cnt >= MS_20 - 1) begin
            press <= ~key_in;
        end
        else begin
            press <= 'd0;
        end
    end

endmodule

2.3 SPI 模块

spi_m25p16.v

/*========================================*\
    filename        : spi_m25p16.v
    description     : 基于SPI控制M25P16
    up file         : SPI_top.v
    reversion       : 
        v1.0 : 2022-8-25 19:08:52
    author          : 张某某
\*========================================*/

module spi_m25p16 #(parameter S_3 = 28'd50_000_000)(
    input                               clk                     ,
    input                               rst_n                   ,
    input           [ 2:0]              press                   ,
    input                               MISO                    ,
    
    output  reg                         SCK                     ,
    output  reg                         CS                      ,
    output  reg                         MOSI                    ,
    output  reg     [ 7:0]              data_out
);

// Parameter definition
    parameter       ORDER_WREN  =       8'h06                   ,
                    ORDER_SE    =       8'hd8                   ,
                    ORDER_PP    =       8'h02                   ,
                    ORDER_READ  =       8'h03                   ;

    parameter       IDLE        =       8'b0000_0001            ,
                    WREN        =       8'b0000_0010            ,
                    WAIT        =       8'b0000_0100            ,
                    SEPP        =       8'b0000_1000            ,
                    RDEN        =       8'b0001_0000            ,
                    ADDRESS     =       8'b0010_0000            ,
                    DATA        =       8'b0100_0000            ,
                    FINAL       =       8'b1000_0000            ;

// Signal definition
    reg             [31:0]              state_ascll             ;

    reg             [ 7:0]              state_c                 ; // 现态
    reg             [ 7:0]              state_n                 ; // 次态

    wire                                idle2wren               ;
    wire                                idle2rden               ;
    wire                                wren2wait               ;
    wire                                wait2sepp               ;
    wire                                sepp2address            ;
    wire                                rden2address            ;
    wire                                address2final           ;
    wire                                address2data            ;
    wire                                data2final              ;
    wire                                final2idle              ;

    reg                                 flag_SE                 ;
    reg                                 flag_PP                 ;
    reg                                 flag_RD                 ;

    reg             [ 3:0]              cnt_SCK_fre             ; // SCK频率计数器
    wire                                add_cnt_SCK_fre         ;
    wire                                end_cnt_SCK_fre         ;

    reg             [ 2:0]              press_0                 ; // press时序逻辑化
    reg             [ 7:0]              send_data               ; // 发送的1byte数据
    reg             [ 7:0]              receive_data            ; // 接收的1byte数据

    reg             [ 2:0]              cnt_bit                 ; // bit计数器
    wire                                add_cnt_bit             ;
    wire                                end_cnt_bit             ;

    reg             [ 1:0]              cnt_byte                ; // byte计数器
    wire                                add_cnt_byte            ;
    wire                                end_cnt_byte            ;

    reg             [27:0]              cnt_3s                  ; // 计数三秒
    wire                                add_cnt_3s              ;
    wire                                end_cnt_3s              ;
    
// Logic description
    // 仿真时显示state_c对应的字符串
    always @(*) begin
        case (state_c)
            IDLE    : state_ascll = 32'h49444C45;
            WREN    : state_ascll = 32'h5752454E;
            WAIT    : state_ascll = 32'h57414954;
            SEPP    : state_ascll = 32'h53455050;
            RDEN    : state_ascll = 32'h5244454E;
            ADDRESS : state_ascll = 32'h41444452;
            DATA    : state_ascll = 32'h44415441;
            FINAL   : state_ascll = 32'h46494E41;
            default : state_ascll = 32'h49444C45;
        endcase
    end

    // 第一段 状态转移
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    // 第二段 状态转移规律
    always @(*) begin
        case (state_c)
            IDLE : 
                begin
                    if (idle2wren) state_n = WREN;
                    else if (idle2rden) state_n = RDEN;
                    else state_n = state_c;
                end
            RDEN : 
                begin
                    if (rden2address) state_n = ADDRESS;
                    else state_n = state_c;
                end
            WREN : 
                begin
                    if (wren2wait) state_n = WAIT;
                    else state_n = state_c;
                end
            WAIT : 
                begin
                    if (wait2sepp) state_n = SEPP;
                    else state_n = state_c;
                end
            SEPP : 
                begin
                    if (sepp2address) state_n = ADDRESS;
                    else state_n = state_c;
                end
            ADDRESS : 
                begin
                    if (address2final) state_n = FINAL;
                    else if (address2data) state_n = DATA;
                    else state_n = state_c;
                end
            DATA : 
                begin
                    if (data2final) state_n = FINAL;
                    else state_n = state_c;
                end
            FINAL : 
                begin
                    if (final2idle) state_n = IDLE;
                    else state_n = state_c;
                end
            default : state_n = state_c;
        endcase
    end

    assign idle2wren     = state_c == IDLE && (flag_SE || flag_PP) && cnt_SCK_fre == 'd6;
    assign idle2rden     = state_c == IDLE && flag_RD && cnt_SCK_fre == 'd6;
    assign wren2wait     = state_c == WREN && end_cnt_bit;
    assign wait2sepp     = state_c == WAIT && cnt_SCK_fre == 'd6;
    assign sepp2address  = state_c == SEPP && end_cnt_bit;
    assign address2final = state_c == ADDRESS && flag_SE && end_cnt_byte;
    assign address2data  = state_c == ADDRESS && (flag_PP || flag_RD) && end_cnt_byte;
    assign rden2address  = state_c == RDEN && end_cnt_bit;
    assign data2final    = state_c == DATA && end_cnt_bit;
    assign final2idle    = state_c == FINAL && end_cnt_3s;

    // 第三段 描述输出

    // SCK时钟信号频率计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_SCK_fre <= 4'd0;
        end
        else if (add_cnt_SCK_fre) begin
            if (end_cnt_SCK_fre) begin
                cnt_SCK_fre <= 4'd0;
            end
            else begin
                cnt_SCK_fre <= cnt_SCK_fre + 4'd1;
            end
        end
        else begin
            cnt_SCK_fre <= 4'd0;
        end
    end
    assign add_cnt_SCK_fre = 1'b1;
    assign end_cnt_SCK_fre = add_cnt_SCK_fre && cnt_SCK_fre >= 4'd9;

    // SCK时钟信号生成
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SCK <= 1'd0;
        end
        else begin
            case (cnt_SCK_fre)
                4'd9 : SCK <= 1'd0;
                4'd4 : SCK <= 1'd1;
                default: SCK <= SCK;
            endcase
        end
    end

    // 对press时序逻辑化
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            press_0 <= 3'd0;
        end
        else begin
            press_0 <= press;
        end
    end

    // 按键控制使能信号
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            flag_SE <= 1'b0;
            flag_PP <= 1'b0;
            flag_RD <= 1'b0;
        end
        else if (final2idle) begin
            flag_SE <= 1'b0;
            flag_PP <= 1'b0;
            flag_RD <= 1'b0;
        end
        else begin
            case (press_0)
                3'b100 : flag_SE <= 1'b1;
                3'b010 : flag_PP <= 1'b1;
                3'b001 : flag_RD <= 1'b1;
                default: 
                    begin
                        flag_SE <= flag_SE;
                        flag_PP <= flag_PP;
                        flag_RD <= flag_RD;
                    end
            endcase
        end
    end

    // CS片选信号
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            CS <= 1'd1;
        end
        else begin
            case (state_c)
                WREN, SEPP, ADDRESS, DATA, RDEN : CS <= 1'd0; 
                default: CS <= 1'd1;
            endcase
        end
    end

    // 待发送的1byte数据
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            send_data <= 8'd0;
        end
        else begin
            case (state_c)
                WREN : send_data <= ORDER_WREN;
                SEPP :
                    begin
                        if (flag_SE) send_data <= ORDER_SE;
                        else if (flag_PP) send_data <= ORDER_PP;
                        else send_data <= 8'd0;
                    end
                ADDRESS :
                    begin
                        case (cnt_byte)
                            2'd0 : send_data <= 8'h01;
                            2'd1 : send_data <= 8'h02;
                            2'd2 : send_data <= 8'h03;
                            default: send_data <= 8'h00;
                        endcase
                    end
                DATA : send_data <= flag_RD ? 8'h00 : 8'h23;
                RDEN : send_data <= ORDER_READ;
                default: send_data <= 8'd0;
            endcase
        end
    end
 
    // bit计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_bit <= 'd0;
        end
        else if (add_cnt_bit) begin
            if (end_cnt_bit) begin
                cnt_bit <= 'd0;
            end
            else begin
                cnt_bit <= cnt_bit + 'd1;
            end
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end
    assign add_cnt_bit = (state_c == WREN || state_c >= SEPP && state_c <= DATA) && cnt_SCK_fre == 4'd6;
    assign end_cnt_bit = add_cnt_bit && cnt_bit >= 3'd7;

    // byte计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_byte <= 2'd0;
        end
        else if (add_cnt_byte) begin
            if (end_cnt_byte) begin
                cnt_byte <= 2'd0;
            end
            else begin
                cnt_byte <= cnt_byte + 2'd1;
            end
        end
        else begin
            cnt_byte <= cnt_byte;
        end
    end
    assign add_cnt_byte = state_c == ADDRESS && end_cnt_bit;
    assign end_cnt_byte = add_cnt_byte && cnt_byte >= 2'd2;

    // MOSI输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            MOSI <= 1'd0;
        end
        else if (~CS && cnt_SCK_fre == 4'd1) begin
            MOSI <= send_data[7 - cnt_bit];
        end
        else begin
            MOSI <= MOSI;
        end
    end

    // 计数3秒
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_3s <= 'd0;
        end
        else if (add_cnt_3s) begin
            if (end_cnt_3s) begin
                cnt_3s <= 'd0;
            end
            else begin
                cnt_3s <= cnt_3s + 'd1;
            end
        end
        else begin
            cnt_3s <= 'd0;
        end
    end
    assign add_cnt_3s = state_c == FINAL;
    assign end_cnt_3s = add_cnt_3s && cnt_3s >= S_3 - 28'd1;

    // 接收1byte数据
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            receive_data <= 8'd0;
        end
        else if (flag_RD && state_c == DATA) begin
            receive_data[7 - cnt_bit] <= MISO;
        end
        else begin
            receive_data <= 8'd0;
        end
    end

    // 将接收的1byte数据送给数码管显示
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 8'd0;
        end
        else if (data2final && flag_RD) begin
            data_out <= receive_data;
        end
        else begin
            data_out <= data_out;
        end
    end

endmodule

2.4 数码管模块

display.v

/*========================================*\
  filename        : display.v
  description     : 滚动显示输入的数据
  up file         : 
  reversion       : 
      v1.0 : 2022-7-27 18:49:34
  author          : 张某某
\*========================================*/

module display #(parameter  MS_1= 16'd50000)(
    input                                   clk                 ,   // 50MHz
    input                                   rst_n               ,   // 复位信号
    input               [ 7:0]              data                ,
    output      reg     [ 5:0]              SEL                 ,   // SEL信号
    output      reg     [ 7:0]              DIG                     // DIG信号
);

// 信号定义
    reg                 [15:0]              cnt_flicker         ;   // 计数1ms
    wire                                    SEL_change          ;   // cnt_flicker计满使能信号
    reg                                     show_wei            ; 
    reg                 [ 3:0]              tmp_data            ;   // 当前DIG的值
    
// 逻辑描述
    // 闪烁频率计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_flicker <= 'd0;
        end
        else if (SEL_change) begin
            cnt_flicker <= 'd0;
        end
        else begin
            cnt_flicker <= cnt_flicker + 'd1; 
        end
    end
    assign SEL_change = cnt_flicker >= MS_1 - 'd1 ? 1'b1 : 1'b0;

    // SEL信号输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SEL <= 6'b011_111;
        end
        else if (SEL_change) begin
            SEL <= {SEL[4], SEL[5], SEL[3:0]};
        end
        else begin
            SEL <= SEL;
        end
    end

    // tmp_data当前SEL位选所对应的DIG十进制值
    always @(*) begin
        case (SEL)
            6'b011_111 : tmp_data = data[3:0];
            6'b101_111 : tmp_data = data[7:4];
            default: tmp_data = 'd0;
        endcase
    end

    // DIG输出各数字对应的二进制
    always @(*) begin
        case (tmp_data)
            4'd0 : DIG = 8'b1100_0000;
            4'd1 : DIG = 8'b1111_1001;
            4'd2 : DIG = 8'b1010_0100;
            4'd3 : DIG = 8'b1011_0000;
            4'd4 : DIG = 8'b1001_1001;
            4'd5 : DIG = 8'b1001_0010;
            4'd6 : DIG = 8'b1000_0010;
            4'd7 : DIG = 8'b1111_1000;
            4'd8 : DIG = 8'b1000_0000;
            4'd9 : DIG = 8'b1001_0000;
            4'd10: DIG = 8'b1000_1000;
            4'd11: DIG = 8'b1000_0011;
            4'd12: DIG = 8'b1100_0110;
            4'd13: DIG = 8'b1010_0001;
            4'd14: DIG = 8'b1000_0110;
            4'd15: DIG = 8'b1000_1110;
        endcase
    end

endmodule

三、仿真模块

tb_spi_m25p16.v

/*========================================*\
    filename        : 
    description     : 
    up file         : 
    reversion       : 
        v1.0 : 
    author          : 张某某
\*========================================*/

`timescale 1ns/1ns

module tb_spi_m25p16;

// Parameter definition
    parameter       CYC_CLK             = 20            ;

// Drive signal
    reg                                 tb_clk          ;
    reg                                 tb_rst_n        ;
    reg            [ 2:0]               tb_press        ;
    reg                                 tb_MISO         ;

// Observation signal
    wire                                tb_SCK          ;
    wire                                tb_CS           ;
    wire                                tb_MOSI         ;

// Module calls
    spi_m25p16    #(.S_3(150))          U_spi_m25p16(
        /*input                 */  .clk    (tb_clk     ),
        /*input                 */  .rst_n  (tb_rst_n   ),
        /*input           [ 1:0]*/  .press  (tb_press   ),
        /*input                 */  .MISO   (tb_MISO    ),
        /*output  reg           */  .SCK    (tb_SCK     ),
        /*output  reg           */  .CS     (tb_CS      ),
        /*output  reg           */  .MOSI   (tb_MOSI    )
    );

// System initialization
    initial begin
        tb_clk = 1'b1;
        tb_rst_n = 1'b0;
        #(CYC_CLK) tb_rst_n = 1'b1;
    end
    always #(CYC_CLK >> 1) tb_clk = ~tb_clk;

    initial begin
        tb_press = 3'b000;
        tb_MISO  = 1'b0;

        #(30 * CYC_CLK);

        // 擦除操作
        tb_press = 3'b100;
        #(CYC_CLK) tb_press = 3'b000;

        #(16000);

        // 页面编程
        tb_press = 3'b010;
        #(CYC_CLK) tb_press = 3'b000;

        #(20000);

        // 读数据
        tb_press = 3'b001;
        #(CYC_CLK) tb_press = 3'b000;

        #(12000);

        $stop;
    end

endmodule

四、管脚配置

set_location_assignment PIN_E1  -to clk
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_E16 -to key_in[0]
set_location_assignment PIN_M16 -to key_in[1]
set_location_assignment PIN_M15 -to key_in[2]
set_location_assignment PIN_H2  -to MISO  
set_location_assignment PIN_H1  -to SCK   
set_location_assignment PIN_D2  -to CS    
set_location_assignment PIN_C1  -to MOSI  
set_location_assignment PIN_A4  -to SEL[0]
set_location_assignment PIN_B4  -to SEL[1] 
set_location_assignment PIN_A3  -to SEL[2]
set_location_assignment PIN_B3  -to SEL[3]
set_location_assignment PIN_A2  -to SEL[4]
set_location_assignment PIN_B1  -to SEL[5]
set_location_assignment PIN_B7  -to DIG[0]
set_location_assignment PIN_A8  -to DIG[1]
set_location_assignment PIN_A6  -to DIG[2]
set_location_assignment PIN_B5  -to DIG[3]
set_location_assignment PIN_B6  -to DIG[4]
set_location_assignment PIN_A7  -to DIG[5]
set_location_assignment PIN_B8  -to DIG[6]
set_location_assignment PIN_A5  -to DIG[7]

五、验证结果

  • 仿真结果,第一部分是擦除操作,第二部分是PP写操作,第三部分是读操作
    基于 FPGA 使用 SPI 协议控制 FLASH(M25P16) 进行数据读写操作_第11张图片
  • 板上验证

    SPI控制FLASH

  • 按左边按键,表示进行擦除操作
    按中间按键,表示进行写数据操作
    按右边按键,表示进行读数据操作

你可能感兴趣的:(FPGA小项目,fpga开发,spi)