通过全擦除和扇区擦除可将固化到Flash芯片中的程序擦除。

一 SPI_Flash全擦除

全擦除就是将Flash芯片所有的存储空间都进行擦除,使各个存储空间的内存数据都恢复到初始值,Flash全擦除有两种方式:

(1)利用FPGA编译软件,通过Quartus软件中的“programmer”可将烧录到Flash中的*.jic文件擦除,如下图所示。

(2)通过编写全擦除程序,将Flash芯片进行全擦除。

1.1实验内容

编写全擦除程序,将Flash芯片事先烧录的程序擦除,使FPGA重新上电后,程序无法自启动。

全擦除指令和时序如下图所示:

由手册可知,全擦除指令是将Flash芯片中的所有存储单元置1,在写入全擦除指令之前,需要写入写使能(WREN)指令,将芯片置为写使能锁存(WEL)状态。拉低片选信号,写入全擦除指令,在指令写入过程中片选信号保持低电平,当指令被芯片所存后,将片选信号拉高;全擦除指令被锁存并执行后,需要等待一个完整的全擦除周期(tBE),才能完成Flash芯片的全擦除操作。

写使能指令和时序如下图所示:

写使能指令、全擦除指令以及其它操作指令写入Flash之前需要遵循芯片的串行输入时序,如下图所示:

需要注意tSLCH、tCHSH、tSHSL这三个参数值:

tSLCH:片选信号拉低到第一个有效数据写入,需要等待的时间,必须大于等于5ns。

tCHSH:最后一个有效数据写入到片选信号拉高,需要等待的时间,必须大于等于5ns。

tSHSL:片选信号拉高到再次拉低,需要等待的时间,必须大于等于100ns。

因此,完整的全擦除操作时序图如下所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。

1.2工程代码

全擦除工程主要包含顶层例化flash_be_top、按键消抖key_filter、擦除控制spi_flash_be三个子模块。

spi_flash_be模块代码如下:

