赞
踩
目录
在编写完Verilog程序后,需要将程序下载到硬件FPGA中进行上板验证,通常有两种下载方式:1.直接生成.sof文件,通过JTAG接口下载到FPGA中,这种方式下载速度较快,操作简单,但是由于它是将程序直接烧录到FPGA内部的SRAM中,因此,断电后程序自动丢失。2.通过转换,将生成的.sof文件转换为.jic文件,同样通过JTAG接口下载到FPGA中,这种方式虽然下载速度较慢,但是它是将编写的程序固化到FPGA的外部flash中,断电后程序不丢失,重新上电后,FPGA从外部的flash中读取烧录的程序到内部的SRAM中,重新执行。
如果要对flash中的程序进行擦除,通常有两种方式,一种是直接在程序下载的页面设置,如下图所示;另一种是通过编写擦除程序,对flash内部程序进行擦除。
flash擦除有两种:全擦除和扇区擦除。全擦除是对flash内部所有扇区进行擦除,扇区擦除是对某个扇区内存放的程序进行擦除,最大只能擦除一个扇区。
以黑金的开发板EP4CE6F17C8为例,外部的flash芯片为M25P16,存储大小为16Mbite,内部共有32个扇区,每个扇区为512K,而每个扇区内部又有256页,每页为256个字节。即16Mbite=(256*8)*256*32 = 16Mbite
首先给出M25P16对应的指令,本实验只关注WREN、SE、BE三个指令,如下图所示,WREN是写使能指令、SE是扇区擦除指令、BE是全擦除指令。
下边是写使能指令WREN对应的时序逻辑图,CS片选信号拉低后,SCLK输出时钟信号,SDI对应输出指令信息,分别对应8个bite位0000_0110。
下边是全擦除指令SE对应的时序图,同样,在输出写使能指令后,紧跟着输出全擦除指令,SDIO对应输出指令信息,分别对应8个bite位1100_0111。
下边是扇区擦除BE对应的时序图。如果是扇区擦除,同样也是先输出写使能指令,随后输出扇区擦除指令,如下图,SDI首先输出扇区擦除的指令信息,然后紧跟着输出三个字节的地址信息,分别对应扇区地址、页地址、字节地址。
外界输入一个全擦除的按键信号,FPGA检测到该标志信号对flash中的数据进行全擦除操作。下边给系统框图。
下边是编写全擦除模块程序的时序图,检测到按键信号后,先等待一段时间(等待32个时钟周期),再发送写使能指令,发送完写使能指令后同样等待32个系统时钟周期后,再发送全擦除指令。
根据上述时序图,可以写出下列Verilog代码
- module flash_all(
- input sclk,
- input rst_n,
- input key_flag,
- output reg cs_n,
- output reg sck,
- output reg sdi
- );
- //定义状态机的四个状态
- parameter IDLE = 4'b0001;
- parameter WREN = 4'b0010;
- parameter DELAT= 4'b0100;
- parameter BE = 4'b1000;
-
- parameter WREN_ORDER = 8'h06;//写允许的命令字
- parameter BE_ORDER = 8'hc7;//扇区擦除的命令字
- //parameter SE_ADDR = 24'h00_01_02;//擦除扇区的地址
- reg [ 3:0] state;//状态机的状态
- reg [ 4:0] cnt_32;//对系统时钟计数,两次命令字之间的延迟时间
- reg [ 2:0] cnt_state;//WREN计数三次,中间一次进行数据传输,SE状态计数6次,中间4次进行数据传输
- reg [ 1:0] cnt_4;//生成sck的时钟计数器,sck=12.5M
- reg [ 4:0] bit_cnt;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cs_n <= 1'b1;
- else if(key_flag == 1'b1)
- cs_n <= 1'b0;
- else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2)
- cs_n <= 1'b1;
- else if(state == DELAT && cnt_32 == 'd31)
- cs_n <= 1'b0;
- else if(state == BE && cnt_32 == 'd31 && cnt_state == 'd2)
- cs_n <= 1'b1;
- else
- cs_n <= cs_n;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- state <= IDLE;
- else case(state)
- IDLE:if(key_flag == 1'b1)
- state <= WREN;
- else
- state <= state;
- WREN:if(cnt_32 == 'd31 && cnt_state == 'd2)
- state <= DELAT;
- else
- state <= state;
- DELAT:if(cnt_32 == 'd31)
- state <= BE;
- else
- state <= state;
- BE :if(cnt_32 == 'd31 && cnt_state == 'd2)
- state <= IDLE;
- else
- state <= state;
- default :state <= IDLE;
- endcase
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_32 <= 5'd0;
- else if(cnt_32 == 'd31)
- cnt_32 <= 5'd0;
- else if(state != IDLE)
- cnt_32 <= cnt_32 + 1'b1;
- else
- cnt_32 <= 5'd0;
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_state <= 3'd0;
- else if(state == IDLE)
- cnt_state <= 3'd0;
- else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2)
- cnt_state <= 3'd0;
- else if(state == DELAT && cnt_32 == 'd31)
- cnt_state <= 3'd0;
- else if(state == BE && cnt_32 == 'd31 && cnt_state == 'd2)
- cnt_state <= 3'd0;
- else if(cnt_32 == 'd31)
- cnt_state <= cnt_state + 1'b1;
- else
- cnt_state <= cnt_state;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_4 <= 'd0;
- else if(state == WREN && cnt_state == 'd1)
- cnt_4 <= cnt_4 + 1'b1;
- else if(state == BE && cnt_state == 'd1)
- cnt_4 <= cnt_4 + 1'b1;
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- sck <= 1'b0;
- else if(cnt_4 == 'd0)
- sck <= 1'b0;
- else if(cnt_4 == 'd2)
- sck <= 1'b1;
- else
- sck <= sck;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- sdi <= 1'b0;
- else if(state == WREN && cnt_state == 'd1 && cnt_4 == 'd0)
- sdi <= WREN_ORDER[7-bit_cnt];
- else if(state == BE && cnt_state == 'd1 && cnt_4 == 'd0)
- sdi <= BE_ORDER[7-bit_cnt];
- else if(state == BE && cnt_32 == 'd31 && cnt_state == 'd1)
- sdi <= 1'b0;
- else
- sdi <= sdi;
-
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- bit_cnt <= 5'd0;
- else if(state == WREN && bit_cnt == 'd7 && cnt_4 == 'd2)
- bit_cnt <= 5'd0;
- else if(state == BE && bit_cnt == 'd7 && cnt_4 == 'd2)
- bit_cnt <= 5'd0;
- else if(cnt_4 == 'd2)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
下面给出仿真测试的tb_flash_all.v代码
- `timescale 1ns/1ns
- module tb_flash_all();
-
- reg sys_clk ;
- reg sys_res ;
- reg key ;
- wire cs_n ;
- wire sck ;
- wire sdi ;
-
- //时钟、复位信号、模拟按键信号
- initial
- begin
- sys_clk <= 1'b1;
- sys_res <= 1'b0;
- key <= 1'b0;
- #100
- sys_res <= 1'b1;
- #1000
- key <= 1'b1;
- #20
- key <= 1'b0;
- end
-
- always #10 sys_clk <= ~sys_clk;
-
- flash_all tb_flash_all
- (
- .sclk (sys_clk ),
- .rst_n (sys_res ),
- .key_flag (key ),
-
- .sck (sck ),
- .cs_n (cs_n ),
- .sdi (sdi )
- );
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
下面给出Modelsim仿真结果
该仿真结果与上述编写的时序图基本吻合,验证了程序的准确性。
扇区擦除与全擦除基本类似,唯一不同的地方在于,扇区擦除在发送完擦除指令后,需要继续发送三个相应的地址(扇区地址、页地址、字节地址)。本实验选择擦除的地址是24‘h00_01_02,分别对应扇区地址8‘h00,页地址8‘h01,字节地址8‘h02。
系统框图和扇区擦除模块的时序图与上述的全擦除实验基本相同,这里不再赘述。下边直接给出扇区擦除模块的Verilog代码:
- module flash_earse(
- input sclk,
- input rst_n,
- input key_flag,
- output reg cs_n,
- output reg sck,
- output reg sdi
- );
- //定义状态机的四个状态
- parameter IDLE = 4'b0001;
- parameter WREN = 4'b0010;
- parameter DELAT= 4'b0100;
- parameter SE = 4'b1000;
-
- parameter WREN_ORDER = 8'h06;//写允许的命令字
- parameter SE_ORDER = 8'hd8;//扇区擦除的命令字
- parameter SE_ADDR = 24'h00_01_02;//擦除扇区的地址
- reg [ 3:0] state;//状态机的状态
- reg [ 4:0] cnt_32;//对系统时钟计数,两次命令字之间的延迟时间
- reg [ 2:0] cnt_state;//WREN计数三次,中间一次进行数据传输,SE状态计数6次,中间4次进行数据传输
- reg [ 1:0] cnt_4;//生成sck的时钟计数器,sck=12.5M
- reg [ 4:0] bit_cnt;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cs_n <= 1'b1;
- else if(key_flag == 1'b1)
- cs_n <= 1'b0;
- else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2)
- cs_n <= 1'b1;
- else if(state == DELAT && cnt_32 == 'd31)
- cs_n <= 1'b0;
- else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd5)
- cs_n <= 1'b1;
- else
- cs_n <= cs_n;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- state <= IDLE;
- else case(state)
- IDLE:if(key_flag == 1'b1)
- state <= WREN;
- else
- state <= state;
- WREN:if(cnt_32 == 'd31 && cnt_state == 'd2)
- state <= DELAT;
- else
- state <= state;
- DELAT:if(cnt_32 == 'd31)
- state <= SE;
- else
- state <= state;
- SE :if(cnt_32 == 'd31 && cnt_state == 'd5)
- state <= IDLE;
- else
- state <= state;
- default :state <= IDLE;
- endcase
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_32 <= 5'd0;
- else if(cnt_32 == 'd31)
- cnt_32 <= 5'd0;
- else if(state != IDLE)
- cnt_32 <= cnt_32 + 1'b1;
- else
- cnt_32 <= 5'd0;
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_state <= 3'd0;
- else if(state == IDLE)
- cnt_state <= 3'd0;
- else if(state == WREN && cnt_32 == 'd31 && cnt_state == 'd2)
- cnt_state <= 3'd0;
- else if(state == DELAT && cnt_32 == 'd31)
- cnt_state <= 3'd0;
- else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd5)
- cnt_state <= 3'd0;
- else if(cnt_32 == 'd31)
- cnt_state <= cnt_state + 1'b1;
- else
- cnt_state <= cnt_state;
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- cnt_4 <= 'd0;
- else if(state == WREN && cnt_state == 'd1)
- cnt_4 <= cnt_4 + 1'b1;
- else if(state == SE && cnt_state >= 'd1 && cnt_state <= 'd4)
- cnt_4 <= cnt_4 + 1'b1;
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- sck <= 1'b0;
- else if(cnt_4 == 'd0)
- sck <= 1'b0;
- else if(cnt_4 == 'd2)
- sck <= 1'b1;
- else
- sck <= sck;
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- sdi <= 1'b0;
- else if(state == WREN && cnt_state == 'd1 && cnt_4 == 'd0)
- sdi <= WREN_ORDER[7-bit_cnt];
- else if(state == SE && cnt_state == 'd1 && cnt_4 == 'd0)
- sdi <= SE_ORDER[7-bit_cnt];
- else if(state == SE && cnt_4 == 'd0 && cnt_state > 'd1 && cnt_state <= 'd4)
- sdi <= SE_ADDR[31-bit_cnt];
- else if(state == SE && cnt_32 == 'd31 && cnt_state == 'd4)
- sdi <= 1'b0;
- else
- sdi <= sdi;
-
-
- always @(posedge sclk or negedge rst_n)
- if(rst_n == 1'b0)
- bit_cnt <= 5'd0;
- else if(state == WREN && bit_cnt == 'd7 && cnt_4 == 'd2)
- bit_cnt <= 5'd0;
- else if(state == SE && bit_cnt == 'd31 && cnt_4 == 'd2)
- bit_cnt <= 5'd0;
- else if(cnt_4 == 'd2)
- bit_cnt <= bit_cnt + 1'b1;
- else
- bit_cnt <= bit_cnt;
-
- endmodule
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
由于tb文件与上述全擦除实验的tb文件基本类似,因此这里直接给出仿真后的波形图,结果如下。检测到KEY按键信号后,同样等待一段时间,然后发送写使能指令,再等待一段时间,然后发送扇区擦除指令接着继续发送三个字节的地址。
开发板选用的是黑金的FPGA,型号是EP4CE6F17C8,将一个事先写好的数码管显示实验的程序编译好后转换成.jic文件,然后将该程序固化到FPGA中,重新上电后再将上述的全擦除实验的程序编译后下载到FPGA中,观察如下图所示:
可以明显的观察到,数码管不再显示,说明Flash中的程序已经被擦除。
初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。