// tb_mod_add_xsim.v - Standard Verilog testbench for mod_add_sync targeting Vivado xsim
//
// Reads test vectors from a hex file using $readmemh.
// Each line is a single 24-bit hex number encoding:
//   bits [23:12] = a, bits [11:0] = b
//
// Drives mod_add_sync, waits for valid_o, and writes
// "RESULT: IDX A B SUM_HEX" to the output file using $fwrite.
//
// Parameters:
//   VECTOR_FILE - path to input hex file (default: "vectors/mod_add_input.hex")
//   RESULT_FILE - path to output file    (default: "vectors/mod_add_result.hex")
//
// Usage:
//   xvlog -sv sync_rtl/common/pipeline_reg.v
//   xvlog -sv sync_rtl/mod_add/mod_add_sync.v
//   xvlog -sv sync_rtl/mod_add/TB/tb_mod_add_xsim.v
//   xelab tb_mod_add_xsim -s tb_mod_add_xsim
//   xsim tb_mod_add_xsim -R

`timescale 1ns / 1ps

module tb_mod_add_xsim;

    // ================================================================
    // Parameters
    // ================================================================
    parameter VECTOR_FILE = "sync_rtl/mod_add/TB/vectors/mod_add_input.hex";
    parameter RESULT_FILE = "sync_rtl/mod_add/TB/vectors/mod_add_result.hex";
    parameter MAX_VECTORS = 256;
    parameter TIMEOUT_CYCLES = 1000;

    // ================================================================
    // DUT signals
    // ================================================================
    reg              clk;
    reg              rst_n;
    reg  [11:0]      a;
    reg  [11:0]      b;
    reg              valid_i;
    wire             ready_o;
    wire [11:0]      sum;
    wire             valid_o;
    reg              ready_i;

    // ================================================================
    // DUT instantiation
    // ================================================================
    mod_add_sync u_dut (
        .clk     (clk),
        .rst_n   (rst_n),
        .a       (a),
        .b       (b),
        .valid_i (valid_i),
        .ready_o (ready_o),
        .sum     (sum),
        .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)
    // ================================================================
    reg [23: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 3-hex-digit (12-bit) value to file
    task write_hex_12bit;
        input integer fd;
        input [11:0] val;
        reg [3:0] nib;
        integer j;
        begin
            for (j = 2; 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] === 24'hx || vector_mem[idx] === 24'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: <6 hex chars> = {a[11:0], b[11:0]}");
            $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
        a       <= 12'd0;
        b       <= 12'd0;
        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_a;
                reg [11:0] vec_b;
                reg [11:0] captured_sum;

                // Extract a and b from memory word
                vec_a = vector_mem[idx][23:12];
                vec_b = vector_mem[idx][11:0];

                $display("INFO: Vector %0d - a=%0d b=%0d", idx, vec_a, vec_b);

                // Drive DUT
                a       <= vec_a;
                b       <= vec_b;
                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);
                    fail_count = fail_count + 1;
                end else begin
                    // Capture sum output
                    captured_sum = sum;
                    pass_count = pass_count + 1;

                    // Write result to output file
                    // Format: "RESULT: IDX A_HEX B_HEX SUM_HEX"
                    $fwrite(result_fd, "RESULT: %0d ", idx);
                    write_hex_12bit(result_fd, vec_a);
                    $fwrite(result_fd, " ");
                    write_hex_12bit(result_fd, vec_b);
                    $fwrite(result_fd, " ");
                    write_hex_12bit(result_fd, captured_sum);
                    $fwrite(result_fd, "\n");
                end

                // One extra cycle for valid_o handshake
                @(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
