`timescale 1ns/1ps
module FifoCtrl_tb;
logic WrClk_i;
logic RdClkDDS_i;
logic RdClkLMX_i;
logic RdClkMax_i;
logic Rst_i;
logic [23:0] dataForDDS;
logic [23:0] dataForLMX;
logic [23:0] dataForMAX;
logic BusySpiLMX;
logic BusySpiDDS;
logic BusySpiMax;
logic valFromSPI64;
logic valFromSPI32;
logic valFromSPI24;

logic wrEnDDS;
logic wrEnLMX;

logic rdEnDDS;
logic rdEnLMX;

logic fullFlagLMX;
logic emptyFlagLMX;

logic fullFlagDDS;
logic emptyFlagDDS;

logic [23:0] dataToFifo24;
logic [31:0] dataToFifo32;
logic [63:0] dataToFifo64;
logic [23:0] dataFromFifo24;
logic [31:0] dataFromFifo32;
logic [63:0] dataFromFifo64;

logic [6:0] OUT_WIDTH;
logic [1:0] WR_NUM;

//***********************************************
//	           LOCALPARAMS
//***********************************************
localparam FIFTY_MHZ = 20;
localparam SIXTY_MHZ = 16;
localparam TWENTY_MHZ = 50;

//***********************************************
//	           CLOCK GENERATION
//***********************************************
always begin
    #50 WrClk_i = ~WrClk_i;
    #(FIFTY_MHZ/2) RdClkDDS_i = ~RdClkDDS_i;
    #(SIXTY_MHZ/2) RdClkLMX_i = ~RdClkLMX_i;
    #(TWENTY_MHZ/2) RdClkMax_i = ~RdClkMax_i;
end
//***********************************************
//	           DUT INSTANTIATION
//***********************************************
FifoCtrl#(
    .IN_WIDTH(24),
    .WR_NUM(3),
    .OUT_WIDTH(64)
) FifoCtrlDDS_inst (
    .WrClk_i(WrClk_i),
    .RdClk_i(RdClkDDS_i),
    .Rst_i(Rst_i),
    .Data_i(dataForDDS),
    .Val_i(valFromSPI64),
    .FifoFull_i(fullFlagDDS),
    .FifoEmpty_i(emptyFlagDDS),
    .BusySpiM_i(BusySpiDDS),
    .Data_o(dataToFifo64),
    .ReadEn_o(rdEnDDS),
    .ValRdData_o(valRdDDS),
    .WriteEn_o(wrEnDDS)
);

FifoCtrl #(
    .IN_WIDTH(24),
    .WR_NUM(1),
    .OUT_WIDTH(24)
) FifoCtrlLMX_inst (
    .WrClk_i(WrClk_i),
    .RdClk_i(RdClkLMX_i),
    .Rst_i(Rst_i),
    .Data_i(dataForLMX),
    .Val_i(valFromSPI24),
    .FifoFull_i(fullFlagLMX),
    .FifoEmpty_i(emptyFlagLMX),
    .BusySpiM_i(BusySpiLMX),
    .Data_o(dataToFifo24),
    .ReadEn_o(rdEnLMX),
    .ValRdData_o(valRdLMX),
    .WriteEn_o(wrEnLMX)
);

FifoCtrl #(
    .IN_WIDTH(24),
    .WR_NUM(2),
    .OUT_WIDTH(32)
) FifoCtrlMax_inst (
    .WrClk_i(WrClk_i),
    .RdClk_i(RdClkMax_i),
    .Rst_i(Rst_i),
    .Data_i(dataForMAX),
    .Val_i(valFromSPI32),
    .FifoFull_i(fullFlagMax),
    .FifoEmpty_i(emptyFlagMax),
    .BusySpiM_i(BusySpiMax),
    .Data_o(dataToFifo32),
    .ReadEn_o(rdEnMax),
    .ValRdData_o(valRdMax),
    .WriteEn_o(wrEnMax)
);
// Depth 16
FifoLMX FifoLMX_inst (
    .Data(dataToFifo24),
    .WrClk(WrClk_i),
    .Reset(Rst_i),
    .RdClk(RdClkLMX_i),
    .WrEn(wrEnLMX),
    .RdEn(rdEnLMX),
    .Q(dataFromFifo24),
    .Empty(emptyFlagLMX),
    .Full(fullFlagLMX)
);
// Depth 2
FifoDDS FifoDDS_inst (
    .Data(dataToFifo64),
    .WrClk(WrClk_i),
    .Reset(Rst_i),
    .RdClk(RdClkDDS_i),
    .WrEn(wrEnDDS),
    .RdEn(rdEnDDS),
    .Q(dataFromFifo64),
    .Empty(emptyFlagDDS),
    .Full(fullFlagDDS)
);
//Depth 4
FifoMax2870 FifoMax_inst (
    .Data(dataToFifo32),
    .WrClk(WrClk_i),
    .Reset(Rst_i),
    .RdClk(RdClkMax_i),
    .WrEn(wrEnMax),
    .RdEn(rdEnMax),
    .Q(dataFromFifo32),
    .Empty(emptyFlagMax),
    .Full(fullFlagMax)
);

SpiM #(
    .DATA_WIDTH(24)
)SpiMLMX_inst(
    .Clk_i(RdClkLMX_i),
    .Rst_i(Rst_i),
    .Val_i(valRdLMX),
    .SpiData_i(dataFromFifo24),
    .Busy_o(BusySpiLMX)
);

SpiM #(
    .DATA_WIDTH(64)
)SpiMDDS_inst(
    .Clk_i(RdClkDDS_i),
    .Rst_i(Rst_i),
    .Val_i(valRdDDS),
    .SpiData_i(dataFromFifo64),
    .Busy_o(BusySpiDDS)
);

SpiM #(
    .DATA_WIDTH(32)
)SpiMMax_inst(
    .Clk_i(RdClkMax_i),
    .Rst_i(Rst_i),
    .Val_i(valRdMax),
    .SpiData_i(dataFromFifo32),
    .Busy_o(BusySpiMax)
);
//***********************************************
//	           TASKS
//***********************************************
task drive_fifo64();
    valFromSPI64 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI64 = 1'b1;
    dataForDDS = {8'h0, 16'hFFFF};
    #10;
    @(posedge WrClk_i)
    valFromSPI64 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI64 = 1'b1;
    dataForDDS = 24'habcdef;
    #10;
    @ (posedge WrClk_i)
    valFromSPI64 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI64 = 1'b1;
    dataForDDS = 24'h123456;
    #10;
    @(posedge WrClk_i)
    valFromSPI64 = 1'b0;
endtask

task drive_fifo32();
    valFromSPI32 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI32 = 1'b1;
    dataForMAX = {16'h0, 8'h12};
    #10;
    @(posedge WrClk_i)
    valFromSPI32 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI32 = 1'b1;
    dataForMAX = 24'habcdef;
    #10;
    @ (posedge WrClk_i)
    valFromSPI32 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI32 = 1'b1;
    dataForMAX = {16'h0, 8'h12};
    #10;
    @(posedge WrClk_i)
    valFromSPI32 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI32 = 1'b1;
    dataForMAX = $urandom();
    #10;
    @(posedge WrClk_i)
    valFromSPI32 = 1'b0;
endtask

task drive_fifo24();
    Rst_i = 1'b1;
    valFromSPI24 = 1'b0;
    dataForLMX = 24'h0;
    #200;
    Rst_i = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI24 = 1'b1;
    dataForLMX = 24'h123456;
    #10;
    @(posedge WrClk_i)
    valFromSPI24 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI24 = 1'b1;
    dataForLMX = 24'habcdef;
    #10;
    @ (posedge WrClk_i)
    valFromSPI24 = 1'b0;
    #300;
    @ (posedge WrClk_i)
    valFromSPI24 = 1'b1;
    dataForLMX = 24'h123456;
    #10;
    @(posedge WrClk_i)
    valFromSPI24 = 1'b0;
endtask
//***********************************************
//	           INITIALIZATION
//***********************************************
initial begin 
    WrClk_i = 1'b1;
    RdClkDDS_i = 1'b1;
    RdClkLMX_i = 1'b1;
    RdClkMax_i = 1'b1;
    drive_fifo24();
    drive_fifo32();
    drive_fifo64();
end

endmodule