`timescale 1ns/1ps
module spi_flash_be(input           clk,input           rst_n,input         key_in,output   reg     cs_n,output reg     sck,output  reg     mosi
);parameter WR_EN_INST  =  8'h06; //写使能指令
parameter   BE_INST     =  8'hc7; //全擦除指令parameter    IDLE            =  4'b0001;   //空闲状态
parameter   WR_EN           =  4'b0010;   //写使能状态
parameter   DELAY           =  4'b0100;   //片选拉高等待状态
parameter   BE              =  4'b1000; //全擦除状态reg    [3:0]   curr_state;
reg [3:0]   next_state;
reg [4:0]   clk_cnt;            //系统时钟计数器
reg [2:0]   byte_cnt;       //字节计数器
reg [1:0]   sck_cnt;            //sck时钟计数器
reg [2:0]   bit_cnt;            //数据位计数器always @(posedge clk or negedge rst_n)
beginif(!rst_n)curr_state   <=  IDLE;elsecurr_state <=  next_state;
endalways @(*)
beginnext_state <=  IDLE;case(curr_state)IDLE:beginif(key_in)next_state <=  WR_EN;elsenext_state    <=  IDLE;   endWR_EN:beginif(byte_cnt == 3'd1 && clk_cnt == 5'd31)next_state  <=  DELAY;elsenext_state    <=  WR_EN;      endDELAY:beginif(byte_cnt == 3'd2 && clk_cnt == 5'd31)next_state  <=  BE;elsenext_state   <=  DELAY;endBE:beginif(byte_cnt == 3'd6 && clk_cnt == 5'd31)next_state   <=  IDLE;elsenext_state <=  BE; enddefault: next_state  <=  IDLE;endcaseend//clk_Cnt:系统时钟计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)clk_cnt  <=  5'd0;else if(curr_state != IDLE)clk_cnt <=    clk_cnt + 1'b1;elseclk_cnt <= 5'd0;
end//byte_cnt:字节计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)byte_cnt <=  3'd0;else if(byte_cnt == 3'd6 && clk_cnt == 5'd31)byte_cnt   <=  3'd0;else if(clk_cnt == 5'd31)byte_cnt  <=  byte_cnt + 1'b1;elsebyte_cnt  <=  byte_cnt;
end//sck_cnt:sck时钟计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)sck_cnt  <=  2'd0;else if(curr_state == WR_EN && byte_cnt == 3'd1)sck_cnt  <=  sck_cnt + 1'b1;else if(curr_state == BE && byte_cnt == 3'd5)sck_cnt  <=  sck_cnt + 1'b1;elsesck_cnt    <=  2'd0;
end//bit_cnt:数据位计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)bit_cnt <= 3'd0;else if(sck_cnt == 2'd1)bit_cnt  <=  bit_cnt + 1'b1;elsebit_cnt    <=  bit_cnt;
end//cs_n:片选信号
always @(posedge clk or negedge rst_n)
beginif(!rst_n)cs_n <=  1'b1;else if(key_in)cs_n   <=  1'b0;else if(byte_cnt == 3'd2 && clk_cnt == 5'd31)cs_n   <=  1'b1;else if(byte_cnt == 3'd3 && clk_cnt == 5'd31)cs_n   <=  1'b0;else if(byte_cnt == 3'd6 && clk_cnt == 5'd31)cs_n   <=  1'b1;elsecs_n  <=  cs_n;
end//sck:sck时钟生成
always @(posedge clk or negedge rst_n)
beginif(!rst_n)sck  <=  1'b0;else if(sck_cnt == 2'd1)sck    <=  1'b1;else if(sck_cnt == 2'd3)sck    <=  1'b0;elsesck   <=  sck;
end//mosi:
always @(posedge clk or negedge rst_n)
beginif(!rst_n)mosi <=  1'b0;else if(byte_cnt == 3'd1 && clk_cnt == 5'd31)mosi   <=  1'b0;else if(byte_cnt == 3'd5 && clk_cnt == 5'd31)mosi   <=  1'b0;else if(byte_cnt == 3'd1 && sck_cnt == 2'd0)    //写入写使能指令mosi   <=  WR_EN_INST[7 - bit_cnt];else if(byte_cnt == 3'd5 && sck_cnt == 2'd0) //写入全擦除指令mosi    <=  BE_INST[7 - bit_cnt];elsemosi   <= mosi;
endendmodule

顶层模块flash_be_top代码如下:

`timescale 1ns/1ps
module flash_be_top(input           sys_clk,input           rst_n,input         key_in, output          cs_n,output         sck,output          mosi
);wire  key_flag;spi_flash_be u_spi_flash_be(.clk(sys_clk),.rst_n(rst_n),.key_in(key_flag),.cs_n(cs_n),.sck(sck),.mosi(mosi)
);key_filter u_key_filter(.clk(sys_clk),.rst_n(rst_n),.key_in(key_in),      //按键输入.key_flag(key_flag)       //检测到按键按下标志信号
);endmodule 

仿真代码flsh_be_top_tb如下:

