// tb_rng_xsim.v - Standard Verilog testbench for rng_sync targeting Vivado xsim
//
// Reads expected LFSR output states from a hex file using $readmemh.
// Each line is a single 256-bit hex number = expected data_o after
// each valid_i pulse.
//
// Drives rng_sync, toggles valid_i for each vector, captures data_o,
// compares against expected value, and writes pass/fail results.
//
// Parameters:
//   VECTOR_FILE - path to input hex file (default: "vectors/rng_input.hex")
//   RESULT_FILE - path to output file    (default: "vectors/rng_result.hex")
//
// Usage:
//   xvlog -sv sync_rtl/rng/rng_sync.v
//   xvlog -sv sync_rtl/rng/TB/tb_rng_xsim.v
//   xelab tb_rng_xsim -s tb_rng_xsim
//   xsim tb_rng_xsim -R

`timescale 1ns / 1ps

module tb_rng_xsim;

    // ================================================================
    // Parameters
    // ================================================================
    parameter VECTOR_FILE = "sync_rtl/rng/TB/vectors/rng_input.hex";
    parameter RESULT_FILE = "sync_rtl/rng/TB/vectors/rng_result.hex";
    parameter MAX_VECTORS = 256;
    parameter TIMEOUT_CYCLES = 1000;

    // ================================================================
    // DUT signals
    // ================================================================
    reg              clk;
    reg              rst_n;
    reg              valid_i;
    wire             ready_o;
    wire [255:0]     data_o;
    wire             valid_o;
    reg              ready_i;

    // ================================================================
    // DUT instantiation
    // ================================================================
    rng_sync u_dut (
        .clk     (clk),
        .rst_n   (rst_n),
        .valid_i (valid_i),
        .ready_o (ready_o),
        .data_o  (data_o),
        .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)
    // Each word is 256 bits = the expected data_o value
    // ================================================================
    reg [255: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

    // Helper: write 256-bit value as 64 hex chars to file
    task write_hex_256bit;
        input integer fd;
        input [255:0] val;
        reg [3:0] nib;
        integer j;
        begin
            for (j = 63; j >= 0; j = j - 1) begin
                nib = val[(j*4)+:4];
                $fwrite(fd, "%c", nibble_to_ascii(nib));
            end
        end
    endtask

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

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

        // Count non-x 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] === 256'hx || vector_mem[idx] === 256'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: <64 hex chars> = expected 256-bit LFSR state");
            $finish;
        end

        $display("INFO: Loaded %0d expected LFSR states 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
        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 [255:0] expected;
                reg [255:0] captured;

                expected = vector_mem[idx];

                $display("INFO: Vector %0d", idx);

                // Drive DUT: pulse valid_i for one cycle
                valid_i <= 1'b1;
                @(posedge clk);
                valid_i <= 1'b0;

                // Wait for valid_o
                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);
                    $fwrite(result_fd, "FAIL: %0d - timeout\n", idx);
                    fail_count = fail_count + 1;
                end else begin
                    // Capture output
                    captured = data_o;

                    // Compare with expected
                    if (captured === expected) begin
                        $display("  PASS: data_o matches expected");
                        $fwrite(result_fd, "PASS: %0d\n", idx);
                        pass_count = pass_count + 1;
                    end else begin
                        $display("  FAIL: data_o mismatch");
                        $display("    Expected: %h", expected);
                        $display("    Got:      %h", captured);
                        $fwrite(result_fd, "FAIL: %0d - mismatch (expected mask)\n", idx);
                        fail_count = fail_count + 1;
                    end
                end

                // One extra cycle for valid_o handshake with ready_i
                @(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);
        $display("========================================");

        $finish;
    end

    // ================================================================
    // Timeout watchdog
    // ================================================================
    initial begin
        #(TIMEOUT_CYCLES * 10 * 100);  // TIMEOUT_CYCLES * 10ns per cycle * extra margin
        $display("FATAL: Global simulation timeout reached");
        $finish;
    end

endmodule
