//----------------------------------------------------------------------
// Testbench for lr_acc_512_8b module
//
// Description:
// - Writes 32 data points (x, y pairs) to the DUT memory.
// - Sends the 'go' signal to start the linear regression calculation.
// - Waits for the 'master_done' signal.
// - Reads the final results and intermediate sums.
// - Displays the read values.
//----------------------------------------------------------------------

`timescale 1ns / 1ps

// Include the header file containing definitions
// Make sure lr_acc.svh defines the necessary parameters like:
// DATA_SET_SIZE, PIPELINE_SETS, ADDRESS_BITS_SIZE, MULT_DATA_IN_LEN,
// OUTPUT_DATA_LEN, SPLIT_DATA_LEN, PC_BITS_SIZE, DSP_MULT_OUT_SIZE
`include "lr_acc.svh"

module tb_lr_acc_512_8b;

    // Parameters
    parameter CLK_PERIOD = 10; // Clock period in ns
    parameter NUM_DATA_POINTS = 32; // Number of data points to write

    // Testbench Signals
    logic                             clk;
    logic                             reset;
    logic [31:0]                      writedata;
    logic                             write;
    logic                             read;
    logic                             chipselect;
    logic [9:0]                       address;
    logic [31:0]                      readdata;

    // Instantiate the Design Under Test (DUT)
    lr_acc_512_8b dut (
        .clk(clk),
        .reset(reset),
        .writedata(writedata),
        .write(write),
        .read(read),
        .chipselect(chipselect),
        .address(address),
        .readdata(readdata)
        // Assuming master_done is an internal signal, not a port.
        // If it's a port, add: .master_done(master_done_wire)
    );

    // Clock Generation
    initial begin
        clk = 0;
        forever #(CLK_PERIOD / 2) clk = ~clk;
    end

    // Task for writing data to DUT memory
    task write_mem (input [9:0] addr, input [3:0] x_val, input [3:0] y_val);
        @(posedge clk);
        chipselect = 1'b1;
        write      = 1'b1;
        read       = 1'b0;
        address    = {1'b0, addr[8:0]}; // Address bit 9 is 0 for data write
        writedata  = {24'h0, y_val, x_val}; // Pack x and y into writedata[7:0]

        $display("TB: Wrote data {x=%h, y=%h} to address %0d", x_val, y_val, addr);
    endtask

    // Task for sending the 'go' signal
    task send_go;
        @(posedge clk);
        chipselect = 1'b1;
        write      = 1'b1;
        read       = 1'b0;
        address    = 10'h220; // Address bit 9 is 1 for control write (go signal)
        writedata  = 32'h00000001; // Set go bit (assuming it's bit 0)
        @(posedge clk);
        chipselect = 1'b0;
        write      = 1'b0;
        address    = 'x;
        writedata  = 'x;
        $display("TB: Sent 'go' signal.");
    endtask

    // Task for reading results from DUT
    task read_result (input [9:0] addr, output logic [31:0] result_data);
        @(posedge clk);
        chipselect = 1'b1;
        write      = 1'b0;
        read       = 1'b1;
        address    = addr;
        @(posedge clk); // Allow one cycle for read data to propagate
        result_data = readdata;
        @(posedge clk);
        chipselect = 1'b0;
        read       = 1'b0;
        address    = 'x;
        $display("TB: Read data %h from address %h", result_data, addr);
    endtask

    // Main Test Sequence
    initial begin
        logic [31:0] read_val;

        $display("TB: Simulation Started.");

        // 1. Initialize signals and apply reset
        chipselect = 1'b0;
        write      = 1'b0;
        read       = 1'b0;
        address    = 'x;
        writedata  = 'x;
        reset      = 1'b1;
        $display("TB: Asserting Reset.");
        repeat (5) @(posedge clk); // Hold reset for a few cycles
        reset = 1'b0;
        $display("TB: Deasserting Reset.");
        @(posedge clk);

        // 2. Write data points
        $display("TB: Writing %0d data points...", NUM_DATA_POINTS);
        for (int i = 0; i < NUM_DATA_POINTS; i++) begin
            // Generate some simple data (e.g., x=i, y=i*2)
            // Ensure values fit within 4 bits (0-15)
            // *** FIX: Declare loop variables as automatic ***
            automatic logic [3:0] x_data = i % 16;
            automatic logic [3:0] y_data = (i * 2) % 16;
            write_mem(i, x_data, y_data);
        end
        @ (posedge clk);
        chipselect = 1'b0;
        write      = 1'b0;
        address    = 'x; // Set to X when inactive
        writedata  = 'x;
        $display("TB: Finished writing data points.");

        // Add some delay before sending go (optional)
        repeat(2) @(posedge clk);

        // 3. Send the 'go' signal
        send_go();

        // 4. Wait for the calculation to complete
        // We need to monitor an internal signal 'master_done'
        // Since it's not a port, we need to access it hierarchically.
        // Or, if the DUT signals completion differently (e.g., specific readdata value
        // or a dedicated done port), adjust the waiting mechanism.
        $display("TB: Waiting for master_done signal...");
        wait (dut.master_done == 1'b1); // Hierarchical reference
        $display("TB: master_done signal received.");
        @(posedge clk); // Allow one cycle for done logic to settle if needed

        // 5. Read the results
        $display("TB: Reading results...");
        read_result(10'h0, read_val); $display("TB: s1s4_minus_s2s2 = %h (%0d)", read_val, read_val);
        read_result(10'h1, read_val); $display("TB: s3s4_minus_s2s5 = %h (%0d)", read_val, read_val);
        read_result(10'h2, read_val); $display("TB: s1s5_minus_s2s3 = %h (%0d)", read_val, read_val);
        read_result(10'h3, read_val); $display("TB: global_s_1       = %h (%0d)", read_val, read_val);
        read_result(10'h4, read_val); $display("TB: global_s_2       = %h (%0d)", read_val, read_val);
        read_result(10'h5, read_val); $display("TB: global_s_3       = %h (%0d)", read_val, read_val);
        read_result(10'h6, read_val); $display("TB: global_s_4       = %h (%0d)", read_val, read_val);
        read_result(10'h7, read_val); $display("TB: global_s_5       = %h (%0d)", read_val, read_val);

        // 6. Finish Simulation
        $display("TB: Simulation Finished.");
        $finish;
    end

endmodule

