【GAOPS037】verilog 实现spi x1、x2和x4模式读写

本文介绍了一个SPI接口的设计,包括SPI_x1、SPI_x2和SPI_x4模块,用于实现不同速度的SPI通信。每个模块独立处理SPI时序,通过LocalBus接口与上层模块交互。顶层模块spi_top根据命令选择相应的SPI速率并进行数据传输。设计中包含了状态机、时钟控制和数据流管理,以适应不同的SPI操作,如读写命令、地址和数据传输。
摘要由CSDN通过智能技术生成

已经在板卡上验证功能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

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1615549892

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值