// tb_comp_decomp_xsim.v - Standard Verilog testbench for comp_decomp_sync targeting Vivado xsim
//
// Reads test vectors from a hex file using $readmemh.
// Each line is a 32-bit hex value (8 hex chars, no spaces):
//   bits[31:20] = expected[11:0]
//   bits[19:8]  = coeff_in[11:0]
//   bits[7:3]   = d[4:0]
//   bit[2]      = mode (0=compress, 1=decompress)
//   bits[1:0]   = padding
//
// Drives comp_decomp_sync, waits for valid_o, compares coeff_out with expected,
// and reports pass/fail.
//
// Parameters:
//   VECTOR_FILE - path to input hex file (default: "sync_rtl/comp_decomp/TB/vectors/comp_decomp_input.hex")
//   RESULT_FILE - path to output file    (default: "sync_rtl/comp_decomp/TB/vectors/comp_decomp_result.hex")
//
// Usage with xsim_run.tcl or manual:
//   xvlog -sv sync_rtl/common/pipeline_reg.v sync_rtl/comp_decomp/comp_decomp_sync.v tb_comp_decomp_xsim.v
//   xelab tb_comp_decomp_xsim -s tb_comp_decomp_xsim
//   xsim tb_comp_decomp_xsim -R

`timescale 1ns / 1ps

module tb_comp_decomp_xsim;

    // ================================================================
    // Parameters
    // ================================================================
    parameter VECTOR_FILE    = "sync_rtl/comp_decomp/TB/vectors/comp_decomp_input.hex";
    parameter RESULT_FILE    = "sync_rtl/comp_decomp/TB/vectors/comp_decomp_result.hex";
    parameter MAX_VECTORS    = 512;
    parameter TIMEOUT_CYCLES = 10000;
    parameter Q              = 3329;

    // ================================================================
    // DUT signals
    // ================================================================
    reg             clk;
    reg             rst_n;
    reg  [11:0]     coeff_in;
    reg  [4:0]      d;
    reg             mode;
    reg             valid_i;
    wire            ready_o;
    wire [11:0]     coeff_out;
    wire            valid_o;
    reg             ready_i;

    // ================================================================
    // DUT instantiation (named ports)
    // ================================================================
    comp_decomp_sync u_dut (
        .clk        (clk),
        .rst_n      (rst_n),
        .coeff_in   (coeff_in),
        .d          (d),
        .mode       (mode),
        .valid_i    (valid_i),
        .ready_o    (ready_o),
        .coeff_out  (coeff_out),
        .valid_o    (valid_o),
        .ready_i    (ready_i)
    );

    // ================================================================
    // Clock generation: 100 MHz (10 ns period)
    // ================================================================
    initial clk = 1'b0;
    always #5 clk = ~clk;

    // ================================================================
    // Vector memory (loaded by $readmemh)
    // 32 bits per word:
    //   bits[31:20] = expected
    //   bits[19:8]  = coeff_in
    //   bits[7:3]   = d
    //   bit[2]      = mode
    //   bits[1:0]   = padding
    // ================================================================
    reg [31:0] vector_mem [0:MAX_VECTORS-1];
    integer    vec_count;
    integer    idx;
    integer    cycle_count;
    integer    result_fd;

    // Test result tracking
    integer    pass_count;
    integer    fail_count;

    // ================================================================
    // Hex-to-ASCII conversion helper
    // ================================================================
    function [7:0] nibble_to_ascii;
        input [3:0] nibble;
        begin
            if (nibble < 4'd10)
                nibble_to_ascii = 8'h30 + {4'd0, nibble};       // '0'-'9'
            else
                nibble_to_ascii = 8'h41 + ({4'd0, nibble} - 4'd10); // 'A'-'F'
        end
    endfunction

    // ================================================================
    // Main test sequence
    // ================================================================
    initial begin
        // Count loaded vectors
        vec_count = 0;

        // Load vectors from hex file
        $readmemh(VECTOR_FILE, vector_mem);

        // Count non-X/non-Z entries to determine actual vector count
        begin
            integer found_end;
            found_end = 0;
            for (idx = 0; idx < MAX_VECTORS; idx = idx + 1) begin
                if (!found_end && (vector_mem[idx] === 32'hx || vector_mem[idx] === 32'hz))
                    found_end = 1;
                else if (!found_end)
                    vec_count = vec_count + 1;
            end
        end

        if (vec_count == 0) begin
            $display("ERROR: No vectors loaded from %s", VECTOR_FILE);
            $display("       Check that the file exists and is in the correct format.");
            $display("       Each line: <8 hex chars> = {expected[11:0], coeff_in[11:0], d[4:0], mode, 2'b0}");
            $finish;
        end

        $display("INFO: Loaded %0d test vectors from %s", vec_count, VECTOR_FILE);

        // Open result file
        result_fd = $fopen(RESULT_FILE, "w");
        if (result_fd == 0) begin
            $display("ERROR: Cannot open result file: %s", RESULT_FILE);
            $finish;
        end

        // Initialize DUT inputs
        coeff_in <= 12'd0;
        d        <= 5'd0;
        mode     <= 1'b0;
        valid_i  <= 1'b0;
        ready_i  <= 1'b1;  // always ready to accept output

        // Reset sequence: rst_n low for 3 cycles, then high
        rst_n <= 1'b0;
        repeat (3) @(posedge clk);
        rst_n <= 1'b1;
        @(posedge clk);

        pass_count = 0;
        fail_count = 0;

        // ============================================================
        // Process each vector
        // ============================================================
        for (idx = 0; idx < vec_count; idx = idx + 1) begin
            begin
                reg [11:0] vec_expected;
                reg [11:0] vec_coeff_in;
                reg [4:0]  vec_d;
                reg        vec_mode;
                reg [11:0] captured_out;

                // Extract fields from vector
                vec_expected = vector_mem[idx][31:20];
                vec_coeff_in = vector_mem[idx][19:8];
                vec_d        = vector_mem[idx][7:3];
                vec_mode     = vector_mem[idx][2];

                $display("INFO: Vector %0d - coeff=%0d d=%0d mode=%s expected=%0d",
                         idx, vec_coeff_in, vec_d,
                         vec_mode ? "DECOMPRESS" : "COMPRESS", vec_expected);

                // Drive DUT inputs
                coeff_in <= vec_coeff_in;
                d        <= vec_d;
                mode     <= vec_mode;
                valid_i  <= 1'b1;
                @(posedge clk);
                valid_i  <= 1'b0;

                // Wait for valid_o assertion (1 pipeline stage latency)
                cycle_count = 0;
                while (!valid_o && cycle_count < TIMEOUT_CYCLES) begin
                    @(posedge clk);
                    cycle_count = cycle_count + 1;
                end

                if (cycle_count >= TIMEOUT_CYCLES) begin
                    $display("ERROR: Timeout waiting for valid_o on vector %0d", idx);
                    fail_count = fail_count + 1;
                    $fwrite(result_fd, "RESULT: VECTOR %0d TIMEOUT\n", idx);
                end else begin
                    // Capture output (valid_o is high, coeff_out is valid)
                    captured_out = coeff_out;

                    // Compare with expected
                    if (captured_out == vec_expected) begin
                        pass_count = pass_count + 1;
                        $fwrite(result_fd, "PASS: %0d - mode=%s coeff=%03X d=%0d expected=%03X got=%03X\n",
                                idx, vec_mode ? "DECOMPRESS" : "COMPRESS",
                                vec_coeff_in, vec_d, vec_expected, captured_out);
                    end else begin
                        $display("FAIL: Vector %0d - mode=%s coeff=%0d d=%0d expected=%0d got=%0d",
                                 idx, vec_mode ? "DECOMPRESS" : "COMPRESS",
                                 vec_coeff_in, vec_d, vec_expected, captured_out);
                        fail_count = fail_count + 1;
                        $fwrite(result_fd, "FAIL: %0d - mode=%s coeff=%03X d=%0d expected=%03X got=%03X\n",
                                idx, vec_mode ? "DECOMPRESS" : "COMPRESS",
                                vec_coeff_in, vec_d, vec_expected, captured_out);
                    end
                end

                // One extra cycle for valid_o -> ready_i handshake (pipeline clears valid_o)
                @(posedge clk);
            end
        end

        // ============================================================
        // Summary
        // ============================================================
        $fclose(result_fd);

        $display("========================================");
        $display("TEST COMPLETE");
        $display("  Total vectors: %0d", vec_count);
        $display("  Passed:        %0d", pass_count);
        $display("  Failed:        %0d", fail_count);
        $display("  Results written to: %s", RESULT_FILE);
        if (fail_count == 0) begin
            $display("  STATUS:        ALL TESTS PASSED");
        end else begin
            $display("  STATUS:        %0d FAILURE(S) DETECTED", fail_count);
        end
        $display("========================================");

        $finish;
    end

    // ================================================================
    // Timeout watchdog (TIMEOUT_CYCLES * 10ns * vectors * margin)
    // ================================================================
    initial begin
        #(TIMEOUT_CYCLES * 10 * MAX_VECTORS * 2);
        $display("FATAL: Global simulation timeout reached");
        $finish;
    end

endmodule
