07_flash全擦除实验

1. SPI 协议

1.1 SPI 协议

在这里插入图片描述
在这里插入图片描述

1.2 SPI 物理层

对于 SPI 协议的物理层,需要讲解的就是 SPI 通讯设备的连接方式和设备引脚的功能描述。SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的个数,SPI 通讯设备之间的连接方式可分为一主一从和一主多从。
一主一从 SPI 通讯设备连接图
在这里插入图片描述
一主多从 SPI 通讯设备连接图
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.3 SPI 协议层

1.3.1 SPI 通讯模式时序图

在这里插入图片描述
CPOL = 0,空闲状态时 SCK 为低电平
CPOL = 1,空闲状态时SCK 为高电平
CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。

1.3.2 CPHA=0 时的 SPI 通讯模式

CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
在这里插入图片描述

1.3.3 CPHA=1 时的 SPI 通讯模式

CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。
在这里插入图片描述

2. 实验目标

事先向 Flash 芯片中烧录流水灯程序,FPGA 上电执行流水灯程序,下载 Flash 芯片全擦除程序到 FPGA 内部 SRAM 并执行,擦除 Flash 芯片中烧录的流水灯程序,FPGA 重新上电后,无程序执行。

3. SPI-Flash 芯片

3.1 硬件资源

Flash 型号为 W25Q16 存储容量为 16Mbit(2M 字节)
在这里插入图片描述

3.2 板载 Flash 原理图

在这里插入图片描述

3.3 操作时序

3.3.1 全擦除时序

全擦除(Bulk Erase)操作,简称 BE,操作指令为 8’b1100_0111(C7h)
在这里插入图片描述
全擦除时序
在这里插入图片描述

3.3.2 写使能时序

写使能(Write Enable)指令,简称 WREN,操作指令为 8’b0000_0110(06h)。
在这里插入图片描述
写使能指令详细介绍及操作时序
在这里插入图片描述

3.3.3 串行输入时序图

在这里插入图片描述
SPI-Flash 是取 12.5MHZ 来算的。一个时钟周期是 80ns , 则传输8bit 需要 640ns。
还需要注意 t_slch 延迟,>=5ns ,在这里为了方便取 640ns。

全擦除 要把 所有的bite 位 变成1 (BE操作变成 全部为 1 ) 在此时前要有一个写使能指令。
实现全擦除需要下面六个步骤:

  1. 写使能
  2. 器件进入到一个写锁存的状态
  3. 全擦除写入 BE
  4. 写入的时候 拉低 s 片选信号
  5. 写入完成拉高s
  6. 完成后 等待一定的周期 完成

4. 模块框图

在这里插入图片描述

5. 波形图

板卡输入的是50MHZ,而协议是基于12.5MHZ 在这里需要注意。
640ns 则需要32个 50MHZ 的时钟周期。SPI 为 0/0 模式。
当cnt_byte == 1 并且 cnt_sck == 2 因为 0/0 模式下 是偶数沿进行的跟新
奇数沿采样 偶数沿进行数据的跟新
大概波形

计数器规范
在这里插入图片描述
详细波形
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6. RTL

6.1 flash_be_ctrl

`timescale  1ns/1ns




module  flash_be_ctrl
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            BE      =   4'b1000 ;   //全擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
            BE_INST     =   8'b1100_0111;   //全擦除指令

//reg   define
reg     [2:0]   cnt_byte;   //字节计数器
reg     [3:0]   state   ;   //状态机状态
reg     [4:0]   cnt_clk ;   //系统时钟计数器
reg     [1:0]   cnt_sck ;   //串行时钟计数器
reg     [2:0]   cnt_bit ;   //比特计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  3'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))
        cnt_byte    <=  3'd0;
    else    if(cnt_clk == 31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == BE) && (cnt_byte == 3'd5))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == BE))
        cs_n    <=  1'b1;

//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
                state   <=  BE;
        BE:     if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd2))
        mosi    <=  1'b0;
    else    if((state == BE) && (cnt_byte == 3'd6))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令
    else    if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
        mosi    <=  BE_INST[7 - cnt_bit];       //全擦除指令

endmodule

6.2 spi_flash_be

`timescale  1ns/1ns




