基于FPGA的SPI接口讲解——flash M25P28为例(1)

M25P128芯片结构介绍

本次实验所使用的软硬件环境如下:
硬件:锆石A4plus开发板
软件:quartus II 13.1
这里吐槽一句,个人感觉锆石的开发板还是做的不错的(虽然配套代码垃圾),就是搞不懂为什么公司倒闭了。

M25P128芯片的结构图如下,整个芯片被细分了扇区和页。该芯片总共128Mbit总容量,每个扇区512Kbit空间,每页256Bytes空间。支持整块擦出和扇区擦除。

该芯片的存储信息可以总结为:芯片包括256个扇区,每个扇区包括256页,每页包括256Bytes。具体的换算信息大家可以自我换算得到。
在这里插入图片描述

M25P128技术手册信息

每个操作的指令代码如下:
在这里插入图片描述
在这里插入图片描述
从该扇区擦出的技术手册中我们需要得到的信息是:
1:扇区擦出是把整个扇区写成1;
2:扇区擦出指令之前,写使能指令必须被执行;
3:扇区擦出的地址只要是该扇区的任意一个地址即可;
所以我们还得观察写使能部分的技术手册:
在这里插入图片描述
从写使能部分的说明来看,就是简单的SPI协议没有任何特殊。

接下来我们需要找到几个重要的参量。
1、SCK的时钟频率;
2、片选拉低到第一个时钟上升沿的最小时间, t S L C H t_{SLCH}
3、最后一个时钟上升沿到片选拉高所需要的最低时间, t C H S H t_{CHSH}
4、片选拉低距离上一次片选拉高的最低时间, t S H S L t_{SHSL} ;
具体情况如下图:
在这里插入图片描述
在这里插入图片描述
从该表中我们查询信息可以知道:
SCK的时钟频率:不同操作不一样,有的命令是50Mhz,有的是20Mhz,所以我们这里选择12.5Mhz。
片选拉低到第一个时钟上升沿的最小时间:5ns
最后一个时钟上升沿到片选拉高所需要的最低时间:5ns
片选拉低距离上一次片选拉高的最低时间:100ns

所以设计需要知道的信息,我们均从技术手册中获得,接下来开始设计时序。

时序图的设计

本次实验的内容是,按键按下,一个扇区擦出指令被执行。系统框图如下:
在这里插入图片描述
这里不多说废话,直接贴上时序图,相信大家从时序图中可以知道具体的设计方法。
在这里插入图片描述

flash earse模块的代码设计

