本文基于【野火】FPGA开发板视频教程及资料,首先对野火公司表示衷心感谢,我认为原波形图有进一步便于理解和整齐的画法,代码也有可以精简的地方,在这里分享给大家,代码通过Modelsim行为级验证。
目录
波形图
代码
`timescale 1ns/1ns
// Author : NSSC_小天
// Create Date : 2022/11/20
// Module Name : flash_be_ctrl
// Project Name : spi_flash_be
// Target Devices: 黑金 AX309
// Tool Versions : ISE 14.7
// Description : flash全擦除模块
// Revision : V1.0
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 [4:0] cnt_clk ; //系统时钟计数器
reg [3:0] state ; //状态机状态
reg [2:0] cnt_byte; //字节计数器
reg [1:0] cnt_sck ; //串行时钟计数器
reg [2:0] cnt_bit ; //比特计数器
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_clk:系统时钟计数器-640ns,即作为单个byte的发送周期,也作为t_SLCH,t_CHSH,t_SLCH的等待时间
//2的5次方=32,计数0-31,自动溢出
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:串行时钟计数器,用以生成串行时钟,4分频,12.5Mhz
//2的2次方=4,计数0-3,自动溢出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_sck <= 2'd0;
else if(cnt_byte == 3'b1)
cnt_sck <= cnt_sck + 1'b1;
else if(cnt_byte == 3'd5)
cnt_sck <= cnt_sck + 1'b1;
//cnt_bit:控制mosi输出指令的哪一位
//2的3次方=8,计数0-7,自动溢出
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if(cnt_sck == 2'd1)
cnt_bit <= cnt_bit + 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'd3)
sck <= 1'b0;
else if(cnt_sck == 2'd1)
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))
cs_n <= 1'b1;
else if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
cs_n <= 1'b0;
else if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
cs_n <= 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 == 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((cnt_byte == 3'd0) && (cnt_clk == 5'd31))
mosi <= WR_EN_INST[7]; //写使能指令[7]
else if((cnt_byte == 3'd1) && (cnt_sck == 2'd3) && (cnt_clk != 5'd31))
mosi <= WR_EN_INST[7 - cnt_bit]; //写使能指令[6-0]
else if((cnt_byte == 3'd1) && (cnt_clk == 5'd31))
mosi <= 1'b0;
else if((cnt_byte == 3'd4) && (cnt_clk == 5'd31))
mosi <= BE_INST[7]; //全擦除指令[7]
else if((cnt_byte == 3'd5) && (cnt_sck == 2'd3) && (cnt_clk != 5'd31))
mosi <= BE_INST[7 - cnt_bit]; //全擦除指令[6-0]
else if((cnt_byte == 3'd5) && (cnt_clk == 5'd31))
mosi <= 1'b0;
endmodule