module  spi_flash_be
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值

//wire  define
wire    po_key  ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//------------- flash_be_ctrl_inst -------------
flash_be_ctrl  flash_be_ctrl_inst
(

    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号

    .sck        (sck        ),  //片选信号
    .cs_n       (cs_n       ),  //串行时钟
    .mosi       (mosi       )   //主输出从输入数据
);

endmodule

7. Testbench

做测试时候需要额外添加的文件。
在这里插入图片描述

7.1 tb_flash_be_ctrl

`timescale  1ns/1ns


module  tb_flash_be_ctrl();

//wire  define
wire            cs_n    ;   //Flash片选信号
wire            sck     ;   //Flash串行时钟
wire            mosi    ;   //Flash主输出从输入信号

//reg   define
reg     sys_clk     ;   //模拟时钟信号
reg     sys_rst_n   ;   //模拟复位信号
reg     key         ;   //模拟全擦除触发信号

//时钟、复位信号、模拟按键信号
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        key <=  1'b0;
        #100
        sys_rst_n   <=  1'b1;
        #1000
        key <=  1'b1;
        #20
        key <=  1'b0;
    end

always  #10 sys_clk <=  ~sys_clk;   //模拟时钟,频率50MHz

//写入Flash仿真模型初始值(全F)
defparam memory.mem_access.initfile = "initmemory.txt";

//------------- flash_be_ctrl_inst -------------
flash_be_ctrl  flash_be_ctrl_inst
(
    .sys_clk    (sys_clk    ),  //输入系统时钟,频率50MHz,1bit
    .sys_rst_n  (sys_rst_n  ),  //输入复位信号,低电平有效,1bit
    .key        (key        ),  //按键输入信号,1bit

    .sck        (sck        ),  //输出串行时钟,1bit
    .cs_n       (cs_n       ),  //输出片选信号,1bit
    .mosi       (mosi       )   //输出主输出从输入数据,1bit
);

//------------- memory -------------
m25p16  memory
(
    .c          (sck    ),  //输入串行时钟,频率12.5Mhz,1bit
    .data_in    (mosi   ),  //输入串行指令或数据,1bit
    .s          (cs_n   ),  //输入片选信号,1bit
    .w          (1'b1   ),  //输入写保护信号,低有效,1bit
    .hold       (1'b1   ),  //输入hold信号,低有效,1bit

    .data_out   (       )   //输出串行数据
);

endmodule

7.2 tb_spi_flash_be

`timescale  1ns/1ns




module  tb_spi_flash_be();

//wire  define
wire    cs_n;
wire    sck ;
wire    mosi ;

//reg   define
reg     clk     ;
reg     rst_n   ;
reg     key     ;

//时钟、复位信号、模拟按键信号
initial
    begin
        clk =   0;
        rst_n   <=  0;
        key <=  0;
        #100
        rst_n   <=  1;
        #1000
        key <=  1;
        #20
        key <=  0;
    end

always  #10 clk <=  ~clk;

defparam memory.mem_access.initfile = "initmemory.txt";

//-------------spi_flash_erase-------------
spi_flash_be    spi_flash_be_inst
(
    .sys_clk    (clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (rst_n  ),  //复位信号,低电平有效
    .pi_key     (key    ),  //按键输入信号

    .sck        (sck    ),  //串行时钟
    .cs_n       (cs_n   ),  //片选信号
    .mosi       (mosi   )   //主输出从输入数据
);

m25p16  memory
(
    .c          (sck    ),  //输入串行时钟,频率12.5Mhz,1bit
    .data_in    (mosi   ),  //输入串行指令或数据,1bit
    .s          (cs_n   ),  //输入片选信号,1bit
    .w          (1'b1   ),  //输入写保护信号,低有效,1bit
    .hold       (1'b1   ),  //输入hold信号,低有效,1bit

    .data_out   (       )   //输出串行数据
);

endmodule
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@大宁字

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值