`timescale 1ns/1ps
module flsh_be_top_tb();reg     sys_clk;
reg     rst_n;
reg     key_in;wire cs_n;
wire    sck;
wire    mosi;initial
beginsys_clk    =  1'b1;rst_n     =  1'b0;key_in    =  1'b1;#200rst_n     =  1'b1;key_in    =  1'b0;#21_000_000key_in =  1'b1;
endalways #10 sys_clk   <=  ~sys_clk;defparam memory.mem_access.initfile = "initmemory.txt";flash_be_top u_flash_be_top(.sys_clk(sys_clk),.rst_n(rst_n),.key_in(key_in),.cs_n(cs_n),.sck(sck),.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 

仿真结果如下:

仿真结果显示,当byte_cnt 为3’d1时完成写使能指令8’h06的写入,当字节计数器byte_cnt为3’d5时完成全擦除指令的写入。之后等待40s后,仿真结果提示全擦除操作完成。

1.3 板级验证

首先将流水灯程序烧录到Flash芯片中,断电后重新上电检测Flash芯片中的流水灯程序可以自启动,然后将全擦除程序下载到FPGA开发板中,之后断电后重新上电,发现流水灯程序无法自启动,可以说明全擦除程序板级验证成功。

二 SPI_flash扇区擦除

M25P16 Flash芯片总存储容量为16Mbit,分为32个扇区,每个扇区容量为512Kbit,扇区地址如下图所示:

2.1实验内容

通过扇区擦除工程将实现烧录到Flash芯片中的流水灯程序所在的某个扇区进行擦除,使流水灯程序上电之后无法自启动,本次选择擦除第0个扇区,扇区地址为24’h00_05_20。

扇区擦除指令和时序如下所示:

从数据手册可以看出,扇区擦除指令是将Flash芯片中的被选中扇区的所有存储单元置为1,在Flash芯片写入扇区擦除指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态。然后拉低片选信号,写入扇区擦除指令、扇区地址、页地址和字节地址,在写入过程中,片选信号一直保持低电平状态,当指令和地址被芯片锁存后,将片选信号拉高。扇区擦除指令、地址被锁存并执行后,需要等待一个完整的扇区擦除周期(tSE),才能完成Flash芯片的扇区擦除操作。

完整的扇区擦除操作时序如下图所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。

2.2工程代码

扇区擦除工程主要包括顶层例化flash_se_top、按键消抖key_filter、扇区擦除控制spi_flash_se三个子模块。

扇区擦除控制spi_flash_se模块如下:

`timescale 1ns/1ps
module spi_flash_se
#(parameter SE_ADDR     =  8'h00, //扇区地址parameter PP_ADDR     =  8'h05, //页地址parameter  BYTE_ADDR   =  8'h20  //字节地址
)
(input      clk,input       rst_n,input     key_in,output   reg cs_n,output reg sck,output  reg mosi
);localparam    WR_EN_INST  =  8'h06; //写使能指令
localparam  SE_INST     =  8'hd8; //扇区擦除指令localparam  IDLE            =  4'b0001;
localparam  WR_EN           =  4'b0010;
localparam  DELAY           =  4'b0100;
localparam  SE              =  4'b0000;reg    [3:0]   curr_state;
reg [3:0]   next_state;
reg [4:0]   clk_cnt;
reg [3:0]   byte_cnt;
reg [2:0]   bit_cnt;
reg [1:0]   sck_cnt;always @(posedge clk or negedge rst_n)
beginif(!rst_n)curr_state   <=  IDLE;elsecurr_state <=  next_state;
endalways @(*)
beginnext_state <=  IDLE;case(curr_state)IDLE:beginif(key_in)next_state <=  WR_EN;elsenext_state    <=  IDLE;endWR_EN:beginif(byte_cnt == 4'd2 && clk_cnt == 5'd31)next_state <=  DELAY;elsenext_state    <=  WR_EN;      endDELAY:beginif(byte_cnt == 4'd3 && clk_cnt == 5'd31)next_state  <=  SE;elsenext_state   <=  DELAY;      endSE:beginif(byte_cnt == 4'd9 && clk_cnt == 5'd31)next_state <=  IDLE;elsenext_state <=  SE;     enddefault: next_state <= IDLE;endcase
end//clk_cnt:系统时钟计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)clk_cnt  <=  5'd0;else if(curr_state != IDLE)clk_cnt   <=  clk_cnt + 1'b1;elseclk_cnt    <=  5'd0;
end//byte_cnt:字节计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)byte_cnt <=  4'd0;else if(byte_cnt == 4'd9 && clk_cnt == 5'd31)byte_cnt   <=  4'd0;else if(clk_cnt == 5'd31)byte_cnt  <=  byte_cnt + 1'b1;elsebyte_cnt  <=  byte_cnt;
end//sck_cnt:sck时钟计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)sck_cnt  <=  2'd0;else if(curr_state == WR_EN && byte_cnt == 4'd1)sck_cnt  <=  sck_cnt + 1'b1;else if(byte_cnt >= 4'd5 && byte_cnt <= 4'd8)sck_cnt <=  sck_cnt + 1'b1;elsesck_cnt    <=  2'd0;
end//bit_cnt:数据位计数器
always @(posedge clk or negedge rst_n)
beginif(!rst_n)bit_cnt  <=  1'b0;else if(sck_cnt == 2'd1)bit_cnt    <=  bit_cnt + 1'b1;elsebit_cnt    <=  bit_cnt;
end//sck:sck时钟生成
always @(posedge clk or negedge rst_n)
beginif(!rst_n) sck <=  1'b0;else if(sck_cnt == 2'd1)sck    <=  1'b1;else if(sck_cnt == 2'd3)sck    <=  1'b0;elsesck   <=  sck;
end//cs_n:片选信号
always @(posedge clk or negedge rst_n)
beginif(!rst_n)cs_n <=  1'b1;else if(key_in)cs_n   <=  1'b0;else if(curr_state == WR_EN && byte_cnt == 4'd2 && clk_cnt == 5'd31)cs_n  <=  1'b1;else if(curr_state == DELAY && byte_cnt == 4'd3 && clk_cnt == 5'd31)cs_n  <=  1'b0;else if(curr_state == SE && byte_cnt == 4'd9 && clk_cnt == 5'd31)cs_n <=  1'b1;else cs_n <=  cs_n;
end//mosi:数据输出
always @(posedge clk or negedge rst_n)
beginif(!rst_n)mosi <=  1'b0;else if(byte_cnt == 4'd1 && clk_cnt == 5'd31)mosi   <=  1'b0;else if(byte_cnt == 4'd8 && clk_cnt == 5'd31)mosi   <=  1'b0;else if(byte_cnt == 4'd1 && sck_cnt == 2'd0)mosi    <=  WR_EN_INST[7 - bit_cnt];else if(byte_cnt == 4'd5 && sck_cnt == 2'd0)mosi  <=  SE_INST[7 - bit_cnt];else if(byte_cnt == 4'd6 && sck_cnt == 2'd0)mosi <=  SE_ADDR[7 - bit_cnt];else if(byte_cnt == 4'd7 && sck_cnt == 2'd0)mosi <=  PP_ADDR[7 - bit_cnt];else if(byte_cnt == 4'd8 && sck_cnt == 2'd0)mosi <=  BYTE_ADDR[7 - bit_cnt];else mosi    <=  mosi;
endendmodule 

