赞
踩
通过全擦除和扇区擦除可将固化到Flash芯片中的程序擦除。
全擦除就是将Flash芯片所有的存储空间都进行擦除,使各个存储空间的内存数据都恢复到初始值,Flash全擦除有两种方式:
(1)利用FPGA编译软件,通过Quartus软件中的“programmer”可将烧录到Flash中的*.jic文件擦除,如下图所示。
(2)通过编写全擦除程序,将Flash芯片进行全擦除。
编写全擦除程序,将Flash芯片事先烧录的程序擦除,使FPGA重新上电后,程序无法自启动。
全擦除指令和时序如下图所示:
由手册可知,全擦除指令是将Flash芯片中的所有存储单元置1,在写入全擦除指令之前,需要写入写使能(WREN)指令,将芯片置为写使能锁存(WEL)状态。拉低片选信号,写入全擦除指令,在指令写入过程中片选信号保持低电平,当指令被芯片所存后,将片选信号拉高;全擦除指令被锁存并执行后,需要等待一个完整的全擦除周期(tBE),才能完成Flash芯片的全擦除操作。
写使能指令和时序如下图所示:
写使能指令、全擦除指令以及其它操作指令写入Flash之前需要遵循芯片的串行输入时序,如下图所示:
需要注意tSLCH、tCHSH、tSHSL这三个参数值:
tSLCH:片选信号拉低到第一个有效数据写入,需要等待的时间,必须大于等于5ns。
tCHSH:最后一个有效数据写入到片选信号拉高,需要等待的时间,必须大于等于5ns。
tSHSL:片选信号拉高到再次拉低,需要等待的时间,必须大于等于100ns。
因此,完整的全擦除操作时序图如下所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。
全擦除工程主要包含顶层例化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)
- begin
- if(!rst_n)
- curr_state <= IDLE;
- else
- curr_state <= next_state;
- end
-
- always @(*)
- begin
- next_state <= IDLE;
- case(curr_state)
-
- IDLE:begin
- if(key_in)
- next_state <= WR_EN;
- else
- next_state <= IDLE;
- end
-
- WR_EN:begin
- if(byte_cnt == 3'd1 && clk_cnt == 5'd31)
- next_state <= DELAY;
- else
- next_state <= WR_EN;
- end
-
- DELAY:begin
- if(byte_cnt == 3'd2 && clk_cnt == 5'd31)
- next_state <= BE;
- else
- next_state <= DELAY;
- end
-
- BE:begin
- if(byte_cnt == 3'd6 && clk_cnt == 5'd31)
- next_state <= IDLE;
- else
- next_state <= BE;
- end
-
- default: next_state <= IDLE;
-
- endcase
-
- end
-
- //clk_Cnt:系统时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- clk_cnt <= 5'd0;
- else if(curr_state != IDLE)
- clk_cnt <= clk_cnt + 1'b1;
- else
- clk_cnt <= 5'd0;
- end
- //byte_cnt:字节计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!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;
- else
- byte_cnt <= byte_cnt;
- end
- //sck_cnt:sck时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!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;
- else
- sck_cnt <= 2'd0;
- end
- //bit_cnt:数据位计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 3'd0;
- else if(sck_cnt == 2'd1)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
- end
-
- //cs_n:片选信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!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;
- else
- cs_n <= cs_n;
- end
- //sck:sck时钟生成
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck <= 1'b0;
- else if(sck_cnt == 2'd1)
- sck <= 1'b1;
- else if(sck_cnt == 2'd3)
- sck <= 1'b0;
- else
- sck <= sck;
- end
-
- //mosi:
- always @(posedge clk or negedge rst_n)
- begin
- if(!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];
- else
- mosi <= mosi;
- end
- endmodule
顶层模块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
- begin
- sys_clk = 1'b1;
- rst_n = 1'b0;
- key_in = 1'b1;
- #200
- rst_n = 1'b1;
- key_in = 1'b0;
- #21_000_000
- key_in = 1'b1;
- end
-
- always #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后,仿真结果提示全擦除操作完成。
首先将流水灯程序烧录到Flash芯片中,断电后重新上电检测Flash芯片中的流水灯程序可以自启动,然后将全擦除程序下载到FPGA开发板中,之后断电后重新上电,发现流水灯程序无法自启动,可以说明全擦除程序板级验证成功。
M25P16 Flash芯片总存储容量为16Mbit,分为32个扇区,每个扇区容量为512Kbit,扇区地址如下图所示:
通过扇区擦除工程将实现烧录到Flash芯片中的流水灯程序所在的某个扇区进行擦除,使流水灯程序上电之后无法自启动,本次选择擦除第0个扇区,扇区地址为24’h00_05_20。
扇区擦除指令和时序如下所示:
从数据手册可以看出,扇区擦除指令是将Flash芯片中的被选中扇区的所有存储单元置为1,在Flash芯片写入扇区擦除指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态。然后拉低片选信号,写入扇区擦除指令、扇区地址、页地址和字节地址,在写入过程中,片选信号一直保持低电平状态,当指令和地址被芯片锁存后,将片选信号拉高。扇区擦除指令、地址被锁存并执行后,需要等待一个完整的扇区擦除周期(tSE),才能完成Flash芯片的扇区擦除操作。
完整的扇区擦除操作时序如下图所示,为了方便起见,每个等待时间统一为32个时钟周期,即640ns。
扇区擦除工程主要包括顶层例化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)
- begin
- if(!rst_n)
- curr_state <= IDLE;
- else
- curr_state <= next_state;
- end
- always @(*)
- begin
- next_state <= IDLE;
- case(curr_state)
-
- IDLE:begin
- if(key_in)
- next_state <= WR_EN;
- else
- next_state <= IDLE;
- end
-
- WR_EN:begin
- if(byte_cnt == 4'd2 && clk_cnt == 5'd31)
- next_state <= DELAY;
- else
- next_state <= WR_EN;
- end
-
- DELAY:begin
- if(byte_cnt == 4'd3 && clk_cnt == 5'd31)
- next_state <= SE;
- else
- next_state <= DELAY;
- end
-
- SE:begin
- if(byte_cnt == 4'd9 && clk_cnt == 5'd31)
- next_state <= IDLE;
- else
- next_state <= SE;
- end
-
- default: next_state <= IDLE;
- endcase
- end
- //clk_cnt:系统时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- clk_cnt <= 5'd0;
- else if(curr_state != IDLE)
- clk_cnt <= clk_cnt + 1'b1;
- else
- clk_cnt <= 5'd0;
- end
-
- //byte_cnt:字节计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!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;
- else
- byte_cnt <= byte_cnt;
- end
-
- //sck_cnt:sck时钟计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!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;
- else
- sck_cnt <= 2'd0;
- end
- //bit_cnt:数据位计数器
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- bit_cnt <= 1'b0;
- else if(sck_cnt == 2'd1)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
- end
-
- //sck:sck时钟生成
- always @(posedge clk or negedge rst_n)
- begin
- if(!rst_n)
- sck <= 1'b0;
- else if(sck_cnt == 2'd1)
- sck <= 1'b1;
- else if(sck_cnt == 2'd3)
- sck <= 1'b0;
- else
- sck <= sck;
- end
- //cs_n:片选信号
- always @(posedge clk or negedge rst_n)
- begin
- if(!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)
- begin
- if(!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;
- end
- endmodule
顶层例化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
- begin
- sys_clk = 1'b1;
- rst_n = 1'b0;
- key_in = 1'b1;
- #200
- rst_n = 1'b1;
- key_in = 1'b0;
- #21_000_000
- key_in = 1'b1;
- end
-
- always #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后,仿真结果提示扇区擦除操作完成。
将流水灯程固化到Flash芯片中,断电后上电验证流水灯程序可以自启动。然后下载扇区擦除工程到Cyclone IV 开发板中,按下按键,执行扇区擦除操作。最后,重新断电后上电,发现流水灯程序无法自启动,证明扇区擦除程序板级验证成功。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。