// tb_sample_cbd_xsim.v - Vivado xsim testbench for sample_cbd_sync
//
// Reads test vectors from a hex file using $readmemh.
// Each line is a packed hex word: {eta_i[1:0], nonce_i[7:0], seed_i[255:0]}
//   - seed_i[255:0]     : 64 hex chars (LSB = seed_i[0])
//   - nonce_i[7:0]      : 2 hex chars
//   - eta_i[1:0]        : 1 hex char (2 or 3)
//   - Total: 67 hex chars (266 bits), zero-padded to fill 4-bit nibbles
//
// Drives sample_cbd_sync, waits for valid_o, collects 256 coefficients,
// and writes results to an output file.
//
// Usage:
//   xvlog -sv sample_cbd_sync.v TB/tb_sample_cbd_xsim.v
//   xelab tb_sample_cbd_xsim -s tb_sample_cbd_xsim
//   xsim tb_sample_cbd_xsim -R
//
// Prerequisites:
//   - Generate vectors: python3 TB/gen_vectors.py
//   - Output file: vectors/sample_cbd_input.hex

`timescale 1ns / 1ps

module tb_sample_cbd_xsim;

    // ================================================================
    // Parameters
    // ================================================================
    parameter VECTOR_FILE  = "sync_rtl/sample_cbd/TB/vectors/sample_cbd_input.hex";
    parameter RESULT_FILE  = "sync_rtl/sample_cbd/TB/vectors/sample_cbd_result.hex";
    parameter EXPECT_FILE  = "sync_rtl/sample_cbd/TB/vectors/sample_cbd_expected.hex";
    parameter MAX_VECTORS  = 32;
    parameter TIMEOUT_CYCLES = 10000;
    parameter N_COEFFS     = 256;

    // ================================================================
    // DUT signals
    // ================================================================
    reg              clk;
    reg              rst_n;
    reg  [255:0]     seed_i;
    reg  [7:0]       nonce_i;
    reg  [1:0]       eta_i;
    reg              valid_i;
    wire             ready_o;
    wire [11:0]      coeff_o;
    wire             valid_o;
    reg              ready_i;
    wire             last_o;

    // ================================================================
    // DUT instantiation
    // ================================================================
    sample_cbd_sync u_dut (
        .clk      (clk),
        .rst_n    (rst_n),
        .seed_i   (seed_i),
        .nonce_i  (nonce_i),
        .eta_i    (eta_i),
        .valid_i  (valid_i),
        .ready_o  (ready_o),
        .coeff_o  (coeff_o),
        .valid_o  (valid_o),
        .ready_i  (ready_i),
        .last_o   (last_o)
    );

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

    // ================================================================
    // Vector memory (loaded by $readmemh)
    // 266 bits per word: {eta_i[1:0], nonce_i[7:0], seed_i[255:0]}
    // Hex: 67 chars = 268 bits, top 2 bits zero-padded
    // ================================================================
    reg [267:0] vector_mem [0:MAX_VECTORS-1];
    integer     vec_count;
    integer     idx;
    integer     cycle_count;
    integer     result_fd;
    integer     coeff_idx;

    // Test result tracking
    integer     pass_count;
    integer     fail_count;

    // ================================================================
    // Hex-to-ASCII conversion helper (for output file)
    // ================================================================
    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
        vec_count = 0;

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

        // Count non-zero/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] === {268{1'bx}} || vector_mem[idx] === {268{1'bz}}))
                    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: <67 hex chars> = {eta, nonce, seed}");
            $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
        seed_i  <= 256'd0;
        nonce_i <= 8'd0;
        eta_i   <= 2'd2;
        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] vec_seed;
                reg [7:0]   vec_nonce;
                reg [1:0]   vec_eta;

                // Extract fields from packed vector_mem
                // vector_mem[267:0] = {2'b0, eta_i, nonce_i, seed_i}
                vec_seed  = vector_mem[idx][255:0];
                vec_nonce = vector_mem[idx][263:256];
                vec_eta   = vector_mem[idx][265:264];

                $display("INFO: Vector %0d - eta=%0d, nonce=0x%02h, seed[0:31]=%0h...",
                         idx, vec_eta, vec_nonce, vec_seed[31:0]);

                // Drive DUT with input
                seed_i  <= vec_seed;
                nonce_i <= vec_nonce;
                eta_i   <= vec_eta;
                valid_i <= 1'b1;
                @(posedge clk);
                valid_i <= 1'b0;

                // Wait for valid_o, then collect all 256 coefficients
                // The DUT uses ready/valid handshake; we set ready_i=1
                coeff_idx = 0;
                cycle_count = 0;

                // Write vector header to result file
                $fwrite(result_fd, "# VECTOR_%0d eta=%0d nonce=0x%02h seed=0x%064h\n",
                        idx, vec_eta, vec_nonce, vec_seed);

                while (coeff_idx < N_COEFFS && cycle_count < TIMEOUT_CYCLES) begin
                    @(posedge clk);
                    cycle_count = cycle_count + 1;

                    if (valid_o) begin
                        // Capture coefficient
                        begin
                            integer k;
                            reg [3:0] nib;
                            for (k = 2; k >= 0; k = k - 1) begin
                                nib = coeff_o[(k*4)+:4];
                                $fwrite(result_fd, "%c", nibble_to_ascii(nib));
                            end
                        end
                        coeff_idx = coeff_idx + 1;

                        if (last_o)
                            $fwrite(result_fd, "  # last at coeff_idx=%0d", coeff_idx);
                        $fwrite(result_fd, "\n");
                    end
                end

                if (cycle_count >= TIMEOUT_CYCLES) begin
                    $display("ERROR: Timeout on vector %0d (got %0d/%0d coefficients)",
                             idx, coeff_idx, N_COEFFS);
                    fail_count = fail_count + 1;
                end else if (coeff_idx != N_COEFFS) begin
                    $display("ERROR: Vector %0d incomplete (got %0d/%0d coefficients)",
                             idx, coeff_idx, N_COEFFS);
                    fail_count = fail_count + 1;
                end else begin
                    $display("INFO: Vector %0d PASSED (%0d coefficients in %0d cycles)",
                             idx, coeff_idx, cycle_count);
                    pass_count = pass_count + 1;
                end

                // Wait for DUT to return to IDLE before next vector
                @(posedge clk);
            end  // inner begin block
        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 (global)
    // ================================================================
    initial begin
        #(TIMEOUT_CYCLES * 10 * 100);  // TIMEOUT_CYCLES * 10ns * extra margin
        $display("FATAL: Global simulation timeout reached");
        $finish;
    end

endmodule