顶层例化flash_se_top模块如下:

`timescale 1ns/1ps
module flash_se_top(input           sys_clk,input           rst_n,input         key_in,output           cs_n,output         sck,output          mosi
);parameter SE_ADDR     =  8'h00; //扇区擦除地址
parameter   PP_ADDR     =  8'h05; //页地址
parameter   BYTE_ADDR   =  8'h20; //字节地址wire      key_flag;//spi_flash_se模块例化
spi_flash_se
#(.SE_ADDR  (SE_ADDR),.PP_ADDR  (PP_ADDR),.BYTE_ADDR    (BYTE_ADDR)
)
u_spi_flash_se(.clk(sys_clk),.rst_n(rst_n),.key_in(key_flag),.cs_n(cs_n),.sck(sck),.mosi(mosi)
);//key_filter 模块例化
key_filter u_key_filter(.clk(sys_clk),.rst_n(rst_n),.key_in(key_in),        //按键输入.key_flag(key_flag)       //检测到按键按下标志信号
);endmodule 

仿真代码flash_se_top_tb如下:

`timescale 1ns/1ps
module flash_se_top_tb();reg        sys_clk;
reg     rst_n;
reg     key_in;wire cs_n;
wire    sck;
wire    mosi;initial
beginsys_clk    =  1'b1;rst_n =  1'b0;key_in    =  1'b1;#200rst_n =  1'b1;key_in    =  1'b0;#21_000_000key_in =  1'b1;
endalways #10 sys_clk   <=  ~sys_clk;defparam memory.mem_access.initfile = "initmemory.txt";flash_se_top u_flash_se_top(.sys_clk(sys_clk),.rst_n(rst_n),.key_in(key_in),.cs_n(cs_n),.sck(sck),.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 

仿真结果如下:

仿真结果显示,在byte_cnt为4’d1时,完成写使能指令写入,当byte_cnt为4’d5时,完成扇区擦除指令写入,随后发送需要擦除的扇区地址、页地址和字节地址三个字节数据,经过3s后,仿真结果提示扇区擦除操作完成。

2.3 板级验证

将流水灯程固化到Flash芯片中,断电后上电验证流水灯程序可以自启动。然后下载扇区擦除工程到Cyclone IV 开发板中,按下按键,执行扇区擦除操作。最后,重新断电后上电,发现流水灯程序无法自启动,证明扇区擦除程序板级验证成功。

