已经在板卡上验证功能ok,文末附代码。
手册要求的时序
CMD时序
FAST Readx1
PPx1
SE
FAST Read x2
PPx4
FAST Readx4
ID
实现思路
参考网络模型分层思想,将每一步操作都分解为1B的读或者写操作。
spi有三种模式x1x2x4,每一种模式用一个单独的模块来实现。每个模块对上层的接口是LocalBus接口,上层从下面的三鹿LocalBus中选一路有效。
下面状态机实现读1B操作(写操作类似),对上层的接口是LocalBus
IDLE:空闲状态
RD:判断是读操作还写操作。
RD_CLK_NOVALID:spi_clk无效,为什么要先无效?因为为了保证后面产生确定的有效沿。如果空闲时有效电平,那么后面无法产生有效沿,如果空闲时无效,那么可以不用此状态。
RD_WAIT_CLK_NOVAlID:无效沿延时状态,按拍数计数
RD_CLK_VALID:时钟沿有效
RD_WAIT_CLK_VAlID:有效沿延时状态,按拍数计数
WAIT:等一拍
GEN_ACK:产生ack信号
顶层汇聚三个模块,对命令进行译码成对应的操作路径,并且将底层上送的localbus总线转换成内部管理口时序(类似于XADC 的DRP时序)。
ILA波形
代码
spi_x1
// spix1
`timescale 1ns/100ps
`define SIM_OPEN 1
//1.cs不能在此模块控制,因为并不是每8bit,cs都会变化。cs只在命令的开始和结束时变化。
//2.所有的CLK最终在上升沿结束,如果需要改为下降沿,则需要让CLK终结在下降沿
module spix1(
input clk ,
input rst ,
input [3:0]spi_di ,// input
output [3:0]spi_do ,// output
output spi_clk ,// output
input SB_req ,// input
input SB_wr ,// input 1:wr 0:rd
input [7:0] SB_wdata ,// input
output [7:0] SB_rdata ,// output
output SB_ack // output
);
//定义设计中使用到的参数
reg [3:0] state_cnt =4'h0;//某个状态的第几拍。从1开始
reg [3:0] bit_cnt =4'h0;//第几个bit。从0开始
wire no_wait ;//1:clk信号不用延时
wire is_rxtx_end ;//已经收到了7bit,只要再执行一次RD_CLK_VALID就可
parameter DLY = 1;
parameter CLK_VALIDWAIT_CNT =4'h3;//时钟有效的拍数>0;//时钟的高电平和低电平应该相等。
//产生1个上升沿的必要条件:先为0再为1,先NOVAlID再VAlID
parameter IDLE = "IDLE" ,//下一拍CS&CLK处于初始值
WAIT = "WAIT" ,//
GEN_ACK = "GEN_ACK" ,//
RD = "RD" ,//下一拍CS&CLK处于初始值
RD_CLK_VALID = "RD_CLK_VALID" ,//下一拍产生CLK上升沿
RD_WAIT_CLK_VAlID = "RD_WAIT_CLK_VAlID" ,//下一拍CLK保持高
RD_CLK_NOVALID = "RD_CLK_NOVALID" ,//下一拍产生CLK下降沿
RD_WAIT_CLK_NOVAlID = "RD_WAIT_CLK_NOVAlID" ,//下一拍CLK保持低
WR = "WR" ,//下一拍CS&CLK处于初始值
WR_CLK_VALID = "WR_CLK_VALID" ,//下一拍产生CLK上升沿
WR_WAIT_CLK_VAlID = "WR_WAIT_CLK_VAlID" ,//下一拍CLK保持高
WR_CLK_NOVALID = "WR_CLK_NOVALID" ,//下一拍产生CLK下降沿
WR_WAIT_CLK_NOVAlID = "WR_WAIT_CLK_NOVAlID" ;//下一拍CLK保持低
//状态寄存器定义
reg[255:0] cur_state,next_state,cur_state_1d;
wire state_change;//进入新的状态,第一拍拉高,表示新状态的开始
assign state_change = (cur_state==cur_state_1d)?1'b0:1'b1;
//第一段状态转移
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
cur_state <= #DLY IDLE;
cur_state_1d <= #DLY IDLE;
end else begin
cur_state <= #DLY next_state;
cur_state_1d <= #DLY cur_state;
end
end
//第二段产生下一状态
always@(*)begin
case(cur_state)
IDLE:
if(SB_req&(~SB_wr))
next_state = RD;
else if(SB_req&(SB_wr))
next_state = WR;
else
next_state = IDLE;
RD:
next_state = RD_CLK_NOVALID;
RD_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = RD_CLK_VALID;
else //用延时
next_state = RD_WAIT_CLK_NOVAlID;
RD_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = RD_CLK_VALID;
else
next_state = RD_WAIT_CLK_NOVAlID;
RD_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = RD_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = RD_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
RD_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else
next_state = RD_WAIT_CLK_VAlID;
WAIT:
next_state = GEN_ACK;
GEN_ACK:
next_state = IDLE;
WR: next_state = WR_CLK_NOVALID;
WR_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = WR_CLK_VALID;
else //用延时
next_state = WR_WAIT_CLK_NOVAlID;
WR_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = WR_CLK_VALID;
else
next_state = WR_WAIT_CLK_NOVAlID;
WR_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = WR_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
WR_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else
next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
state_cnt <= #DLY 4'h0;
end else if(cur_state==IDLE) begin
state_cnt <= #DLY 4'h1;
end else if(state_change) begin
state_cnt <= #DLY 4'h2;
end else begin
state_cnt <= #DLY state_cnt+1'b1;
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
bit_cnt <= #DLY 4'h0;
end else if((cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID) ) begin
bit_cnt <= #DLY bit_cnt+1'b1;
end else if(cur_state==IDLE) begin
bit_cnt <= #DLY 4'h0;
end
end
reg spi_clk_inter=1;//在IDLE需要确定clk=0。
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
spi_clk_inter <= #DLY 1'h1;
end else if( (cur_state==RD_CLK_NOVALID)|(cur_state==WR_CLK_NOVALID) ) begin
spi_clk_inter <= #DLY 1'b0;
end else if( (cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID)) begin
spi_clk_inter <= #DLY 3'h1;
end
end
assign no_wait = ( (CLK_VALIDWAIT_CNT==4'h1)|(CLK_VALIDWAIT_CNT==4'h0) ) ? 1'b1:1'b0;
assign is_rxtx_end = (bit_cnt==4'h7) ? 1'b1:1'b0;//收发结束
//产生localbus响应
// W25Q128JV flash 是下发上采,FPGA则需要反过来上采下发。
reg [7:0] rdata;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
rdata <= #DLY 8'h0;
end else if( (cur_state==RD_CLK_VALID) ) begin
rdata <= #DLY {rdata[6:0],spi_di[1]} ;
end
end
reg ack;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
ack <= #DLY 1'h0;
end else if( (cur_state==WAIT)&(~ack) ) begin
ack <= #DLY 1'b1 ;
end else begin
ack <= #DLY 1'b0 ;
end
end
reg[8:0] data_out;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
data_out <= #DLY 8'h0;
end else if( (cur_state==WR) ) begin
data_out <= #DLY {data_out[8],SB_wdata} ;
end else if( (cur_state==WR_CLK_NOVALID) ) begin
data_out <= #DLY {data_out[7:0],1'b1} ;
end
end
assign SB_rdata = rdata;
assign SB_ack = ack;
assign spi_do = {3'b0,data_out[8]};
assign spi_clk = spi_clk_inter;
endmodule
// spix1 u_spix1(
// .clk ( ),//input clk
// .rst ( ),//input rst
// .spi_di ( ),//input spi_di
// .spi_do ( ),//output spi_do
// .SB_req ( ),//input SB_req
// .SB_wr ( ),//input SB_wr
// .SB_wdata ( ),//input [7:0] SB_wdata
// .SB_rdata ( ),//output [7:0] SB_rdata
// .SB_ack ( ),//output SB_ack
// .is_input ( ) //output [3:0] is_input
// );
spix2
`timescale 1ns/100ps
`define SIM_OPEN 1
//1.cs不能在此模块控制,因为并不是每8bit,cs都会变化。cs只在命令的开始和结束时变化。
//2.所有的CLK最终在上升沿结束,如果需要改为下降沿,则需要让CLK终结在下降沿
module spix2(
input clk ,
input rst ,
input [3:0] spi_di ,// input
output[3:0] spi_do ,// output
output spi_clk ,// output
input SB_req ,// input
input SB_wr ,// input 1:wr 0:rd
input [7:0] SB_wdata ,// input
output [7:0] SB_rdata ,// output
output SB_ack // output
);
//定义设计中使用到的参数
reg [3:0] state_cnt =4'h0;//某个状态的第几拍。从1开始
reg [3:0] bit_cnt =4'h0;//第几个bit。从0开始
wire no_wait ;//1:clk信号不用延时
wire is_rxtx_end ;//已经收到了7bit,只要再执行一次RD_CLK_VALID就可
parameter DLY = 1;
parameter CLK_VALIDWAIT_CNT =4'h3;//时钟有效的拍数>0;//时钟的高电平和低电平应该相等。
//产生1个上升沿的必要条件:先为0再为1,先NOVAlID再VAlID
parameter IDLE = "IDLE" ,//下一拍CS&CLK处于初始值
WAIT = "WAIT" ,//
GEN_ACK = "GEN_ACK" ,//
RD = "RD" ,//下一拍CS&CLK处于初始值
RD_CLK_VALID = "RD_CLK_VALID" ,//下一拍产生CLK上升沿
RD_WAIT_CLK_VAlID = "RD_WAIT_CLK_VAlID" ,//下一拍CLK保持高
RD_CLK_NOVALID = "RD_CLK_NOVALID" ,//下一拍产生CLK下降沿
RD_WAIT_CLK_NOVAlID = "RD_WAIT_CLK_NOVAlID" ,//下一拍CLK保持低
WR = "WR" ,//下一拍CS&CLK处于初始值
WR_CLK_VALID = "WR_CLK_VALID" ,//下一拍产生CLK上升沿
WR_WAIT_CLK_VAlID = "WR_WAIT_CLK_VAlID" ,//下一拍CLK保持高
WR_CLK_NOVALID = "WR_CLK_NOVALID" ,//下一拍产生CLK下降沿
WR_WAIT_CLK_NOVAlID = "WR_WAIT_CLK_NOVAlID" ;//下一拍CLK保持低
//状态寄存器定义
reg[255:0] cur_state,next_state,cur_state_1d;
wire state_change;//进入新的状态,第一拍拉高,表示新状态的开始
assign state_change = (cur_state==cur_state_1d)?1'b0:1'b1;
//第一段状态转移
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
cur_state <= #DLY IDLE;
cur_state_1d <= #DLY IDLE;
end else begin
cur_state <= #DLY next_state;
cur_state_1d <= #DLY cur_state;
end
end
//第二段产生下一状态
always@(*)begin
case(cur_state)
IDLE:
if(SB_req&(~SB_wr))
next_state = RD;
else if(SB_req&(SB_wr))
next_state = WR;
else
next_state = IDLE;
RD:
next_state = RD_CLK_NOVALID;
RD_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = RD_CLK_VALID;
else //用延时
next_state = RD_WAIT_CLK_NOVAlID;
RD_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = RD_CLK_VALID;
else
next_state = RD_WAIT_CLK_NOVAlID;
RD_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = RD_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = RD_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
RD_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else
next_state = RD_WAIT_CLK_VAlID;
WAIT:
next_state = GEN_ACK;
GEN_ACK:
next_state = IDLE;
WR: next_state = WR_CLK_NOVALID;
WR_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = WR_CLK_VALID;
else //用延时
next_state = WR_WAIT_CLK_NOVAlID;
WR_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = WR_CLK_VALID;
else
next_state = WR_WAIT_CLK_NOVAlID;
WR_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = WR_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
WR_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else
next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
state_cnt <= #DLY 4'h0;
end else if(cur_state==IDLE) begin
state_cnt <= #DLY 4'h1;
end else if(state_change) begin
state_cnt <= #DLY 4'h2;
end else begin
state_cnt <= #DLY state_cnt+1'b1;
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
bit_cnt <= #DLY 4'h0;
end else if((cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID) ) begin
bit_cnt <= #DLY bit_cnt+2'h2;
end else if(cur_state==IDLE) begin
bit_cnt <= #DLY 4'h0;
end
end
reg spi_clk_inter=1;//在IDLE需要确定clk=0。
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
spi_clk_inter <= #DLY 1'h1;
end else if( (cur_state==RD_CLK_NOVALID)|(cur_state==WR_CLK_NOVALID) ) begin
spi_clk_inter <= #DLY 1'b0;
end else if( (cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID)) begin
spi_clk_inter <= #DLY 3'h1;
end
end
assign no_wait = ( (CLK_VALIDWAIT_CNT==4'h1)|(CLK_VALIDWAIT_CNT==4'h0) ) ? 1'b1:1'b0;
assign is_rxtx_end = (bit_cnt==4'h6) ? 1'b1:1'b0;//收发结束
//产生localbus响应
// W25Q128JV flash 是下发上采,FPGA则需要反过来上采下发。
reg [7:0] rdata;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
rdata <= #DLY 8'h0;
end else if( (cur_state==RD_CLK_VALID) ) begin
rdata <= #DLY {rdata[6:0],spi_di[1:0]} ;
end
end
reg ack;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
ack <= #DLY 1'h0;
end else if( (cur_state==WAIT)&(~ack) ) begin
ack <= #DLY 1'b1 ;
end else begin
ack <= #DLY 1'b0 ;
end
end
reg[9:0] data_out;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
data_out <= #DLY 10'h0;
end else if( (cur_state==WR) ) begin
data_out <= #DLY {data_out[9:8],SB_wdata} ;
end else if( (cur_state==WR_CLK_NOVALID) ) begin
data_out <= #DLY {data_out[7:0],2'b00} ;
end
end
assign SB_rdata = rdata;
assign SB_ack = ack;
assign spi_do = {2'b0,data_out[9:8]};
assign spi_clk = spi_clk_inter;
endmodule
// spix2 u_spix2(
// .clk ( ),//input clk
// .rst ( ),//input rst
// .spi_di ( ),//input spi_di
// .spi_do ( ),//output spi_do
// .spi_clk ( ),//output spi_clk
// .SB_req ( ),//input SB_req
// .SB_wr ( ),//input SB_wr
// .SB_wdata ( ),//input [7:0] SB_wdata
// .SB_rdata ( ),//output [7:0] SB_rdata
// .SB_ack ( ),//output SB_ack
// .is_input ( ) //output [3:0] is_input
// );
spix4
`timescale 1ns/100ps
`define SIM_OPEN 1
//1.cs不能在此模块控制,因为并不是每8bit,cs都会变化。cs只在命令的开始和结束时变化。
//2.所有的CLK最终在上升沿结束,如果需要改为下降沿,则需要让CLK终结在下降沿
module spix4(
input clk ,
input rst ,
input [3:0] spi_di ,// input
output[3:0] spi_do ,// output
output spi_clk ,// output
input SB_req ,// input
input SB_wr ,// input 1:wr 0:rd
input [7:0] SB_wdata ,// input
output [7:0] SB_rdata ,// output
output SB_ack // output
);
//定义设计中使用到的参数
reg [3:0] state_cnt =4'h0;//某个状态的第几拍。从1开始
reg [3:0] bit_cnt =4'h0;//第几个bit。从0开始
wire no_wait ;//1:clk信号不用延时
wire is_rxtx_end ;//已经收到了7bit,只要再执行一次RD_CLK_VALID就可
parameter DLY = 1;
parameter CLK_VALIDWAIT_CNT =4'h3;//时钟有效的拍数>0;//时钟的高电平和低电平应该相等。
//产生1个上升沿的必要条件:先为0再为1,先NOVAlID再VAlID
parameter IDLE = "IDLE" ,//下一拍CS&CLK处于初始值
WAIT = "WAIT" ,//
GEN_ACK = "GEN_ACK" ,//
RD = "RD" ,//下一拍CS&CLK处于初始值
RD_CLK_VALID = "RD_CLK_VALID" ,//下一拍产生CLK上升沿
RD_WAIT_CLK_VAlID = "RD_WAIT_CLK_VAlID" ,//下一拍CLK保持高
RD_CLK_NOVALID = "RD_CLK_NOVALID" ,//下一拍产生CLK下降沿
RD_WAIT_CLK_NOVAlID = "RD_WAIT_CLK_NOVAlID" ,//下一拍CLK保持低
WR = "WR" ,//下一拍CS&CLK处于初始值
WR_CLK_VALID = "WR_CLK_VALID" ,//下一拍产生CLK上升沿
WR_WAIT_CLK_VAlID = "WR_WAIT_CLK_VAlID" ,//下一拍CLK保持高
WR_CLK_NOVALID = "WR_CLK_NOVALID" ,//下一拍产生CLK下降沿
WR_WAIT_CLK_NOVAlID = "WR_WAIT_CLK_NOVAlID" ;//下一拍CLK保持低
//状态寄存器定义
reg[255:0] cur_state,next_state,cur_state_1d;
wire state_change;//进入新的状态,第一拍拉高,表示新状态的开始
assign state_change = (cur_state==cur_state_1d)?1'b0:1'b1;
//第一段状态转移
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
cur_state <= #DLY IDLE;
cur_state_1d <= #DLY IDLE;
end else begin
cur_state <= #DLY next_state;
cur_state_1d <= #DLY cur_state;
end
end
//第二段产生下一状态
always@(*)begin
case(cur_state)
IDLE:
if(SB_req&(~SB_wr))
next_state = RD;
else if(SB_req&(SB_wr))
next_state = WR;
else
next_state = IDLE;
RD:
next_state = RD_CLK_NOVALID;
RD_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = RD_CLK_VALID;
else //用延时
next_state = RD_WAIT_CLK_NOVAlID;
RD_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = RD_CLK_VALID;
else
next_state = RD_WAIT_CLK_NOVAlID;
RD_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = RD_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = RD_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
RD_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = RD_CLK_NOVALID;
else
next_state = RD_WAIT_CLK_VAlID;
WAIT:
next_state = GEN_ACK;
GEN_ACK:
next_state = IDLE;
WR: next_state = WR_CLK_NOVALID;
WR_CLK_NOVALID://clk=0
if(no_wait) //不用延时
next_state = WR_CLK_VALID;
else //用延时
next_state = WR_WAIT_CLK_NOVAlID;
WR_WAIT_CLK_NOVAlID://延时
if(state_cnt==CLK_VALIDWAIT_CNT) //
next_state = WR_CLK_VALID;
else
next_state = WR_WAIT_CLK_NOVAlID;
WR_CLK_VALID:
case({no_wait,is_rxtx_end})//{不要延时,收发结束}
2'b11: next_state = WAIT;
2'b10: next_state = WR_CLK_NOVALID;
2'b01: next_state = WAIT;
2'b00: next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
WR_WAIT_CLK_VAlID:
if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else if((state_cnt==CLK_VALIDWAIT_CNT) )
next_state = WR_CLK_NOVALID;
else
next_state = WR_WAIT_CLK_VAlID;
default: next_state = IDLE;
endcase
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
state_cnt <= #DLY 4'h0;
end else if(cur_state==IDLE) begin
state_cnt <= #DLY 4'h1;
end else if(state_change) begin
state_cnt <= #DLY 4'h2;
end else begin
state_cnt <= #DLY state_cnt+1'b1;
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
bit_cnt <= #DLY 4'h0;
end else if((cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID) ) begin
bit_cnt <= #DLY bit_cnt+3'h4;
end else if(cur_state==IDLE) begin
bit_cnt <= #DLY 4'h0;
end
end
reg spi_clk_inter=1;//在IDLE需要确定clk=0。
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
spi_clk_inter <= #DLY 1'h1;
end else if( (cur_state==RD_CLK_NOVALID)|(cur_state==WR_CLK_NOVALID) ) begin
spi_clk_inter <= #DLY 1'b0;
end else if( (cur_state==RD_CLK_VALID)|(cur_state==WR_CLK_VALID)) begin
spi_clk_inter <= #DLY 3'h1;
end
end
assign no_wait = ( (CLK_VALIDWAIT_CNT==4'h1)|(CLK_VALIDWAIT_CNT==4'h0) ) ? 1'b1:1'b0;
assign is_rxtx_end = (bit_cnt==4'h4) ? 1'b1:1'b0;//收发结束
//产生localbus响应
// W25Q128JV flash 是下发上采,FPGA则需要反过来上采下发。
reg [7:0] rdata;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
rdata <= #DLY 8'h0;
end else if( (cur_state==RD_CLK_VALID) ) begin
rdata <= #DLY {rdata[3:0],spi_di[3:0]} ;
end
end
reg ack;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
ack <= #DLY 1'h0;
end else if( (cur_state==WAIT)&(~ack) ) begin
ack <= #DLY 1'b1 ;
end else begin
ack <= #DLY 1'b0 ;
end
end
reg[11:0] data_out;
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
data_out <= #DLY 12'h0;
end else if( (cur_state==WR) ) begin
data_out <= #DLY {data_out[11:8],SB_wdata} ;
end else if( (cur_state==WR_CLK_NOVALID) ) begin
data_out <= #DLY {data_out[7:0],4'b0000} ;
end
end
assign SB_rdata = rdata;
assign SB_ack = ack;
assign spi_do = data_out[11:8];
assign spi_clk = spi_clk_inter;
endmodule
// spix2 u_spix2(
// .clk ( ),//input clk
// .rst ( ),//input rst
// .spi_di ( ),//input spi_di
// .spi_do ( ),//output spi_do
// .spi_clk ( ),//output spi_clk
// .SB_req ( ),//input SB_req
// .SB_wr ( ),//input SB_wr
// .SB_wdata ( ),//input [7:0] SB_wdata
// .SB_rdata ( ),//output [7:0] SB_rdata
// .SB_ack ( ),//output SB_ack
// .is_input ( ) //output [3:0] is_input
// );
spi_top
`timescale 1ns/100ps
`define SIM_OPEN 1
//1.cs不能在此模块控制,因为并不是每8bit,cs都会变化。cs只在命令的开始和结束时变化。
//2.所有的CLK最终在上升沿结束,如果需要改为下降沿,则需要让CLK终结在下降沿
module spi_top(
input clk ,
input rst ,
input [3:0] spi_di ,// input
output[3:0] spi_do ,// output
output[3:0] spi_is_out ,// output
output spi_clk ,// output
output spi_csn ,// output
//内部管理口
input m_cs ,
input m_wr ,
input [2:0] m_addr ,
input [7:0] m_din ,
output reg [7:0] m_dout
);
reg [7:0] tx_data0 ;//待发送的数据寄存器
reg [7:0] rx_data0 ;//已接收的数据寄存器
reg [7:0] cmd ;//命令寄存器
reg [7:0] state ;//状态寄存器
reg [7:0] add_h ;//地址高寄存器
reg [7:0] add_m ;//地址中寄存器
reg [7:0] add_l ;//地址低寄存器
//检测到操作
wire rd_data ;
wire wr_data ;
reg wr_data_1d;
wire rd_stata ;
wire wr_cmd ;
wire rd_add_h ;
wire rd_add_m ;
wire rd_add_l ;
wire wr_add_h ;
wire wr_add_m ;
wire wr_add_l ;
//判断是什么操作
assign rd_data = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b0,3'd0})?1'b1 :1'b0 ;
assign wr_data = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b1,3'd0})?1'b1 :1'b0 ;
assign rd_stata = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b0,3'd1})?1'b1 :1'b0 ;
assign wr_cmd = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b1,3'd1})?1'b1 :1'b0 ;
assign rd_add_h = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b0,3'd2})?1'b1 :1'b0 ;
assign rd_add_m = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b0,3'd3})?1'b1 :1'b0 ;
assign rd_add_l = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b0,3'd4})?1'b1 :1'b0 ;
assign wr_add_h = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b1,3'd2})?1'b1 :1'b0 ;
assign wr_add_m = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b1,3'd3})?1'b1 :1'b0 ;
assign wr_add_l = ({m_cs,m_wr,m_addr[2:0]} == {1'b1,1'b1,3'd4})?1'b1 :1'b0 ;
reg [3:0] spi_is_out_inter;//io方向
reg spi_csn_inter;//
assign spi_is_out = spi_is_out_inter;
assign spi_csn = spi_csn_inter;
parameter x1 = 2'b01;
parameter x2 = 2'b10;
parameter x4 = 2'b11;
parameter x0 = 2'b00;
reg [3:0] state_cnt = 4'h0;//某个状态的第几拍。从1开始
reg [1:0] mode_sel = 2'b0;//00:都不选;01:x1; 10:x2;11:x4
wire [3:0] x1_di ,x2_di ,x4_di ,ctrl_di ;
wire [3:0] x1_do ,x2_do ,x4_do ;
wire x1_clk ,x2_clk ,x4_clk ;
wire x1_req ,x2_req ,x4_req ;//ctrl_req是3种当中,选的一种
wire x1_wr ,x2_wr ,x4_wr ;//req不能随便产生
wire [7:0] x1_wdata ,x2_wdata ,x4_wdata ;
wire [7:0] x1_rdata ,x2_rdata ,x4_rdata ;
wire x1_ack ,x2_ack ,x4_ack ;
reg [7:0] ctrl_rdata;
reg ctrl_req,ctrl_ack,ctrl_clk,ctrl_wr;
reg [3:0] ctrl_do;
reg [7:0] ctrl_wdata;
assign ctrl_di = spi_di;
assign spi_do = ctrl_do;
assign spi_clk = ctrl_clk;
always@(*)begin//spi模块的输入不管,只管模块的输出
case(mode_sel)
x0 :begin ctrl_rdata = 8'b0 ; ctrl_ack= 1'b0 ;ctrl_do = 4'b0 ;ctrl_clk= 1'b1 ; end
x1 :begin ctrl_rdata = x1_rdata; ctrl_ack= x1_ack;ctrl_do = x1_do;ctrl_clk= x1_clk; end
x2 :begin ctrl_rdata = x2_rdata; ctrl_ack= x2_ack;ctrl_do = x2_do;ctrl_clk= x2_clk; end
x4 :begin ctrl_rdata = x4_rdata; ctrl_ack= x4_ack;ctrl_do = x4_do;ctrl_clk= x4_clk; end
default:begin ctrl_rdata = 8'b0 ; ctrl_ack= 1'b0 ;ctrl_do = 4'b0 ;ctrl_clk= 1'b0 ; end
endcase
end
assign {x1_di ,x2_di ,x4_di } = {3{ctrl_di}};
assign {x1_wr ,x2_wr ,x4_wr } = {3{ctrl_wr}};
assign {x1_wdata ,x2_wdata ,x4_wdata } = {3{ctrl_wdata}};
assign x1_req = (mode_sel==x1) ? ctrl_req : 1'b0;
assign x2_req = (mode_sel==x2) ? ctrl_req : 1'b0;
assign x4_req = (mode_sel==x4) ? ctrl_req : 1'b0;
//需要支持的命令
parameter NOP = 8'hff;
parameter WREN = 8'h06; //CMD
parameter F_RDx1 = 8'h0B; //CMD+ADDR+dummy+rdata+...
parameter F_RDx2 = 8'h3B; //CMD+ADDR+dummy+rdata_x2+...
parameter F_RDx4 = 8'h6B; //CMD+ADDR+dummy+rdata_x4+...
parameter PPx1 = 8'h02; //CMD+ADDR+wdata_x1+...
parameter PPx4 = 8'h32; //CMD+ADDR+wdata_x4+...
parameter SE = 8'hD8; //CMD+ADDR
parameter RDID = 8'h9F; //CMD+rdata_x1+...
parameter DLY = 1;
`ifdef SIM_OPEN
parameter IDLE = "IDLE ";
parameter CMD = "CMD ";
parameter ADDR_H = "ADDR_H ";
parameter ADDR_M = "ADDR_M ";
parameter ADDR_L = "ADDR_L ";
parameter DUMMY = "DUMMY ";
parameter TX_DATAX1 = "TX_DATAX1";
parameter TX_DATAX4 = "TX_DATAX4";
parameter TX_WAIT = "TX_WAIT ";
parameter RX_WAIT = "RX_WAIT ";
parameter RX_DATAX4 = "RX_DATAX4";
parameter RX_DATAX2 = "RX_DATAX2";
parameter RX_DATAX1 = "RX_DATAX1";
parameter CLR_CMD = "CLR_CMD ";
reg[127:0] cur_state,next_state,cur_state_1d;
`else
parameter IDLE = 4'd0;
parameter CMD = 4'd1;
parameter ADDR_H = 4'd2;
parameter ADDR_M = 4'd3;
parameter ADDR_L = 4'd4;
parameter DUMMY = 4'd5;
parameter TX_DATAX1 = 4'd6;
parameter TX_DATAX4 = 4'd7;
parameter TX_WAIT = 4'd8;
parameter RX_WAIT = 4'd9;
parameter RX_DATAX4 = 4'd10;
parameter RX_DATAX2 = 4'd11;
parameter RX_DATAX1 = 4'd12;
parameter CLR_CMD = 4'd13;
reg[3:0] cur_state,next_state,cur_state_1d;
`endif
wire state_change;
assign state_change = cur_state_1d != cur_state;
always @(posedge clk) cur_state_1d <= #DLY cur_state;
//第一段状态转移
always@(posedge clk or negedge rst)begin
if(rst == 1'b1)begin
cur_state <= #DLY IDLE;
end else begin
cur_state <= #DLY next_state;
end
end
//第二段产生下一状态 数据流检测序列状态变化
always@(*)begin
case(cur_state)
IDLE:
if(cmd==NOP)
next_state =IDLE;
else
next_state =CMD;
CMD:
if(ctrl_ack)
case(cmd)
F_RDx1,F_RDx2,F_RDx4,PPx1,PPx4,SE: next_state =ADDR_H;
WREN :next_state =CLR_CMD;
RDID :next_state =RX_DATAX1;
default:next_state =IDLE;
endcase
else
next_state = CMD;
ADDR_H:
if(ctrl_ack)
next_state = ADDR_M;
else
next_state = ADDR_H;
ADDR_M:
if(ctrl_ack)
next_state = ADDR_L;
else
next_state = ADDR_M;
ADDR_L:
if(ctrl_ack)
case(cmd)
F_RDx1,F_RDx2,F_RDx4 :next_state =DUMMY;
PPx1 :next_state =TX_DATAX1;
PPx4 :next_state =TX_DATAX4;
SE :next_state =CLR_CMD;
default:next_state =IDLE;
endcase
else
next_state = ADDR_L;
DUMMY:
if(ctrl_ack)
case(cmd)
F_RDx1 :next_state =RX_DATAX1;
F_RDx2 :next_state =RX_DATAX2;
F_RDx4 :next_state =RX_DATAX4;
default:next_state =IDLE;
endcase
else
next_state = DUMMY;
RX_DATAX4:
if(ctrl_ack)
next_state = RX_WAIT;
else
next_state = RX_DATAX4;
RX_DATAX2:
if(ctrl_ack)
next_state = RX_WAIT;
else
next_state = RX_DATAX2;
RX_DATAX1:
if(ctrl_ack)
next_state = RX_WAIT;
else
next_state = RX_DATAX1;
RX_WAIT:
if(rd_data)
case(cmd)
RDID,F_RDx1 :next_state =RX_DATAX1;
F_RDx2 :next_state =RX_DATAX2;
F_RDx4 :next_state =RX_DATAX4;
default:next_state =IDLE;
endcase
else if(cmd==NOP)
next_state = CLR_CMD;
else
next_state = RX_WAIT;
TX_DATAX1:
if(ctrl_ack)
next_state = TX_WAIT;
else
next_state = TX_DATAX1;
TX_DATAX4:
if(ctrl_ack)
next_state = TX_WAIT;
else
next_state = TX_DATAX4;
TX_WAIT:
if(wr_data)
case(cmd)
PPx1 :next_state =TX_DATAX1;
PPx4 :next_state =TX_DATAX4;
default:next_state =IDLE;
endcase
else if(cmd==NOP)
next_state = CLR_CMD;
else
next_state = TX_WAIT;
CLR_CMD:
next_state = IDLE;
default:
next_state = IDLE;
endcase
end
//每个状态内的拍数计数
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
state_cnt <= #DLY 4'h0;
end else if(cur_state==IDLE) begin
state_cnt <= #DLY 4'h1;
end else if(state_change) begin
state_cnt <= #DLY 4'h2;
end else if(state_cnt==4'hF) begin
state_cnt <= #DLY 4'hF;
end else begin
state_cnt <= #DLY state_cnt+1'b1;
end
end
//总线x1x2x4选择的产生,在进入状态的第二拍生效
// always@(posedge clk or posedge rst)begin
always@(*)begin
// if(rst == 1'b1)begin
// mode_sel <= #DLY x0;
// end else if((cur_state==RX_DATAX4) | (cur_state==TX_DATAX4) ) begin//x4模式
if((cur_state==RX_DATAX4) | (cur_state==TX_DATAX4) ) begin//x4模式
mode_sel <= #DLY x4;
end else if(cur_state==RX_DATAX2) begin//x2模式
mode_sel <= #DLY x2;
end else begin
mode_sel <= #DLY x1;
end
end
//spi_csn_inter
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
spi_csn_inter <= #DLY 1'b1;
end else if(state_change&(cur_state==CMD)) begin
spi_csn_inter <= #DLY 1'b0;
end else if((cur_state==CLR_CMD)) begin
spi_csn_inter <= #DLY 1'b1;
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
ctrl_req <= #DLY 1'b0;
end else if(state_change) begin
if( (cur_state==IDLE) | (cur_state==CLR_CMD)| (cur_state==RX_WAIT)| (cur_state==TX_WAIT) )begin
ctrl_req <= #DLY 1'b0;
end else begin
ctrl_req <= #DLY 1'b1;
end
end else if(ctrl_ack) begin
ctrl_req <= #DLY 1'b0;
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
ctrl_wr <= #DLY 1'b0;
ctrl_wdata <= #DLY 8'b0;
end else if(state_change) begin
case(cur_state)
CMD :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY cmd ;end
ADDR_H :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY add_h ;end
ADDR_M :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY add_m ;end
ADDR_L :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY add_l ;end
DUMMY :begin ctrl_wr<= #DLY 1'b0; ctrl_wdata<= #DLY 8'hFF ;end
TX_DATAX1 :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY tx_data0;end
TX_DATAX4 :begin ctrl_wr<= #DLY 1'b1; ctrl_wdata<= #DLY tx_data0;end
RX_DATAX4 :begin ctrl_wr<= #DLY 1'b0; end
RX_DATAX2 :begin ctrl_wr<= #DLY 1'b0; end
RX_DATAX1 :begin ctrl_wr<= #DLY 1'b0; end
default:begin ctrl_wr<= #DLY ctrl_wr; ctrl_wdata<= #DLY ctrl_wdata ;end
endcase
end
end
always@(posedge clk or posedge rst)begin
if(rst == 1'b1)begin
spi_is_out_inter<=#DLY 4'b0000;
end else if(state_change) begin
case({cur_state,mode_sel})
{CMD ,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end//x1模式下:IO0永远是输出
{ADDR_H ,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end
{ADDR_M ,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end
{ADDR_L ,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end
{DUMMY ,x1} :begin spi_is_out_inter<=#DLY 4'b0000;end
{TX_DATAX1,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end
{TX_DATAX4,x1} :begin spi_is_out_inter<=#DLY 4'b0001;end
{RX_DATAX4,x1} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX2,x1} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX1,x1} :begin spi_is_out_inter<=#DLY 4'b0000;end
{CMD ,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{ADDR_H ,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{ADDR_M ,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{ADDR_L ,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{DUMMY ,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{TX_DATAX1,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{TX_DATAX4,x2} :begin spi_is_out_inter<=#DLY 4'b0011;end
{RX_DATAX4,x2} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX2,x2} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX1,x2} :begin spi_is_out_inter<=#DLY 4'b0000;end
{CMD ,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{ADDR_H ,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{ADDR_M ,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{ADDR_L ,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{DUMMY ,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{TX_DATAX1,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{TX_DATAX4,x4} :begin spi_is_out_inter<=#DLY 4'b1111;end
{RX_DATAX4,x4} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX2,x4} :begin spi_is_out_inter<=#DLY 4'b0000;end
{RX_DATAX1,x4} :begin spi_is_out_inter<=#DLY 4'b0000;end
default :begin spi_is_out_inter<=#DLY 4'b0000;end
endcase
end
end
spix1 u_spix1(
.clk (clk ),//input
.rst (rst ),//input
.spi_di (x1_di ),//input
.spi_do (x1_do ),//output
.spi_clk (x1_clk ),//output
.SB_req (x1_req ),//input
.SB_wr (x1_wr ),//input
.SB_wdata (x1_wdata ),//input [7:0]
.SB_rdata (x1_rdata ),//output [7:0]
.SB_ack (x1_ack ) //output
);
spix2 u_spix2(
.clk (clk ),//input
.rst (rst ),//input
.spi_di (x2_di ),//input
.spi_do (x2_do ),//output
.spi_clk (x2_clk ),//output
.SB_req (x2_req ),//input
.SB_wr (x2_wr ),//input
.SB_wdata (x2_wdata ),//input [7:0]
.SB_rdata (x2_rdata ),//output [7:0]
.SB_ack (x2_ack ) //output
);
spix4 u_spix4(
.clk (clk ),//input
.rst (rst ),//input
.spi_di (x4_di ),//input
.spi_do (x4_do ),//output
.spi_clk (x4_clk ),//output
.SB_req (x4_req ),//input
.SB_wr (x4_wr ),//input
.SB_wdata (x4_wdata ),//input [7:0]
.SB_rdata (x4_rdata ),//output [7:0]
.SB_ack (x4_ack ) //output
);
//锁存数据到寄存器
always @(posedge clk or posedge rst)if(rst) tx_data0 <=#1 8'b0;else if(wr_data )tx_data0 <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) cmd <=#1 8'hFF;else if(wr_cmd )cmd <=#1 m_din;else if(cur_state==CLR_CMD )cmd <=#1 8'hFF;
always @(posedge clk or posedge rst)if(rst) add_h <=#1 8'b0;else if(wr_add_h)add_h <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) add_m <=#1 8'b0;else if(wr_add_m)add_m <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) add_l <=#1 8'b0;else if(wr_add_l)add_l <=#1 m_din;
always @(posedge clk or posedge rst)if(rst) wr_data_1d <=#1 1'b0;else wr_data_1d <=#1 wr_data;
// 读数据接口
always @(posedge clk or posedge rst)begin
if(rst)
m_dout <=#1 8'b0;
else begin
case({rd_data,rd_stata,rd_add_h,rd_add_m,rd_add_l})
5'b00001: m_dout <=#1 add_l;
5'b00010: m_dout <=#1 add_m;
5'b00100: m_dout <=#1 add_h;
5'b01000: m_dout <=#1 state;
5'b10000: m_dout <=#1 rx_data0;
default : m_dout <=#1 m_dout;
endcase
end
end
//rx_data0
always @(posedge clk or posedge rst)begin
if(rst)
rx_data0 <=#1 8'b0;
else if(ctrl_ack) begin
case(cur_state)
RX_DATAX4: rx_data0 <=#1 ctrl_rdata;
RX_DATAX2: rx_data0 <=#1 ctrl_rdata;
RX_DATAX1: rx_data0 <=#1 ctrl_rdata;
RDID : rx_data0 <=#1 ctrl_rdata;
default : rx_data0 <=#1 ctrl_rdata;
endcase
end
end
//state bit0:忙不处于IDLE bit1:无意义 bit2:接收就绪 bit3:等待新数据
always @(posedge clk or posedge rst)begin
if(rst)
state <=#1 8'b0;
else begin
case(cur_state)
TX_WAIT : state[3:0] <=#1 4'b1001;
RX_WAIT : state[3:0] <=#1 4'b0101;
default : state <=#1 4'b0001;
endcase
end
end
endmodule
tb
`timescale 1ns/100ps
`define SIM_OPEN 1
module tb ( );
reg clk =0,rst =1;
always clk =#5 ~clk;
reg m_cs = 0 ;
reg m_wr = 0 ;
reg [2:0] m_addr = 0 ;
reg [7:0] m_din = 0 ;
wire [7:0] m_dout ;
wire spi_clk ;
wire spi_csn ;
wire[3:0] spi_din ;
wire[3:0] spi_dout ;
parameter NOP = 8'hff;
parameter WREN = 8'h06; //CMD ok
parameter F_RDx1 = 8'h0B; //CMD+ADDR+dummy+rdata+... OK
parameter F_RDx2 = 8'h3B; //CMD+ADDR+dummy+rdata_x2+... ok
parameter F_RDx4 = 8'h6B; //CMD+ADDR+dummy+rdata_x4+... ok
parameter PPx1 = 8'h02; //CMD+ADDR+wdata_x1+... OK
parameter PPx4 = 8'h32; //CMD+ADDR+wdata_x4+...ok
parameter SE = 8'hD8; //CMD+ADDR ok
parameter RDID = 8'h9F; //CMD+rdata_x1+... ok
parameter REG_ADD_L = 4'd4;
parameter REG_ADD_M = 4'd3;
parameter REG_ADD_H = 4'd2;
parameter REG_STATE = 4'd1;
parameter REG_CMD = 4'd1;
parameter REG_RDATA0 = 4'd0;
parameter REG_WDATA0 = 4'd0;
//-------------产生spi_din------------
reg [23:0] rand1=0;
initial begin
while(1) begin
@(negedge spi_clk);
rand1 = {$random} % 100;
end
end
assign spi_din = rand1[3:0];
reg [7:0] rdata_sim;
reg [7:0] WaitData=8'h80;
reg [31:0] index ;
integer i;
initial begin
rst =1;
#100;
@(posedge clk);#1;
rst =0;
#100;
@(posedge clk);#1;
m_interfasc(1,REG_WDATA0,8'h99,rdata_sim);//数据aa
m_interfasc(1,REG_ADD_H,8'h01,rdata_sim);//addh
m_interfasc(1,REG_ADD_M,8'h02,rdata_sim);//addm
m_interfasc(1,REG_ADD_L,8'h03,rdata_sim);//addl
// m_interfasc(1,REG_CMD,WREN ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,F_RDx1 ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,F_RDx2 ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,F_RDx4 ,rdata_sim);//cmd
m_interfasc(1,REG_CMD,PPx1 ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,PPx4 ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,SE ,rdata_sim);//cmd
// m_interfasc(1,REG_CMD,RDID ,rdata_sim);//cmd
#5000;
// m_interfasc(0,REG_RDATA0,8'hAA,rdata_sim);//数据aa
m_interfasc(1,REG_WDATA0,8'hAA,rdata_sim);//数据aa
#2000;
// m_interfasc(0,REG_RDATA0,8'hBB,rdata_sim);//数据aa
m_interfasc(1,REG_WDATA0,8'hBB,rdata_sim);//数据aa
#2000;
m_interfasc(1,REG_CMD,NOP,rdata_sim);//cmd
// pp
// for(i=0;i<7;i=1+i) begin
// index = 1;
// while(index)
// begin
// #100;
// @(posedge clk);#1;
// m_interfasc(0,3'h1,8'h00,rdata_sim);//读状态
// if(rdata_sim[3:0]==4'b1011)
// begin
// m_interfasc(1,3'h0,WaitData,rdata_sim);//数据
// index = 0;
// end
// end
// WaitData= WaitData>>1;
// end
// #1000;
// m_interfasc(1,3'h1,NOP,rdata_sim);//cmd
// #1000;
// F_RD
// m_interfasc(1,3'h1,F_RD,rdata_sim);//cmd
// for(i=0;i<10;i=1+i) begin
// index = 1;
// while(index)
// begin
// #100;
// @(posedge clk);#1;
// m_interfasc(0,3'h1,8'h00,rdata_sim);//读状态
// if(rdata_sim[3:0]==4'b0111)
// begin
// m_interfasc(0,3'h0,8'h00,rdata_sim);//数据
// index = 0;
// end
// end
// end
// #1000;
// m_interfasc(1,3'h1,NOP,rdata_sim);//cmd
end
spi_top U_spi_top(
.clk (clk ),//input clk ,
.rst (rst ),//input rst ,
.spi_clk (spi_clk ),//output spi_clk ,
.spi_csn ( ),//output spi_csn ,
.spi_di (spi_din ),//input spi_din ,
.spi_do ( ),//output spi_dout ,
.m_cs (m_cs ),//output m_cs ,
.m_wr (m_wr ),//output m_wr ,
.m_addr (m_addr ),//output m_addr ,
.m_din (m_din ),//output m_din ,
.m_dout (m_dout )//output m_dout
);
task m_interfasc;//注意分号; 在第一行“task”语句中不能列出端口名称;
input wr;
input [3:0] addr;
input [7:0] wdata;
output reg[7:0] rdata;
#100;
@(posedge tb.clk);#1;
tb.m_cs = 0 ;
tb.m_wr = 0 ;
tb.m_addr = 0 ;
tb.m_din = 0 ;
@(posedge tb.clk);#1;
tb.m_cs = 1 ;
tb.m_wr = wr ;
tb.m_addr = addr ;
tb.m_din = wdata ;
if(wr==0) begin
@(posedge tb.clk);#3;
tb.m_cs = 0 ;
rdata = tb.m_dout ;
$display("%h",rdata);
end else rdata = tb.m_dout ;
@(posedge tb.clk);#1;
tb.m_cs = 0 ;
tb.m_wr = 0 ;
tb.m_addr = 0 ;
tb.m_din = 0 ;
endtask
endmodule