有个疑问,代码哪里写错了,为什么在0到2状态以及0到4状态时,cs会被拉低一小段
注(平台是国产的efinity,该代码需要在interface的GPIO中添加flash的四根线以及系统时钟clk,还需添加一个锁相环输出clk54m+rst)
该代码可以正确执行写数据功能,状态从写使能到读寄存器到写使能到写数据到读寄存器,注意状态转换之中会插入一个空闲状态IDLE
`timescale 1ms / 10ns
module flash_wr(
input clk, ///54M
output reg spi_clk ,///27M
output reg spi_cs ,
output reg spi_mosi,
input spi_miso,
input clk_54m ,
input rst_n
);
//状态机
parameter IDLE =4'd0;//空闲状态
parameter WEL =4'd1;//写使能状态///cmd_cnt=3 spicmd=1
parameter C_ERA =4'd2;//全局擦除
parameter READ =4'd3;//读数据状态///cmd_cnt=6
parameter WRITE =4'd4;//写状态
parameter S_ERA =4'd6;//全局擦除
parameter R_STA_REG =4'd5;//读寄存器//
//指令集
parameter WEL_CMD =8'h06;
parameter C_ERA_CMD =8'hc7;
parameter S_ERA_CMD =8'h20;//块擦除
parameter READ_CMD =8'h03;
parameter WRITE_CMD =8'h02;
parameter R_STA_REG_CMD=8'h05;//读状态寄存器
//`include "parameter.v"
wire[23:0] spi_addr ;
//reg define
reg [3:0] current_state ;
reg [3:0] next_state ;
reg [7:0 ] data_buffer ;
reg [7:0 ] cmd_buffer ;
reg [23:0] addr_buffer ;
reg [31:0] bit_cnt ;
reg dely_cnt ;
reg clk_cnt ;
reg [31:0] dely_state_cnt ;
reg stdone ;
reg [7:0] r_data ;
reg [7:0] spi_data ;
reg [7:0] spi_cmd ;
reg [3:0] change ;
reg spi_clk0 ;
reg [3:0] q ;
reg [3:0] x ;
reg stdone_buff ;
reg alldone ;
reg rst_n_buffer ;
///reg rst_n ;
assign spi_addr = 24'b00010 ;
initial begin
spi_data = 8'b10011100 ;
current_state = 1'b0 ;
data_buffer = 8'b0 ;
bit_cnt = 32'b0 ;///FFF*0.037*2=0.3ms FFFFFFFF*0.037*2=317.8s 他结束后会返回值吗
spi_cs = 1'b1 ;
addr_buffer = 8'd0 ;
dely_cnt = 8'b0 ;
clk_cnt = 8'b0 ;
dely_state_cnt = 32'b0 ;
cmd_buffer = 8'b0 ;
spi_mosi = 8'b0 ;
spi_clk = 8'b0 ;
spi_clk0 = 8'b0 ;
stdone = 1'b0 ;
change = 8'b0 ;
/// rst_n = 1'b1 ;
q = 1'b0 ;//debugger状态机具体步骤检测标志位
x = 1'b0 ;//debugger状态机检测标志位
alldone = 1'b0 ;//写数据执行完寻reg也结束后标志位
end
//always @(posedge clk_54m or negedge rst_n) begin
// if (!rst_n)
// spi_data <= 8'b10011100 ;
// else
// spi_data <= spi_data+1'b1 ;
//end
//定义一个计数器产生延时以匹配spi mode0模式
always @(posedge clk_54m or negedge rst_n )begin
if(!rst_n)
dely_cnt<=1'd0;
else if(spi_cs==0)begin///是不是可以用stdone_buff
if(dely_cnt<1)
dely_cnt<=dely_cnt+1'd1;
else
dely_cnt<=dely_cnt;
end
else
dely_cnt<=1'd0;
end
//定义一个状态来区分数据采样完/传输完 上升沿在采样,下降沿在输出,如果不定义这个直接采用spiclk的反向逻辑行吗?
//不行,spiclk需要bitcnt来开启,这里如果bitcnt再由spiclk会出现锁存情况
always @(posedge clk_54m or negedge rst_n )begin
if(!rst_n)
clk_cnt<=1'd0;
else if(dely_cnt==1)
clk_cnt<=clk_cnt+1'd1;
else
clk_cnt<=1'd0;
end
//定义一个计数器统计开始后的传输位数///有办法舍去clk_cnt吗
always @(posedge clk_54m or negedge rst_n )begin ///若dely==1
if(!rst_n)begin
bit_cnt<=8'b0;
end
else if(dely_cnt==1)begin
if(clk_cnt==1'b1)begin
bit_cnt<=bit_cnt+1'd1;
end
else begin
bit_cnt<=bit_cnt;
end
end
else begin
bit_cnt<=8'b0;
end
end
//定义一个计数器(满足状态切换间的时间延迟)
always @(posedge clk_54m or negedge rst_n )begin
if(!rst_n)
dely_state_cnt<=1'd0;
else if(spi_cs)begin
if(dely_state_cnt<400000000)
dely_state_cnt<=dely_state_cnt+1'd1;
else
dely_state_cnt<=dely_state_cnt;
end
else
dely_state_cnt<=1'd0;
end
//指令的移位变化
always @(posedge clk_54m or negedge rst_n) begin
if (!rst_n)
cmd_buffer <= 8'b0;
else if (stdone==0&&dely_cnt==0)//cs改用stdone,这里spi_cmd应会晚一个54M的clk赋值,cmd_buf落后两个///注意此处的时序问题
cmd_buffer <= spi_cmd;
else if (clk_cnt==1&&bit_cnt<8) begin///如果不管IDLE状态会怎么样//这里clk_cnt应该也可以换成spiclk的反逻辑
cmd_buffer <= {cmd_buffer[6:0],cmd_buffer[7]};
end
else cmd_buffer <= cmd_buffer;
end
//地址位的移位变化
always @(posedge clk_54m or negedge rst_n )begin
if(!rst_n)
addr_buffer<=8'd0;
else if(spi_cs==0&&dely_cnt==0)
addr_buffer<=spi_addr;
else if(clk_cnt==1&¤t_state==WRITE&&bit_cnt>=8&&bit_cnt<32)
addr_buffer<={addr_buffer[22:0],addr_buffer[23]};
else
addr_buffer<=addr_buffer;
end
//写数据移位寄存
always @(posedge clk_54m or negedge rst_n )begin
if(!rst_n)begin
data_buffer<=8'd0;
// spi_data <=8'b10011100;
end
else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)///因为是31结束,32开始给,所以要+1除8,bitcnt之所以不是大于32,是因为写数据请求发送后要占用一个时钟周期,而我们中间设定的缓冲区也要占用一个时钟
data_buffer<=spi_data;
else if(clk_cnt==1&¤t_state==WRITE&&bit_cnt>=32)
data_buffer<={data_buffer[6:0],data_buffer[7]};
else
data_buffer<=data_buffer;
end
//----------------------------------------------------------------------
//-- 状态机第1段
//----------------------------------------------------------------------
always @(posedge clk_54m or negedge rst_n)begin
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
//----------------------------------------------------------------------
//-- 状态机第2段//写nextstate根据change的变化
//----------------------------------------------------------------------
always @(*)begin
case(current_state)
IDLE: begin1011之后是WEL,然后WEL返回0000,0000之后是CERA,然后CERA返回0001,0001之后是REG,然后REG返回0010,0010之后是WEL,然后WEL返回0011,
0011之后是WRITE,然后WRITE返回0100,0100之后是REG,然后REG返回1011进行循环。
if(change==4'b1011||change==4'b0010)
next_state = WEL ;
else if(change==4'b0000)
next_state = C_ERA;
else if(change==4'b0001||change==4'b0100)
next_state = R_STA_REG;
else if(change==4'b0011)begin
next_state = WRITE;
// x=7;
end
else begin
next_state = IDLE;注意状态转化
// x=5;
end
end
WEL: begin
if(change==4'b1011||change==4'b0010)begin//1011
next_state = WEL;
// x=1;
end
else begin
next_state = IDLE;
// x=2;
end
end
C_ERA: begin
if(change==4'b0000)begin
next_state = C_ERA;
// x=3;
end
else begin
next_state = IDLE;
// x=4;
end
end
R_STA_REG: begin
if(change==4'b0001||change==4'b0100)begin
next_state = R_STA_REG;
end
else begin
next_state = IDLE;
end
end
WRITE: begin
if(change==4'b0011)
next_state = WRITE;
else
next_state = IDLE;
end
default:begin
next_state = IDLE;
// x=15;
end
endcase
end
//----------------------------------------------------------------------
//-- 状态机第3段 写change的变化以及具体执行功能
//----------------------------------------------------------------------
always @(posedge clk_54m or negedge rst_n)begin
if(!rst_n)begin
change <= 4'b1011 ;
spi_clk <= 1'b0 ;
spi_clk0 <= 1'b0 ;
spi_cs <= 1'b1 ;
spi_mosi <= 1'b0 ;
alldone <= 1'b0 ;
end
///可设计当bitcnt>某值时,给rst一个复位信号
else begin
case(current_state)
IDLE: begin
spi_cs <=1'b1 ;
spi_clk <=1'b0 ;
spi_clk0 <=1'b0 ;
spi_mosi <=1'b0 ;
alldone <=1'b0 ;
change <= change;
stdone <= stdone;
q <=1'b0 ;
end
WEL: begin
spi_cmd<=WEL_CMD;
stdone<=1'b0;
spi_cs<=1'b0;
if (dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
// q<=2;
end
else if(bit_cnt==8&&clk_cnt==0)begin
stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
// q<=3;
end
else if(bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
stdone<=1'b1;
// q<=4;
end
else if(dely_cnt==1&&stdone&&change==4'b1011)begin
change <= 4'b0000;//进入空闲,空闲后是擦除(0000),
end
else if(dely_cnt==1&&stdone&&change==4'b0010)begin
change <= 4'b0011;//进入空闲,空闲后写数据(0011)
end
/// else begin change <= change;
/// stdone <= stdone;
/// end
end
C_ERA: begin
spi_cmd<=C_ERA_CMD;
stdone<=1'b0;
spi_clk <=1'b0 ;
spi_clk0 <=1'b0 ;
if(dely_state_cnt==10)begin
spi_cs<=1'b0;
// q<=1;
end
else if (dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
// q<=2;
end
else if(bit_cnt==8&&clk_cnt==0)begin
// stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
// q<=3;
end
else if (bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
stdone<=1'b1;
// q<=4;
end
else if(stdone&&change==4'b0000)begin//之前是空闲,空闲前是写使能(0000)
change<=4'b0001;//进入空闲,空闲后寻reg(0001)
x<=3;
end
end
R_STA_REG: begin
spi_cmd<=R_STA_REG_CMD;
stdone<=1'b0;
spi_clk <=1'b0 ;
spi_clk0 <=1'b0 ;
/// q<=6;
if(dely_state_cnt==10)begin
spi_cs<=1'b0;
/// q<=3;
end
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
/// q<=7;
end
else if(bit_cnt==8)begin //发送完instruction
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=1'b0;
/// q<=8;
end
else if(~spi_miso&&bit_cnt%8==0&&clk_cnt==0)begin//这里应该是-1之后%8但是因为只有一次,所以直接给%9
spi_clk<=1'b0;
spi_cs<=1'b1;
stdone<=1'b1;
end
else if(~spi_cs&&dely_cnt)begin //else if(~spi_cs&&==1)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
end
else if(stdone&&change==4'b0001)begin//之前是空闲,空闲前是片擦除
change<=4'b0010;
/// q<=11;
end
else if(stdone&&change==4'b0100)begin//之前是空闲,空闲前是写数据
change<=4'b1110;//进入长期空闲//这里如果需要循环的化可以接如初始状态1011
alldone<=1;
q<=13;
end
/// else begin change <= change;
/// stdone <= stdone;
/// q<=15;
/// end
end
WRITE: begin
spi_cmd<=WRITE_CMD;
stdone<=1'b0;
q<=1;
spi_clk <=1'b0 ;
spi_clk0 <=1'b0 ;
if(dely_state_cnt==10)begin
spi_cs<=1'b0;
// q<=7;
end
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt>=32&&bit_cnt<2080)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=data_buffer[7];
end
else if(bit_cnt==2080&&clk_cnt==0) begin
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
else if(bit_cnt==2080&&clk_cnt==1) begin
spi_cs<=1'b1;
stdone<=1'b1;
// q<=5;
end
else if(stdone&&change==4'b0011)begin//之前是空闲,空闲前是写使能 ///第十步
change<=4'b0100;//进入空闲,空闲后是询reg
x<=1;
end
/// else begin change <= change;
/// stdone <= stdone;
/// end
end
default:begin
stdone<=1'b0;
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_clk0<=1'b0;
spi_mosi<=1'b0;
change <= 15;
q<=0;
end
endcase
end
end
endmodule