`timescale 1ns / 1ps
module SpiM_tb;
logic Clk_i;
logic Rst_i;
logic [23:0] spiData;

//***********************************************
//	            LOCALPARAMS
//***********************************************
localparam N = 16;

//***********************************************
//	            SIGNALS
//***********************************************
logic [23:0] randData;
logic [23:0] spiQueue [N-1:0];
logic [23:0] spisQueue [N-1:0];
logic busyFromSpi;
logic valToSpi;
logic [4:0] numOfWords;
logic [4:0] numOfWordsSPIs;
logic valFromSPIs;
logic sck;
logic ss;
logic mosi;
logic [23:0] spiDataFromDUT;

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

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

Packet pkt;


//***********************************************
//	           CLOCK GENERATION
//***********************************************
always begin
    #5 Clk_i = ~Clk_i;
end

//***********************************************
//	           INITIALIZATION
//***********************************************
initial begin 
    Clk_i =1'b1;
    pkt = new();
    Rst_i = 1'b1;
    #50 Rst_i = 1'b0;
    foreach(spiQueue[i]) begin
        spiQueue[i] = $urandom();
    end
    wait(numOfWordsSPIs == 5'h10);
    if (spiQueue == spisQueue) begin
        $display("Test Passed");
    end
    else begin
        $display("Test Failed");
    end
end

//***********************************************
//	           CODING
//***********************************************
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        randData<=0;
    end
    else begin 
        randData <= pkt.randomize(data);
    end
end

always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        numOfWords<=5'h10;
    end
    else begin 
        if (valToSpi) begin 
            numOfWords <= numOfWords - 1;
        end
    end
end

always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        valToSpi <= 1'b0;
    end
    else begin 
        if (!busyFromSpi && numOfWords != 0) begin 
            valToSpi <= 1'b1;
        end
        else begin 
            valToSpi <= 1'b0;
        end
    end
end

always_ff @(*) begin 
    if (Rst_i) begin 
        spiData=0;
    end
    else begin 
        if (valToSpi) begin 
            spiData = spiQueue[numOfWords-1];
        end
    end
end

always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        numOfWordsSPIs<=5'h0;
    end
    else begin
        if (valFromSPIs) begin 
            numOfWordsSPIs <= numOfWordsSPIs + 1;
        end
    end
end



always_ff @(posedge Clk_i) begin
    if (Rst_i) begin 
        foreach(spisQueue[i]) begin
            spisQueue[i] <= 0;
        end
    end
    else begin 
        if (valFromSPIs) begin 
            spisQueue[(N-1)-numOfWordsSPIs] <= spiDataFromDUT;
        end
    end
end


//***********************************************
//	           DUT INSTANTIATION
//***********************************************
SpiM DUT (
    .Clk_i(Clk_i),
    .Rst_i(Rst_i),
    .SpiData_i(spiData),
    .Val_i(valToSpi),
    .Sck_o(sck),
    .Ss_o(ss),
    .Mosi_o(mosi),
    .Busy_o(busyFromSpi)
);

ExtSpiSlaveEmul ExtSpiSlaveEmul_inst (
    .Clk_i(Clk_i),
    .Rst_i(Rst_i),
    .Sck_i(sck),
    .Ss_i(ss),
    .Mosi0_i(mosi),
    .Data_o(spiDataFromDUT),
    .Val_o(valFromSPIs)
);

endmodule