07_flash全擦除实验

07_flash全擦除实验

  • 1. SPI 协议
    • 1.1 SPI 协议
    • 1.2 SPI 物理层
    • 1.3 SPI 协议层
      • 1.3.1 SPI 通讯模式时序图
      • 1.3.2 CPHA=0 时的 SPI 通讯模式
      • 1.3.3 CPHA=1 时的 SPI 通讯模式
  • 2. 实验目标
  • 3. SPI-Flash 芯片
    • 3.1 硬件资源
    • 3.2 板载 Flash 原理图
    • 3.3 操作时序
      • 3.3.1 全擦除时序
      • 3.3.2 写使能时序
      • 3.3.3 串行输入时序图
  • 4. 模块框图
  • 5. 波形图
  • 6. RTL
    • 6.1 flash_be_ctrl
    • 6.2 spi_flash_be
  • 7. Testbench
    • 7.1 tb_flash_be_ctrl
    • 7.2 tb_spi_flash_be

1. SPI 协议

1.1 SPI 协议

07_flash全擦除实验_第1张图片
07_flash全擦除实验_第2张图片

1.2 SPI 物理层

对于 SPI 协议的物理层,需要讲解的就是 SPI 通讯设备的连接方式和设备引脚的功能描述。SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的个数,SPI 通讯设备之间的连接方式可分为一主一从和一主多从。
一主一从 SPI 通讯设备连接图
07_flash全擦除实验_第3张图片
一主多从 SPI 通讯设备连接图
07_flash全擦除实验_第4张图片
07_flash全擦除实验_第5张图片

07_flash全擦除实验_第6张图片

1.3 SPI 协议层

1.3.1 SPI 通讯模式时序图

07_flash全擦除实验_第7张图片
CPOL = 0,空闲状态时 SCK 为低电平
CPOL = 1,空闲状态时SCK 为高电平
CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。

1.3.2 CPHA=0 时的 SPI 通讯模式

CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
07_flash全擦除实验_第8张图片

1.3.3 CPHA=1 时的 SPI 通讯模式

CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。
07_flash全擦除实验_第9张图片

2. 实验目标

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

3. SPI-Flash 芯片

3.1 硬件资源

Flash 型号为 W25Q16 存储容量为 16Mbit(2M 字节)
07_flash全擦除实验_第10张图片

3.2 板载 Flash 原理图

07_flash全擦除实验_第11张图片

3.3 操作时序

3.3.1 全擦除时序

全擦除(Bulk Erase)操作,简称 BE,操作指令为 8’b1100_0111(C7h)
在这里插入图片描述
全擦除时序
07_flash全擦除实验_第12张图片

3.3.2 写使能时序

写使能(Write Enable)指令,简称 WREN,操作指令为 8’b0000_0110(06h)。
在这里插入图片描述
写使能指令详细介绍及操作时序
07_flash全擦除实验_第13张图片

3.3.3 串行输入时序图

07_flash全擦除实验_第14张图片
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. 模块框图

07_flash全擦除实验_第15张图片

5. 波形图

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

计数器规范
07_flash全擦除实验_第16张图片
详细波形
07_flash全擦除实验_第17张图片


07_flash全擦除实验_第18张图片

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

做测试时候需要额外添加的文件。
07_flash全擦除实验_第19张图片

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

你可能感兴趣的:(#,Verilog学习强化案例,fpga开发)