`timescale 1ns/1ns

module TopSbTmsgTb(inout Mosi1_io);
   parameter CLK_PERIOD = 8.13; // Clock period in ns

    // Inputs
    logic Clk_i;
    logic Clk100;
    logic Clk200;
    logic Clk125;
    logic Clk60;
    logic Clk20;
    logic Clk80;
    logic Clk50;
    logic Clk24;
    logic Clk10; 
    logic Rst_i;
    logic Start_i;
    logic CPHA_i;
    logic [31:0] SPIdata;
	logic SpiDataVal_i;
    logic SELST_i;
    logic [1:0] WidthSel_i;
    logic LAG_i;
    logic LEAD_i;
    logic EndianSel_i;
    logic [5:0] Stop_i;
    logic PulsePol_i;
    logic MisoLdLmx_i;

    // Outputs
    wire Mosi0_o;
    wire Mosi1_o;
    wire Mosi2_o;
    wire Mosi3_o;
    wire Sck_o;
    wire Ss_o;
    wire Val_o;
    wire anyFlag;

    wire valR;
    wire valQ;
    wire SckR;
    wire SckQ;
    wire SsR;
    wire SsQ;
    wire mosi0R;
    wire mosi0Q;

    wire locked;
    wire rstInit;

    logic mosi1Reg;

    logic [16:0] trCnt;
    logic [4:0] trCntSync;


    logic modeSel; 
    logic [23:0] randData;
    logic [31:0] randData32;
    logic [5:0] QSPITotalWordNum;
    logic Stop;
    logic [31:0] stopCnt;
    logic rstForFPGA;

//***********************************************
//	            Lines From RF Top
//***********************************************

    logic [7:0] sckFromRFTop;
    logic [7:0] mosiFromRFTop;
    logic [7:0] ssFromRFTop;


    logic [23:0] dataFromSPItb;
    logic        valFromSPItb; 

//***********************************************
//	            CLASSES
//***********************************************

class Packet;
    rand bit [23:0] data;
    rand bit [31:0] data32;
endclass

Packet pkt;

//***********************************************
//	      HEADERS FOR DEVICES
//***********************************************
localparam [4:0]  DeviceIdLmx2594 = 5'h0;
localparam [4:0]  DeviceIdDDS = 5'h1;
localparam [4:0]  DeviceIdPot = 5'h2;
localparam [4:0]  DeviceIdDac = 5'h3;
localparam [4:0]  DeviceIdAtt = 5'h4;
localparam [4:0]  DeviceIdShReg = 5'h5;
localparam [4:0]  DeviceIdMax2870 = 5'h6;
localparam [4:0]  DeviceIdGpio1 = 5'h7;
localparam [4:0]  DeviceIdTemp = 5'h8;
localparam [4:0]  DeviceIdGpio2 = 5'h9;

localparam [16:0] Gpio1InitWordNum = 17'd1;
localparam [16:0] Gpio2InitWordNum = 17'd1;
localparam [16:0] PotWordInitNum = 17'd1;
localparam [16:0] DacWordInitNum = 17'd1;
localparam [16:0] AttWordInitNum = 17'd1;
localparam [16:0] ShRegWordInitNum = 17'd1;
localparam [16:0] Lmx2594InitWordNum = 17'd13;
localparam [16:0] DDSInitWordNum = 17'd7;
localparam [16:0] MaxInitWordNum = 17'd6;
localparam [16:0] TempSensWordNum = 17'd1;

localparam [23:0] InitGpio1Header       = {1'h0, DeviceIdGpio1, Gpio1InitWordNum, 1'h1};
localparam [23:0] InitGpio2Header       = {1'b0, DeviceIdGpio2,Gpio2InitWordNum,1'h1 };
localparam [23:0] TempSensHeader        = {1'h0, DeviceIdTemp, TempSensWordNum, 1'h1};
localparam [23:0] InitLMX2594Header     = {1'h0, DeviceIdLmx2594, Lmx2594InitWordNum, 1'h1};
localparam [23:0] InitDDSHeader         = {1'h0, DeviceIdDDS, DDSInitWordNum, 1'h1};
localparam [23:0] InitMAX2870Header     = {1'h0, DeviceIdMax2870, MaxInitWordNum, 1'h1};
localparam [23:0] InitPotHeader         = {1'h0, DeviceIdPot, PotWordInitNum, 1'h1};
localparam [23:0] InitDacHeader         = {1'h0, DeviceIdDac, DacWordInitNum, 1'h1};
localparam [23:0] InitAttHeader         = {1'h0, DeviceIdAtt, AttWordInitNum, 1'h1};
localparam [23:0] InitShRegHeader       = {1'h0, DeviceIdShReg, ShRegWordInitNum, 1'h1};

localparam [3:0]  LMXWordNum = 4'd4;
localparam [2:0]  DDSWordNum = 3'd4;
localparam        POTWordNum = 2'd2;
localparam        DACWordNum = 1'd1;
localparam        ATTWordNum = 1'd1;
localparam [1:0]  ShRegWordNum = 2'd1;
localparam [1:0]  MaxWordNum =   2'd2;
localparam [1:0]  GPIOWordNum =  2'd1;

//***********************************************
//	           GPIO 1 REG 
//***********************************************
localparam [0:0] RF_SW1 = 1'h0;
localparam [0:0] RF_SW2 = 1'h0;
localparam [0:0] CTRL_AM_SW3 = 1'h0;
localparam [0:0] DDS_SYNC_CTRL_FPGA = 1'h0;
localparam [0:0] DDS_RESET_FPGA = 1'h0;
localparam [0:0] DDS_SYNC_FPGA = 1'h0;
localparam [0:0] SW_CAP4 = 1'h0;
localparam [0:0] AM_ALC_SW = 1'h0;
localparam [0:0] SW_CAP3 = 1'h0;
localparam [0:0] SW_CAP2 = 1'h0;
localparam [0:0] SW_CAP1 = 1'h0;
localparam [0:0] AM_ALC_1_FIX = 1'h0;
localparam [0:0] PLL_VTUNE_CTRL = 1'h0;
localparam [0:0] PLL_SYNC_CTRL = 1'h0;
localparam [0:0] PLL_SYNC = 1'h0;
localparam [0:0] PLL_LOOP_CTRL = 1'h0;
localparam [0:0] DDS_X2_FPGA = 1'h0;
localparam [0:0] DDS_SAW2_FPGA = 1'h0;
localparam [0:0] REF_OFFSET_CTRL_FPGA = 1'h0;
localparam [0:0] GPIO_ADRF_V1 = 1'h0;
localparam [0:0] GPIO_ADRF_V2 = 1'h0;
localparam [0:0] DDS_SAW1_FPGA = 1'h0;

localparam [23:0] GPIO_REG = {DDS_SAW1_FPGA,GPIO_ADRF_V2,GPIO_ADRF_V1,REF_OFFSET_CTRL_FPGA,DDS_SAW2_FPGA,DDS_X2_FPGA,PLL_LOOP_CTRL,PLL_SYNC,PLL_SYNC_CTRL,PLL_VTUNE_CTRL,AM_ALC_1_FIX,SW_CAP1,SW_CAP2,SW_CAP3,AM_ALC_SW,SW_CAP4,DDS_SYNC_FPGA,DDS_RESET_FPGA,DDS_SYNC_CTRL_FPGA,CTRL_AM_SW3,RF_SW2,RF_SW1};

//***********************************************

// localparam [23:0] AllDevQSPIHeader = {1'h1, LMXWordNum, DDSWordNum, POTWordNum, DACWordNum,ATTWordNum, ShRegWordNum,MaxWordNum, GPIOWordNum, 7'h1};
localparam [23:0] AllDevQSPIHeader = {1'h1, 1'h1,LMXWordNum,GPIOWordNum, 1'h0, DDSWordNum,1'h0,MaxWordNum,1'h0,ShRegWordNum,1'h0,POTWordNum,DACWordNum,ATTWordNum,1'h1};

//***********************************************
//	           ASSIGNS
//***********************************************
assign Val_o = (modeSel) ? valQ : valR;
assign Sck_o = (modeSel) ? SckQ : SckR;
assign Ss_o = (modeSel) ? SsQ : SsR;
assign Mosi0_o = (modeSel) ? mosi0Q : mosi0R;
assign Mosi1_io = (anyFlag) ? 1'bz : Mosi1_o;
assign MisoLdLmx_i = 1'b1;

assign emptyFlagTx = (trCnt > 61) ? 1'b1 : 1'b0;
assign QSPITotalWordNum = LMXWordNum + DDSWordNum + POTWordNum + DACWordNum + ATTWordNum + ShRegWordNum + MaxWordNum + GPIOWordNum; 

assign currClk = (modeSel) ? Clk60 : Clk10;

//***********************************************
//	           CLOCK GENERATION
//***********************************************
always #(CLK_PERIOD/2) Clk_i = ~Clk_i;
always #(10/2) Clk100 = ~Clk100;
always #(5/2) Clk200 = ~Clk200;
always #(8/2) Clk125 = ~Clk125;
always #(16.67/2) Clk60 = ~Clk60;
always #(20/2) Clk50 = ~Clk50;
always #(12.5/2) Clk80 = ~Clk80;
always #(41.67/2) Clk24 = ~Clk24;
always #(50/2) Clk20 = ~Clk20;
always #(50)   Clk10 = ~Clk10; 

//***********************************************
//	           INITIALIZATION
//***********************************************

initial begin
      // Initialize Inputs
      Clk_i = 1;
      Clk100= 1;
      Clk200 = 1;
      Clk125 = 1;
      Clk60 = 1;
      Clk20 = 1;
      Clk50 = 1;
      Clk80 = 1;
      Clk24 = 1;
      rstForFPGA = 0;
      Clk10 = 1;
      pkt = new();
      Rst_i = 1;
      Start_i = 0;
      CPHA_i = 0;		SpiDataVal_i = 0;
      SELST_i = 1;//0:High, 1:Low
    //   WidthSel_i = 2; // 3-32bit, 2-24bit, 1-16bit, 0-8bit
      LAG_i = 0;
      LEAD_i = 0;
      EndianSel_i = 0; // 0:MSB first, 1:lsb first
      PulsePol_i = 0;
      // Reset the system
      #(CLK_PERIOD*10) Rst_i = 0;
      #(700000-60) rstForFPGA = 1;
      #(CLK_PERIOD*74) rstForFPGA = 0;
      #(165000) Start_i = 1; // Start SPI transaction
       wait (trCnt == 45) begin 
            Start_i = 0;
        end
        #(CLK_PERIOD*100) 
        Start_i = 1; // Start SPI transaction
      wait (trCnt == 70) begin 
            Start_i = 0;
        end
        #(CLK_PERIOD*1000) 
        Start_i = 1; // Start SPI transaction
  end
//***********************************************

always_ff @(posedge currClk) begin
    if (Rst_i) begin 
        trCnt <= 0;
    end
    else begin 
        if (Val_o) begin 
            trCnt <= trCnt + 1;
        end
    end
end

always_comb begin 
    if (Rst_i) begin 
        mosi1Reg = 0;
    end
    else begin 
        mosi1Reg = Mosi1_io;
    end
end



genvar i;
always_comb begin 
    if (Rst_i) begin 
        WidthSel_i = 2'd0;
    end
    else begin
        if (trCnt == 1 || trCnt == 3 ) begin 
            WidthSel_i = 2'd0;
        end 
        else if (trCnt > 36 && trCnt < 43) begin 
            WidthSel_i = 2'd3;
        end
        else begin 
            WidthSel_i = 2'd2;
        end
    end
end

always_comb begin 
    if (Rst_i) begin 
        modeSel = 0;
    end
    else begin 
        if (trCnt == 45) begin 
            modeSel = 1;
        end
    end
end

always_comb begin 
    if (Rst_i) begin 
        Stop_i = 6'd0;
    end
    else begin 
        if (trCnt == 158) begin 
            Stop_i = 6'h0;
        end
        else begin
            Stop_i = 6'd0;
        end
    end
end

always_ff @(posedge currClk) begin 
    if (Rst_i) begin 
        randData<=0;
        randData32 <= 0;
    end
    else begin 
        randData <= pkt.randomize(data);
        randData32 <= pkt.randomize(data32);
    end
end

always_comb begin 
    if (Rst_i) begin 
        SPIdata = 0;
    end
    else begin 
        // if (!rstInit && locked) begin
            if (trCnt == 0) begin 
                SPIdata = InitGpio1Header;
            end
            else if (trCnt == 2) begin 
                SPIdata = InitGpio1Header;
            end
            else if (trCnt == 4) begin 
                SPIdata = TempSensHeader;
            end
            else if (trCnt == 6) begin
                SPIdata = InitLMX2594Header;
            end
            // else if (trCnt > 0 && trCnt < 114) begin 
            //     SPIdata = pkt.data;
            // end
            else if (trCnt == 20) begin 
                SPIdata = InitDDSHeader;
            end
            else if (trCnt == 28) begin 
                SPIdata = InitPotHeader;
            end
            else if (trCnt == 30) begin 
                SPIdata = InitDacHeader;
            end
            else if (trCnt == 32) begin 
                SPIdata = InitAttHeader;
            end
            else if (trCnt == 34) begin 
                SPIdata = InitShRegHeader;
            end
            else if (trCnt == 36) begin 
                SPIdata = InitMAX2870Header;
            end
            else if (trCnt > 36 && trCnt < 43) begin 
                SPIdata = 32'haaaaaaaa;
            end
            else if (trCnt == 43) begin 
                SPIdata = InitGpio2Header;
            end
            else if (trCnt == 45) begin 
                SPIdata = AllDevQSPIHeader;
            end
            else if (trCnt == 46) begin 
                SPIdata = 7'h55;
            end
            else if (trCnt == 72) begin 
                SPIdata = AllDevQSPIHeader;
            end
            else begin
                SPIdata = 24'haaaaaa;
            end
        end
    end
// end

//***********************************************
//	           DUT INSTANTIATION
//***********************************************
    GSR GSR(.GSRI(1'b1));

   ExtSpiMEmul ExtSpiMEmul_inst (
        .Clk_i(currClk), 
        .Rst_i(Rst_i || modeSel), 
        .Start_i(Start_i), 
        .ClockPhase_i(CPHA_i),
        .EmptyFlag_i(emptyFlagTx), 
        .SpiData_i(SPIdata),
        .SelSt_i(SELST_i),
        .WidthSel_i(WidthSel_i),
        .Lag_i(LAG_i),
        .Lead_i(LEAD_i),
        .EndianSel_i(EndianSel_i),
        .Stop_i(6'h0),
        .PulsePol_i(PulsePol_i),
        .Mosi0_o(mosi0R),
        .Sck_o(SckR),
        .Ss_o(SsR),
        .Val_o(valR)
    );

    ExtQspiMEmul ExtQspiMEmul_inst (
        .Clk_i(currClk),
        .Rst_i(Rst_i || !modeSel),
        .Start_i(Start_i),
        .ClockPhase_i(CPHA_i),
        .EmptyFlag_i(emptyFlagTx),
        .SpiData_i(SPIdata),
        .SelSt_i(SELST_i),
        .WidthSel_i(WidthSel_i),
        .Lag_i(LAG_i),
        .Lead_i(LEAD_i),
        .EndianSel_i(EndianSel_i),
        .Stop_i(6'h0),
        .PulsePol_i(PulsePol_i),
        .Mosi0_o(mosi0Q),
        .Mosi1_o(Mosi1_o),
        .Mosi2_o(Mosi2_o),
        .Mosi3_o(Mosi3_o),
        .Sck_o(SckQ),
        .Ss_o(SsQ),
        .Val_o(valQ)
    );
    
    ExtI2cSlaveEmul ExtI2cSlaveEmul_inst (
        .scl (i2cScl),
        .sda (i2cSda)
    );

    pullup p2(i2cSda); // pullup sda line

    TopSbTmsg TopSbTmsg_inst (
        .Clk_i(Clk24),
        .Rst_i(rstForFPGA),
        .Sck_i(Sck_o),
        .Ss_i(Ss_o),
        .MisoLdLmx_i(1'b1),
        .MisoLdMax2870_i(1'b1),
        .I2cScl_o(i2cScl),
        .I2cSda_io(i2cSda),
        .AnyFlag_o(anyFlag),
        .Mosi0_i(Mosi0_o),
        .Mosi1_io(Mosi1_io),
        .Mosi2_i(Mosi2_o),
        .Mosi3_i(Mosi3_o)
    );

    endmodule