由上面设计的时序图可以很轻松的得到下面的代码:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : flash_earse.v
// Create Time  : 2020-01-08 18:57:42
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module flash_earse(
    input                   sclk            ,
    input                   rst_n           ,
    input                   key_flag        ,
    output  reg             cs_n            ,
    output  reg             sck             ,
    output  reg             sdi             
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter   IDLE        =   4'b0001         ;
parameter   WREN        =   4'b0010         ;
parameter   DELAT       =   4'b0100         ;
parameter   SE          =   4'b1000         ;
parameter   WREN_INST   =   8'h06           ;
parameter   SE_INST     =   8'hd8           ;
parameter   SE_ADDR     =   24'h00_a1_12    ;

reg                 [ 3:0]  state           ;
reg                 [ 4:0]  cnt_32          ;
reg                 [ 2:0]  cnt_state       ;
reg                 [ 1:0]  cnt_4           ;
reg                 [ 4:0]  bit_cnt         ;

 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cs_n            <=      1'b1;
    else if(key_flag == 1'b1)
        cs_n            <=      1'b0;
    else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2) 
         cs_n            <=     1'b1;
    else if(state == DELAT && cnt_32  == 'd31)
        cs_n            <=      1'b0;
    else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd5)
        cs_n            <=      1'b1;
    else
        cs_n            <=      cs_n;        

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      IDLE;
    else case(state)
        IDLE    :   if(key_flag == 1'b1)
                        state           <=      WREN;
                    else
                        state           <=      state;                                    
        WREN    :   if(cnt_32 == 'd31 && cnt_state == 'd2)
                        state           <=      DELAT;
                    else
                        state           <=      state;                        
        DELAT   :   if(cnt_32  == 'd31)
                        state           <=      SE;
                    else
                        state           <=      state;                        
        SE      :   if(cnt_32 == 'd31 && cnt_state == 'd5)
                        state           <=      IDLE;
                    else
                        state           <=      state;
        default :   state           <=      IDLE;
    endcase
                        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_32          <=      5'd0;
    else if(cnt_32 == 'd31)
        cnt_32          <=      5'd0;
    else if(state != IDLE)
        cnt_32          <=      cnt_32 + 1'b1;
    else
        cnt_32          <=      5'd0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_state       <=      3'd0;
    else if(state == IDLE)
        cnt_state       <=      3'd0;
    else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2) 
        cnt_state       <=      3'd0;
    else if(state == DELAT && cnt_32 == 'd31)
        cnt_state       <=      3'd0;
    else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd5)
        cnt_state       <=      3'd0;
    else if(cnt_32 == 'd31)
        cnt_state       <=      cnt_state + 1'b1;
    else
        cnt_state       <=      cnt_state;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_4           <=      'd0;
    else if(state == WREN && cnt_state == 'd1)
        cnt_4           <=      cnt_4 + 1'b1;
    else if(state == SE && cnt_state >= 'd1 && cnt_state <= 'd4)
        cnt_4           <=      cnt_4 + 1'b1;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sck             <=      1'b0;
    else if(cnt_4 == 'd0)
        sck             <=      1'b0;
    else if(cnt_4 == 'd2)
        sck             <=      1'b1;
    else
        sck             <=      sck;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sdi             <=      1'b0;
    else if(state == WREN && cnt_state == 'd1 && cnt_4 == 'd0)
        sdi             <=      WREN_INST[7-bit_cnt];
    else if(state == SE && cnt_state == 'd1 && cnt_4 == 'd0)
        sdi             <=      SE_INST[7-bit_cnt];
    else if(state == SE && cnt_4 == 'd0 && cnt_state > 'd1 && cnt_state <= 'd4)
        sdi             <=      SE_ADDR[31-bit_cnt];
    else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd4)
        sdi             <=      1'b0;
    else
        sdi             <=      sdi;
      
    
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        bit_cnt         <=      5'd0;
    else if(state == WREN && bit_cnt == 'd7 && cnt_4 == 'd2)
        bit_cnt         <=      5'd0;
    else if(state == SE && bit_cnt == 'd31 && cnt_4 == 'd2)
        bit_cnt         <=      5'd0;
    else if(cnt_4 == 'd2)
        bit_cnt         <=      bit_cnt + 1'b1;
    else
        bit_cnt         <=      bit_cnt;

endmodule

flash earse模块的测试代码

大家应该已经知道我的风格,给出测试代码一定会给出测试代码:

扫描二维码关注公众号,回复: 8635845 查看本文章
`timescale 1ns / 1ps
`define     CLOCK   20
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : flash_earse_tb.v
// Create Time  : 2020-01-08 19:57:13
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module flash_earse_tb();

reg                     sclk             ;
reg                     rst_n            ;
reg                     key_flag         ;
wire                    cs_n             ;
wire                    sck              ;
wire                    sdi              ;

initial begin
    sclk                <=          1'b0;
    rst_n               <=          1'b0;
    key_flag            <=          1'b0;
    #(100*`CLOCK)
    rst_n               <=          1'b1;
    #(100*`CLOCK)
    key_flag            <=          1'b1;
    #(`CLOCK)
    key_flag            <=          1'b0;
    #(1000*`CLOCK)
    key_flag            <=          1'b1;
    #(`CLOCK)
    key_flag            <=          1'b0;
end
always      #(`CLOCK/2)     sclk    <=      ~sclk;



flash_earse flash_earse_inst(
    .sclk               (sclk               ),
    .rst_n              (rst_n              ),
    .key_flag           (key_flag           ),
    .cs_n               (cs_n               ),
    .sck                (sck                ),
    .sdi                (sdi                )
);

endmodule

其余模块的代码

这里为了防止同学们麻烦,我们直接把其他模块的代码也贴上:
key模块

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : key.v
// Create Time  : 2020-01-05 13:49:36
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module key(
    input                       sclk            ,
    input                       rst_n           ,
    input                       key             ,
    output  reg                 key_o       
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter       IDLE        =       4'b0001     ;    
parameter       S1          =       4'b0010     ;
parameter       S2          =       4'b0100     ;
parameter       S3          =       4'b1000     ;

reg                 [ 3:0]      state           ;
reg                 [ 9:0]      cnt             ;
reg                             key_r1          ;
reg                             key_r2          ;
reg                             key_r3          ;
reg                             nege_flag       ;
reg                             pose_flag       ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk)
    key_r1          <=      key;

always @(posedge sclk)
    key_r2          <=      key_r1;

always @(posedge sclk)
    key_r3          <=      key_r2;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        nege_flag       <=      1'b0;
    else if(key_r3 == 1'b1 && key_r2 == 1'b0)
        nege_flag       <=      1'b1;
    else
        nege_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        pose_flag       <=      1'b0;
    else if(key_r3 == 1'b0 && key_r2 == 1'b1) 
        pose_flag       <=      1'b1;
    else
        pose_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      IDLE;
    else case(state)
        IDLE    :   if(nege_flag == 1'b1)
                        state           <=      S1;
                    else
                        state           <=      IDLE;                        
        S1      :   if(cnt == 10'd999)
                        state           <=      S2;
                    else if(pose_flag == 1'b1)
                        state           <=      IDLE;
                    else
                        state           <=      S1;                        
        S2      :   if(pose_flag == 1'b1)
                        state           <=      S3;
                    else
                        state           <=      S2;                        
        S3      :   if(cnt == 10'd999)
                        state           <=      IDLE;
                    else if(nege_flag == 1'b1)
                        state           <=      S2;
                    else
                        state           <=      S3;
                        
        default :   state           <=      IDLE;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt             <=      10'd0;
    else if(state != S1 && state != S3)
        cnt             <=      10'd0;
    else
        cnt             <=      cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_o           <=      1'b0;
    else if(state == S1 && cnt == 10'd999) 
        key_o           <=      1'b1;
    else
        key_o           <=      1'b0;

endmodule

top模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : top.v
// Create Time  : 2020-01-08 21:18:52
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module top(
    input                   sclk            ,
    input                   rst_n           ,
    input                   key             ,
    output  wire            cs_n            ,
    output  wire            sck             ,
    output  wire            sdi             
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
wire                        key_flag        ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/

key key_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key                    (~key                   ),
    .key_o                  (key_flag               )
);

flash_earse flash_earse_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key_flag               (key_flag               ),
    .cs_n                   (cs_n                   ),
    .sck                    (sck                    ),
    .sdi                    (sdi                    )
);

endmodule 

实验结果

这里说明本次实验的正确性,先将一个程序烧录到flash中,然后再将该程序下载进去,按下按键原程序无法正常运行。

结束语

创作不易,认为文章有帮助的同学们可以收藏点赞支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

发布了14 篇原创文章 · 获赞 4 · 访问量 591

猜你喜欢

转载自blog.csdn.net/zhangningning1996/article/details/103918673