当一块Flash芯片中的不同的扇区烧录了不同的程序,而我们只想擦除某个扇区的程序保留其他程序时,Flash的全擦除是不能满足要求的,这时候就需要扇区擦除来实现这一功能。扇区擦除可以对Flash芯片中的某一扇区进行擦除而不改变其他扇区中的存储数据,要擦除扇区的选择通过扇区擦除地址来表示。
1、实验目标
编写扇区擦除工程,擦除事先烧录到Flash中的某程序所占的某个扇区,使该程序不能正常工作。在此次实验工程,我们选择擦除第0个扇区,擦除地址为24’h00_04_25。
2、时序分析
扇区擦除(Sector Erase)操作,简称SE,操作指令为8’b1101_0000(D8h),具体见图 。
由数据手册中扇区擦除介绍部分可知,扇区擦除指令是将Flash芯片中的被选中扇区的所有存储单元设置为全1,在Flash芯片写入扇区擦出指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态;随后要拉低片选信号,写入扇区擦除指令、扇区地址、页地址和字节地址,在指令、地址写入过程 中,片选信号始终保持低电平,待指令、地址被芯片锁存后,将片选信号拉高;扇区擦除指令、地址被锁存并执行后,需要等待一个完整的扇区擦除周期(tSE),才能完成Flash芯片的扇区擦除操作。扇区擦除操作的详细介绍及时序图,具体见图
上文全擦除操作中我们提到,扇区擦除(SE)指令写入前必须先对Flash芯片写入写使能(WREN)指令。 结合写使能指令、扇区擦除指令的相关内容和操作时序,绘制完整扇区擦除操作时序图如图 。
3、程序设计
程序设计可参考作者全擦除文章https://blog.csdn.net/qq_52438147/article/details/142622093?spm=1001.2014.3001.5502
Flash扇区擦除模块波形图,具体见图 :
3、程序设计
flash_se_ctrl.v
`timescale 1ns/1ns
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 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 ; //比特计数器
//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
仿真设计:
`timescale 1ns/1ns
module tb_flash_se_ctrl();
//wire define
wire cs_n;
wire sck ;
wire mosi ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg key ;
//时钟、复位信号、模拟按键信号
initial
begin
sys_clk = 0;
sys_rst_n <= 0;
key <= 0;
#100
sys_rst_n <= 1;
#1000
key <= 1;
#20
key <= 0;
end
always #10 sys_clk <= ~sys_clk;
//写入Flash仿真模型初始值(全F)
defparam memory.mem_access.initfile = "initmemory.txt";
//------------- flash_se_ctrl_inst -------------
flash_se_ctrl flash_se_ctrl_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.key (key ), //按键输入信号
.sck (sck ), //串行时钟
.cs_n (cs_n ), //片选信号
.mosi (mosi ) //主输出从输入数据
);
//------------- 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
关于flash的仿真模型文件请移步到全擦除文章中,笔者已经上传了
仿真波形: