【时序协议】-SPI- BE全擦除-SE扇区擦除

基础知识

实验目标:

将固化到Flash中的程序,通过全擦除程序将其中的固化程序擦除,重新上电后,不再执行固化文件里的内容

全擦除做了一个啥事:刚出生的Flash芯片里面的数据全部是1,通过jic文件往里面烧录程序,某些位置就会用0替换掉1;全擦除就是把Flash里面的0全部变成1;让Flash失忆

在上板验证时,我曾试过两种下载程序的方法

  • sof文件:将该文件下载到SRAM中,但是会掉电丢失
  • jic文件:将该文件烧录到Flash中,掉电不丢失

Flash在SPI通信中充当从设备

在进行程序固化时,在下图中选择主设备型号

 具体操作

 查看Flash芯片的数据手册:

写使能指令

——执行完毕

——器件:写锁存状态

——在写锁存状态下,写入全擦除指令

——写全擦除指令前先拉低片选信号

——指令写入过程中,片选信号保持低电平

——指令写完后,拉高片选信号

——拉高后,等待一段时间后(全擦除周期tBE),才能完全擦除

全擦除实验时序图

 

程序

一、设计文件

第一种:Flash芯片控制模块——全擦除

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_7byte;   //字节计数器
reg     [3:0]   state   ;   //状态机状态
reg     [4:0]   cnt_32clk ;   //系统时钟计数器
reg     [1:0]   cnt_4sck ;   //串行时钟计数器
reg     [2:0]   cnt_8bit ;   //比特计数器

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

// 时钟总计数器,只此一个管7个计数器
//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_32clk  <=  5'd0;
    else    if(state != IDLE)// tips:这里定义cnt_clk位宽为5,最大记到31,记满后一处清0,就不需要判断cnt_clk计满清零了,实现了0-31的循环计数
        cnt_32clk  <=  cnt_32clk + 1'b1;

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

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_4sck <=  2'd0;// 定义cnt_4sck位宽为2,记到3就溢出清0,用位宽清0就不需要考虑计数器清零了,只关心计数
    else    if((state == WR_EN) && (cnt_7byte == 1'b1))
     cnt_4sck <=  cnt_4sck + 1'b1;// 上面的条件和本句是同步,看0-,此时还不满足条件,等到下一个时钟上升沿就满足了,所以在这里可以看到计数器慢一拍
    else    if((state == BE) && (cnt_7byte == 3'd5))
        cnt_4sck <=  cnt_4sck + 1'b1;

//cs_n:片选信号
// 先看外部输入的异步信号key,异步看0+得知key为1
// 于是片选信号被拉低
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_7byte == 3'd2) && (cnt_32clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_7byte == 3'd3) && (cnt_32clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_7byte == 3'd6) && (cnt_32clk == 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_4sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_4sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_8bit <=  3'd0;
    else    if(cnt_4sck == 2'd2)
        cnt_8bit <=  cnt_8bit + 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)// 先看外部输入的异步信号key,异步看0+得知key为1
          state   <=  WR_EN;// 此时状态不需要等待,立马转移到下一个状态
        WR_EN:  if((cnt_7byte == 3'd2) && (cnt_32clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_7byte == 3'd3) && (cnt_32clk == 5'd31))
                state   <=  BE;
        BE:     if((cnt_7byte == 3'd6) && (cnt_32clk == 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_7byte == 3'd2))
        mosi    <=  1'b0;
    else    if((state == BE) && (cnt_7byte == 3'd6))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_7byte == 3'd1) && (cnt_4sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_8bit];    //写使能指令,高位在前,低位在后;同0-
    else    if((state == BE) && (cnt_7byte == 3'd5) && (cnt_4sck == 5'd0))
        mosi    <=  BE_INST[7 - cnt_8bit];       //全擦除指令

endmodule

第二种:Flash芯片控制模块——扇区擦除

module  flash_se_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 ,   //等待状态
            SE      =   4'b1000 ;   //扇区擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
            SE_INST     =   8'b1101_1000;   //扇区擦除指令
parameter   SECTOR_ADDR =   8'b0000_0000,   //扇区地址
            PAGE_ADDR   =   8'b0000_0100,   //页地址
            BYTE_ADDR   =   8'b0010_0101;   //字节地址

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

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

// 在时序上有7端时间,原则上需要7个计数器
// 此处只使用一个计数器,可以节约资源

//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    <=  4'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 4'd9))
        cnt_byte    <=  4'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 == SE) && (cnt_byte >= 4'd5) && (cnt_byte <= 4'd8))
        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 == 4'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 4'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == 4'd9) && (cnt_clk == 5'd31) && (state == SE))
        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 == 4'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 4'd3) && (cnt_clk == 5'd31))
                state   <=  SE;
        SE:     if((cnt_byte == 4'd9) && (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 == 4'd2))
        mosi    <=  1'b0;
    else    if((state == SE) && (cnt_byte == 4'd9))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 4'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];  //写使能指令
    else    if((state == SE) && (cnt_byte == 4'd5) && (cnt_sck == 5'd0))
        mosi    <=  SE_INST[7 - cnt_bit];    //扇区擦除指令
    else    if((state == SE) && (cnt_byte == 4'd6) && (cnt_sck == 5'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == SE) && (cnt_byte == 4'd7) && (cnt_sck == 5'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == SE) && (cnt_byte == 4'd8) && (cnt_sck == 5'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址

endmodule

 

二、测试文件

三、波形图

在看波形图时,可以分状态去看每个状态做了什么事

两种擦除的不同点在操作指令后面的3byte地址

全擦除波形:

 

扇区擦除波形:

 

 

posted @ 2022-04-20 11:19  刘小颜  阅读(375)  评论(0编辑  收藏  举报