【时序协议】-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地址
全擦除波形:
扇区擦除波形: