07_flash全擦除实验
- 1. SPI 协议
- 1.1 SPI 协议
- 1.2 SPI 物理层
- 1.3 SPI 协议层
- 1.3.1 SPI 通讯模式时序图
- 1.3.2 CPHA=0 时的 SPI 通讯模式
- 1.3.3 CPHA=1 时的 SPI 通讯模式
- 2. 实验目标
- 3. SPI-Flash 芯片
- 3.1 硬件资源
- 3.2 板载 Flash 原理图
- 3.3 操作时序
- 3.3.1 全擦除时序
- 3.3.2 写使能时序
- 3.3.3 串行输入时序图
- 4. 模块框图
- 5. 波形图
- 6. RTL
- 6.1 flash_be_ctrl
- 6.2 spi_flash_be
- 7. Testbench
- 7.1 tb_flash_be_ctrl
- 7.2 tb_spi_flash_be
1. SPI 协议
1.1 SPI 协议
1.2 SPI 物理层
对于 SPI 协议的物理层,需要讲解的就是 SPI 通讯设备的连接方式和设备引脚的功能描述。SPI 通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的个数,SPI 通讯设备之间的连接方式可分为一主一从和一主多从。
一主一从 SPI 通讯设备连接图
一主多从 SPI 通讯设备连接图
1.3 SPI 协议层
1.3.1 SPI 通讯模式时序图
CPOL = 0,空闲状态时 SCK 为低电平
CPOL = 1,空闲状态时SCK 为高电平
CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。
1.3.2 CPHA=0 时的 SPI 通讯模式
CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,偶数跟新。
1.3.3 CPHA=1 时的 SPI 通讯模式
CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,奇数跟新。
2. 实验目标
事先向 Flash 芯片中烧录流水灯程序,FPGA 上电执行流水灯程序,下载 Flash 芯片全擦除程序到 FPGA 内部 SRAM 并执行,擦除 Flash 芯片中烧录的流水灯程序,FPGA 重新上电后,无程序执行。
3. SPI-Flash 芯片
3.1 硬件资源
Flash 型号为 W25Q16 存储容量为 16Mbit(2M 字节)
3.2 板载 Flash 原理图
3.3 操作时序
3.3.1 全擦除时序
全擦除(Bulk Erase)操作,简称 BE,操作指令为 8’b1100_0111(C7h)
全擦除时序
3.3.2 写使能时序
写使能(Write Enable)指令,简称 WREN,操作指令为 8’b0000_0110(06h)。
写使能指令详细介绍及操作时序
3.3.3 串行输入时序图
SPI-Flash 是取 12.5MHZ 来算的。一个时钟周期是 80ns , 则传输8bit 需要 640ns。
还需要注意 t_slch 延迟,>=5ns ,在这里为了方便取 640ns。
全擦除 要把 所有的bite 位 变成1 (BE操作变成 全部为 1 ) 在此时前要有一个写使能指令。
实现全擦除需要下面六个步骤:
- 写使能
- 器件进入到一个写锁存的状态
- 全擦除写入 BE
- 写入的时候 拉低 s 片选信号
- 写入完成拉高s
- 完成后 等待一定的周期 完成
4. 模块框图
5. 波形图
板卡输入的是50MHZ,而协议是基于12.5MHZ 在这里需要注意。
640ns 则需要32个 50MHZ 的时钟周期。SPI 为 0/0 模式。
当cnt_byte == 1 并且 cnt_sck == 2 因为 0/0 模式下 是偶数沿进行的跟新
奇数沿采样 偶数沿进行数据的跟新
大概波形
计数器规范
详细波形
6. RTL
6.1 flash_be_ctrl
`timescale 1ns/1ns
module flash_be_ctrl
(input wire sys_clk , //系统时钟,频率50MHzinput 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_byte; //字节计数器
reg [3:0] state ; //状态机状态
reg [4:0] cnt_clk ; //系统时钟计数器
reg [1:0] cnt_sck ; //串行时钟计数器
reg [2:0] cnt_bit ; //比特计数器//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************////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 <= 3'd0;else if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))cnt_byte <= 3'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 == BE) && (cnt_byte == 3'd5))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 == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))cs_n <= 1'b1;else if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))cs_n <= 1'b0;else if((cnt_byte == 3'd6) && (cnt_clk == 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_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;elsecase(state)IDLE: if(key == 1'b1)state <= WR_EN;WR_EN: if((cnt_byte == 3'd2) && (cnt_clk == 5'd31))state <= DELAY;DELAY: if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))state <= BE;BE: if((cnt_byte == 3'd6) && (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 == 3'd2))mosi <= 1'b0;else if((state == BE) && (cnt_byte == 3'd6))mosi <= 1'b0;else if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))mosi <= WR_EN_INST[7 - cnt_bit]; //写使能指令else if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))mosi <= BE_INST[7 - cnt_bit]; //全擦除指令endmodule
6.2 spi_flash_be
`timescale 1ns/1ns
module spi_flash_be
(input wire sys_clk , //系统时钟,频率50MHzinput wire sys_rst_n , //复位信号,低电平有效input wire pi_key , //按键输入信号output wire cs_n , //片选信号output wire sck , //串行时钟output wire mosi //主输出从输入数据
);//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter CNT_MAX = 20'd999_999; //计数器计数最大值//wire define
wire po_key ;//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(.CNT_MAX (CNT_MAX ) //计数器计数最大值
)
key_filter_inst
(.sys_clk (sys_clk ), //系统时钟,频率50MHz.sys_rst_n (sys_rst_n ), //复位信号,低电平有效.key_in (pi_key ), //按键输入信号.key_flag (po_key ) //消抖后信号
);//------------- flash_be_ctrl_inst -------------
flash_be_ctrl flash_be_ctrl_inst
(.sys_clk (sys_clk ), //系统时钟,频率50MHz.sys_rst_n (sys_rst_n ), //复位信号,低电平有效.key (po_key ), //按键输入信号.sck (sck ), //片选信号.cs_n (cs_n ), //串行时钟.mosi (mosi ) //主输出从输入数据
);endmodule
7. Testbench
做测试时候需要额外添加的文件。
7.1 tb_flash_be_ctrl
`timescale 1ns/1nsmodule tb_flash_be_ctrl();//wire define
wire cs_n ; //Flash片选信号
wire sck ; //Flash串行时钟
wire mosi ; //Flash主输出从输入信号//reg define
reg sys_clk ; //模拟时钟信号
reg sys_rst_n ; //模拟复位信号
reg key ; //模拟全擦除触发信号//时钟、复位信号、模拟按键信号
initialbeginsys_clk = 1'b1;sys_rst_n <= 1'b0;key <= 1'b0;#100sys_rst_n <= 1'b1;#1000key <= 1'b1;#20key <= 1'b0;endalways #10 sys_clk <= ~sys_clk; //模拟时钟,频率50MHz//写入Flash仿真模型初始值(全F)
defparam memory.mem_access.initfile = "initmemory.txt";//------------- flash_be_ctrl_inst -------------
flash_be_ctrl flash_be_ctrl_inst
(.sys_clk (sys_clk ), //输入系统时钟,频率50MHz,1bit.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效,1bit.key (key ), //按键输入信号,1bit.sck (sck ), //输出串行时钟,1bit.cs_n (cs_n ), //输出片选信号,1bit.mosi (mosi ) //输出主输出从输入数据,1bit
);//------------- 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
7.2 tb_spi_flash_be
`timescale 1ns/1ns
module tb_spi_flash_be();//wire define
wire cs_n;
wire sck ;
wire mosi ;//reg define
reg clk ;
reg rst_n ;
reg key ;//时钟、复位信号、模拟按键信号
initialbeginclk = 0;rst_n <= 0;key <= 0;#100rst_n <= 1;#1000key <= 1;#20key <= 0;endalways #10 clk <= ~clk;defparam memory.mem_access.initfile = "initmemory.txt";//-------------spi_flash_erase-------------
spi_flash_be spi_flash_be_inst
(.sys_clk (clk ), //系统时钟,频率50MHz.sys_rst_n (rst_n ), //复位信号,低电平有效.pi_key (key ), //按键输入信号.sck (sck ), //串行时钟.cs_n (cs_n ), //片选信号.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