// tb_sha3_xsim.v - Standard Verilog testbench for sha3_top targeting Vivado xsim
//
// Reads test vectors from a hex file using $readmemh.
// Each line is a single hex number encoding both mode and data:
//   - Upper 8 bits [519:512]: mode[1:0] in bits [513:512]
//   - Lower 512 bits [511:0]: data_i
//   - Total: 130 hex chars per line, NO spaces
//
// Drives sha3_top, waits for valid_o, and writes "RESULT: MODE HASH_HEX"
// to the output file using $fwrite.
//
// Parameters:
//   VECTOR_FILE - path to input hex file (default: "vectors/g_basic_input.hex")
//   RESULT_FILE - path to output file    (default: "vectors/g_basic_result.hex")
//
// Usage:
//   xvlog -sv sha3_top.v tb_sha3_xsim.v
//   xelab tb_sha3_xsim -s tb_sha3_xsim
//   xsim tb_sha3_xsim -R

`timescale 1ns / 1ps

module tb_sha3_xsim;

    // ================================================================
    // Parameters
    // ================================================================
    parameter VECTOR_FILE = "sync_rtl/sha3/TB/vectors/g_basic_input.hex";
    parameter EXPECTED_FILE = "sync_rtl/sha3/TB/vectors/g_basic_expected.hex";
    parameter RESULT_FILE = "sync_rtl/sha3/TB/vectors/g_basic_result.hex";
    parameter MAX_VECTORS = 256;
    parameter TIMEOUT_CYCLES = 1000;

    // ================================================================
    // DUT signals
    // ================================================================
    reg              clk;
    reg              rst_n;
    reg  [1:0]       mode;
    reg  [511:0]     data_i;
    reg              valid_i;
    wire             ready_o;
    wire [511:0]     hash_o;
    wire             valid_o;
    reg              ready_i;

    // ================================================================
    // DUT instantiation
    // ================================================================
    sha3_top u_dut (
        .clk      (clk),
        .rst_n    (rst_n),
        .mode     (mode),
        .data_i   (data_i),
        .valid_i  (valid_i),
        .ready_o  (ready_o),
        .hash_o   (hash_o),
        .valid_o  (valid_o),
        .ready_i  (ready_i),
        // multi-block absorb path disabled for single-block G/H/J tests
        .mb_en      (1'b0),
        .mb_block_i (1088'b0),
        .mb_valid_i (1'b0),
        .mb_last_i  (1'b0),
        .mb_ready_o ()
    );

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

    // ================================================================
    // Vector memory (loaded by $readmemh)
    // 520 bits per word: bits[519:512]=padding+mode, bits[511:0]=data_i
    // ================================================================
    reg [519:0] vector_mem [0:MAX_VECTORS-1];
    reg [511:0] expected_mem [0:MAX_VECTORS-1];   // expected hash per vector
    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);
        // Load expected hashes (one 512-bit hex per line, MSB-first)
        $readmemh(EXPECTED_FILE, expected_mem);

        // Count non-zero entries to determine actual vector count
        // (XSim leaves unloaded entries as 520'hX)
        begin
            integer found_end;
            found_end = 0;
            for (idx = 0; idx < MAX_VECTORS; idx = idx + 1) begin
                if (!found_end && (vector_mem[idx] === 520'hx || vector_mem[idx] === 520'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: <130 hex chars> = {8-bit mode_header, 512-bit data}");
            $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
        mode    <= 2'd0;
        data_i  <= 512'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
            // Extract mode and data from memory word
            // mode in bits [513:512], data in bits [511:0]
            begin
                reg [1:0]   vec_mode;
                reg [511:0] vec_data;
                reg [511:0] captured_hash;

                vec_mode = vector_mem[idx][513:512];
                vec_data = vector_mem[idx][511:0];

            $display("INFO: Vector %0d - mode=%0d", idx, vec_mode);

            // Drive DUT
            mode    <= vec_mode;
            data_i  <= vec_data;
            valid_i <= 1'b1;
            @(posedge clk);
            valid_i <= 1'b0;

            // Wait for ready_o (DUT enters PERMUTE state on this cycle)
            // Then wait for valid_o asserted
            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 hash output and self-check against expected.
                // G (mode 0) uses all 512 bits; H/J use the low 256 bits.
                captured_hash = hash_o;

                begin
                    reg [511:0] exp_hash;
                    reg         match;
                    exp_hash = expected_mem[idx];
                    if (vec_mode == 2'd0)
                        match = (captured_hash === exp_hash);
                    else
                        match = (captured_hash[255:0] === exp_hash[255:0]);

                    if (match) begin
                        pass_count = pass_count + 1;
                        $display("PASS: Vector %0d (mode=%0d)", idx, vec_mode);
                    end else begin
                        fail_count = fail_count + 1;
                        $display("FAIL: Vector %0d (mode=%0d) hash mismatch", idx, vec_mode);
                        $display("  got = %0h", (vec_mode==2'd0) ? captured_hash : {256'd0, captured_hash[255:0]});
                        $display("  exp = %0h", (vec_mode==2'd0) ? exp_hash      : {256'd0, exp_hash[255:0]});
                    end
                end

                // Write result to output file
                // Format: "RESULT: MODE HASH_HEX"
                $fwrite(result_fd, "RESULT: %0d ", vec_mode);
                // Write hash as hex (128 chars for 512 bits)
                begin
                    integer bit_idx;
                    reg [3:0] nib;
                    for (bit_idx = 127; bit_idx >= 0; bit_idx = bit_idx - 1) begin
                        nib = captured_hash[(bit_idx*4)+:4];
                        $fwrite(result_fd, "%c", nibble_to_ascii(nib));
                    end
                end
                $fwrite(result_fd, "\n");
            end

            // One extra cycle for valid_o handshake
            @(posedge clk);
            end  // inner begin block for variable scope
        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("========================================");
        if (fail_count == 0)
            $display("ALL TESTS PASSED (%0d/%0d)", pass_count, vec_count);
        else
            $display("TESTS FAILED: %0d of %0d", fail_count, vec_count);
        $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
