SPI协议(二):SPI_Flash(M25P16)擦除操作
通过全擦除和扇区擦除可将固化到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)擦除操作相关推荐
- 基于SPI协议的Flash全擦除
基于SPI协议的Flash全擦除 `timescale 1ns / 1ps module flash_be_ctrl(input wire sys_clk,//系统时钟频率50MHZ input wi ...
- FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用
写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 在上篇文章,简要介绍了SPI协 ...
- 【FPGA】SPI协议详解及对flash读写操作
FPGA基于SPI实现对flash读写操作 概括 一.SPI协议.flash讲解 1.SPI协议 2.flash (1)WREN (2)RDID (3)WRSR (4)READ (5)PP (6)SE ...
- STM32F429入门(二十一):SPI协议及SPI读写FLASH
IIC主要用于通讯速率一般的场合,而SPI一般用于较高速的场合. 一.SPI协议简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设 ...
- android 获取gpio电平值_使用GPIO实现SPI协议操作OLED
来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版) 作者:韦东山 本文字数:5055,阅读时长:3分钟 现在开始写代码,使用GPIO实现SPI协议操作. 我们现 ...
- 基于SPI协议的Flash驱动控制-扇区擦除
目录 Flash扇区擦除 实现原理 verilog设计代码 verilog测试代码 Flash扇区擦除 实现原理 扇区的概念 Flash型号的数字代表容量,单位兆bit,如M25P16,此Flash的 ...
- 【STM32】SPI协议通信详解
目录 一.SPI协议简介 二.SPI物理层 三.SPI协议层 1.通讯的起始和停止信号 2.数据有效性 3.时钟信号的相位和极性(重点) 四.SPI 特性及架构(重点) 1.通信引脚 2.时钟控制逻辑 ...
- STM32学习之SPI协议(读写FLASH)
关于STM32学习分享 第八章 SPI协议(读写FLASH) 文章目录 关于STM32学习分享 前言 二.代码 1.spi_flash.c 2.spi_flash.h 3.main.c 总结 前言 开 ...
- FPGA实现的SPI协议(一)----SPI驱动
写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 1.什么是SPI协议 SPI( ...
最新文章
- 用计算机能改装成万用表吗,用旧手机电池修改万用表
- CentOS使用chkconfig增加开机服务提示service xxx does not support chkconfig的问题解决
- win10系统svn服务器端安装步骤,win10系统安装svn服务器的解决步骤
- dev c++怎么调试_「正点原子NANO STM32开发板资料连载」第十八章 USMART 调试组件...
- C++利用访函数进行选择排序
- 前端必看 | 2D游戏化互动入门基础知识
- oracle exp imp
- 模板变量,过滤器和静态文件引用
- C#开发笔记之09-如何用C#判断社会信用代码是否合法?
- 【Elasticsearch】java 客户端 获取 termvectors 词频 统计
- linux防火墙多个 多个ip配置,iptables一次性封多个ip,使用ipset 工具
- 信息抽取 | 72篇论文梳理:涉及NER、复杂关系、小样本、文档级、多模态、开放域抽取...
- vue学习(9)-路由守卫
- 美团员工被指用钓鱼邮件获拼多多薪资;华为回应暂无其它手机厂商接入HarmonyOS;GCC 放弃版权转让政策...
- jboss7 应用详解_【扔掉说明书114】本田 思域 2020款 舒适与娱乐功能详解
- 速率法和终点法的区别_终点法 速率法 二点法
- oracle学习札记46
- 惠普服务器sd卡作用,HP服务器如何操作设置
- CSJ加人|cs如何加人|cs加人快捷键
- 《C++游戏编程入门 第四版》的例子Blackjack-
热门文章
- 用两种遍历方法判断图中两点是否有路径
- 2022云原生网络趋势 | K8s托管整个基础设施、多云、边缘计算、安全等场景,将云原生网络带向新战场
- EcoVadis认证的评分标准详解
- [C语言]宏定义#define的使用详解
- 计算机垃圾回收站内容怎么恢复,电脑回收站文件被删除了如何恢复|电脑恢复回收站文件的方法...
- ios12最后一个正式版_iOS 12.4 正式版发布,最后一版 iOS12 值得升级吗
- 在计算机中移动硬盘一般用作什么,移动硬盘装在电脑上使用(肿么装)
- Word 中取消所有的超级链接
- 毕业设计 基于Arduino的智能移动保湿器
- MongDB如何安装和使用(完结)