SPI协议(二):SPI_Flash(M25P16)擦除操作相关推荐

  1. 基于SPI协议的Flash全擦除

    基于SPI协议的Flash全擦除 `timescale 1ns / 1ps module flash_be_ctrl(input wire sys_clk,//系统时钟频率50MHZ input wi ...

  2. FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用

    写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 在上篇文章,简要介绍了SPI协 ...

  3. 【FPGA】SPI协议详解及对flash读写操作

    FPGA基于SPI实现对flash读写操作 概括 一.SPI协议.flash讲解 1.SPI协议 2.flash (1)WREN (2)RDID (3)WRSR (4)READ (5)PP (6)SE ...

  4. STM32F429入门(二十一):SPI协议及SPI读写FLASH

    IIC主要用于通讯速率一般的场合,而SPI一般用于较高速的场合. 一.SPI协议简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设 ...

  5. android 获取gpio电平值_使用GPIO实现SPI协议操作OLED

    来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版) 作者:韦东山 本文字数:5055,阅读时长:3分钟 现在开始写代码,使用GPIO实现SPI协议操作. 我们现 ...

  6. 基于SPI协议的Flash驱动控制-扇区擦除

    目录 Flash扇区擦除 实现原理 verilog设计代码 verilog测试代码 Flash扇区擦除 实现原理 扇区的概念 Flash型号的数字代表容量,单位兆bit,如M25P16,此Flash的 ...

  7. 【STM32】SPI协议通信详解

    目录 一.SPI协议简介 二.SPI物理层 三.SPI协议层 1.通讯的起始和停止信号 2.数据有效性 3.时钟信号的相位和极性(重点) 四.SPI 特性及架构(重点) 1.通信引脚 2.时钟控制逻辑 ...

  8. STM32学习之SPI协议(读写FLASH)

    关于STM32学习分享 第八章 SPI协议(读写FLASH) 文章目录 关于STM32学习分享 前言 二.代码 1.spi_flash.c 2.spi_flash.h 3.main.c 总结 前言 开 ...

  9. FPGA实现的SPI协议(一)----SPI驱动

    写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 1.什么是SPI协议 SPI( ...

最新文章

  1. 用计算机能改装成万用表吗,用旧手机电池修改万用表
  2. CentOS使用chkconfig增加开机服务提示service xxx does not support chkconfig的问题解决
  3. win10系统svn服务器端安装步骤,win10系统安装svn服务器的解决步骤
  4. dev c++怎么调试_「正点原子NANO STM32开发板资料连载」第十八章 USMART 调试组件...
  5. C++利用访函数进行选择排序
  6. 前端必看 | 2D游戏化互动入门基础知识
  7. oracle exp imp
  8. 模板变量,过滤器和静态文件引用
  9. C#开发笔记之09-如何用C#判断社会信用代码是否合法?
  10. 【Elasticsearch】java 客户端 获取 termvectors 词频 统计
  11. linux防火墙多个 多个ip配置,iptables一次性封多个ip,使用ipset 工具
  12. 信息抽取 | 72篇论文梳理:涉及NER、复杂关系、小样本、文档级、多模态、开放域抽取...
  13. vue学习(9)-路由守卫
  14. 美团员工被指用钓鱼邮件获拼多多薪资;华为回应暂无其它手机厂商接入HarmonyOS;GCC 放弃版权转让政策...
  15. jboss7 应用详解_【扔掉说明书114】本田 思域 2020款 舒适与娱乐功能详解
  16. 速率法和终点法的区别_终点法 速率法 二点法
  17. oracle学习札记46
  18. 惠普服务器sd卡作用,HP服务器如何操作设置
  19. CSJ加人|cs如何加人|cs加人快捷键
  20. 《C++游戏编程入门 第四版》的例子Blackjack-

热门文章

  1. 用两种遍历方法判断图中两点是否有路径
  2. 2022云原生网络趋势 | K8s托管整个基础设施、多云、边缘计算、安全等场景,将云原生网络带向新战场
  3. EcoVadis认证的评分标准详解
  4. [C语言]宏定义#define的使用详解
  5. 计算机垃圾回收站内容怎么恢复,电脑回收站文件被删除了如何恢复|电脑恢复回收站文件的方法...
  6. ios12最后一个正式版_iOS 12.4 正式版发布,最后一版 iOS12 值得升级吗
  7. 在计算机中移动硬盘一般用作什么,移动硬盘装在电脑上使用(肿么装)
  8. Word 中取消所有的超级链接
  9. 毕业设计 基于Arduino的智能移动保湿器
  10. MongDB如何安装和使用(完结)