`timescale 1ns / 1ps

module tb_kv_store;

    // Clock and Reset
    reg clk;
    reg rst;
    
    // Inputs
    reg enable;
    reg op;
    reg [7:0] key;
    reg [15:0] value_in;
    
    // Outputs
    wire [15:0] value_out;
    wire hit;
    
    // Instantiate DUT
    kv_store uut (
        .clk(clk),
        .rst(rst),
        .enable(enable),
        .op(op),
        .key(key),
        .value_in(value_in),
        .value_out(value_out),
        .hit(hit)
    );
    
    // Clock generation
    initial clk = 0;
    always #5 clk = ~clk;
    
    // Golden Vectors
    localparam NUM_TESTS = 25;
    reg        tb_op      [0:24];
    reg [7:0]  tb_key     [0:24];
    reg [15:0] tb_value_in[0:24];
    reg [15:0] exp_value  [0:24];
    reg        exp_hit    [0:24];
    
    integer i;
    integer errors = 0;
    
    initial begin
        // Test 0: GET empty
        tb_op[0] = 1'b0;    tb_key[0] = 8'h10;    tb_value_in[0] = 16'h0000;    exp_value[0] = 16'h0000;    exp_hit[0] = 1'b0;
        // Test 1: PUT key=0x10
        tb_op[1] = 1'b1;    tb_key[1] = 8'h10;    tb_value_in[1] = 16'h1234;    exp_value[1] = 16'h0000;    exp_hit[1] = 1'b1;
        // Test 2: PUT key=0x20
        tb_op[2] = 1'b1;    tb_key[2] = 8'h20;    tb_value_in[2] = 16'h5678;    exp_value[2] = 16'h0000;    exp_hit[2] = 1'b1;
        // Test 3: PUT key=0x30
        tb_op[3] = 1'b1;    tb_key[3] = 8'h30;    tb_value_in[3] = 16'habcd;    exp_value[3] = 16'h0000;    exp_hit[3] = 1'b1;
        // Test 4: PUT key=0x40
        tb_op[4] = 1'b1;    tb_key[4] = 8'h40;    tb_value_in[4] = 16'hef01;    exp_value[4] = 16'h0000;    exp_hit[4] = 1'b1;
        // Test 5: GET key=0x10
        tb_op[5] = 1'b0;    tb_key[5] = 8'h10;    tb_value_in[5] = 16'h0000;    exp_value[5] = 16'h1234;    exp_hit[5] = 1'b1;
        // Test 6: GET key=0x20
        tb_op[6] = 1'b0;    tb_key[6] = 8'h20;    tb_value_in[6] = 16'h0000;    exp_value[6] = 16'h5678;    exp_hit[6] = 1'b1;
        // Test 7: GET key=0x30
        tb_op[7] = 1'b0;    tb_key[7] = 8'h30;    tb_value_in[7] = 16'h0000;    exp_value[7] = 16'habcd;    exp_hit[7] = 1'b1;
        // Test 8: GET key=0x40
        tb_op[8] = 1'b0;    tb_key[8] = 8'h40;    tb_value_in[8] = 16'h0000;    exp_value[8] = 16'hef01;    exp_hit[8] = 1'b1;
        // Test 9: GET non-existent
        tb_op[9] = 1'b0;    tb_key[9] = 8'hff;    tb_value_in[9] = 16'h0000;    exp_value[9] = 16'h0000;    exp_hit[9] = 1'b0;
        // Test 10: PUT update key=0x10
        tb_op[10] = 1'b1;   tb_key[10] = 8'h10;   tb_value_in[10] = 16'h9999;   exp_value[10] = 16'h0000;   exp_hit[10] = 1'b1;
        // Test 11: GET updated key=0x10
        tb_op[11] = 1'b0;   tb_key[11] = 8'h10;   tb_value_in[11] = 16'h0000;   exp_value[11] = 16'h9999;   exp_hit[11] = 1'b1;
        // Test 12: PUT fill key=0x50
        tb_op[12] = 1'b1;   tb_key[12] = 8'h50;   tb_value_in[12] = 16'h1111;   exp_value[12] = 16'h0000;   exp_hit[12] = 1'b1;
        // Test 13: PUT fill key=0x60
        tb_op[13] = 1'b1;   tb_key[13] = 8'h60;   tb_value_in[13] = 16'h2222;   exp_value[13] = 16'h0000;   exp_hit[13] = 1'b1;
        // Test 14: PUT fill key=0x70
        tb_op[14] = 1'b1;   tb_key[14] = 8'h70;   tb_value_in[14] = 16'h3333;   exp_value[14] = 16'h0000;   exp_hit[14] = 1'b1;
        // Test 15: PUT fill key=0x80
        tb_op[15] = 1'b1;   tb_key[15] = 8'h80;   tb_value_in[15] = 16'h4444;   exp_value[15] = 16'h0000;   exp_hit[15] = 1'b1;
        // Test 16: PUT replace slot 0
        tb_op[16] = 1'b1;   tb_key[16] = 8'h90;   tb_value_in[16] = 16'h5555;   exp_value[16] = 16'h0000;   exp_hit[16] = 1'b1;
        // Test 17: GET evicted key=0x10
        tb_op[17] = 1'b0;   tb_key[17] = 8'h10;   tb_value_in[17] = 16'h0000;   exp_value[17] = 16'h0000;   exp_hit[17] = 1'b0;
        // Test 18: GET new key=0x90
        tb_op[18] = 1'b0;   tb_key[18] = 8'h90;   tb_value_in[18] = 16'h0000;   exp_value[18] = 16'h5555;   exp_hit[18] = 1'b1;
        // Test 19: PUT replace slot 1
        tb_op[19] = 1'b1;   tb_key[19] = 8'ha0;   tb_value_in[19] = 16'h6666;   exp_value[19] = 16'h0000;   exp_hit[19] = 1'b1;
        // Test 20: GET evicted key=0x20
        tb_op[20] = 1'b0;   tb_key[20] = 8'h20;   tb_value_in[20] = 16'h0000;   exp_value[20] = 16'h0000;   exp_hit[20] = 1'b0;
        // Test 21: GET surviving key=0x30
        tb_op[21] = 1'b0;   tb_key[21] = 8'h30;   tb_value_in[21] = 16'h0000;   exp_value[21] = 16'habcd;   exp_hit[21] = 1'b1;
        // Test 22: GET surviving key=0x40
        tb_op[22] = 1'b0;   tb_key[22] = 8'h40;   tb_value_in[22] = 16'h0000;   exp_value[22] = 16'hef01;   exp_hit[22] = 1'b1;
        // Test 23: GET surviving key=0x50
        tb_op[23] = 1'b0;   tb_key[23] = 8'h50;   tb_value_in[23] = 16'h0000;   exp_value[23] = 16'h1111;   exp_hit[23] = 1'b1;
        // Test 24: GET surviving key=0x60
        tb_op[24] = 1'b0;   tb_key[24] = 8'h60;   tb_value_in[24] = 16'h0000;   exp_value[24] = 16'h2222;   exp_hit[24] = 1'b1;

        // Initialize
        enable = 0;
        op = 0;
        key = 0;
        value_in = 0;
        rst = 1;
        
        // Reset sequence
        @(posedge clk);
        @(posedge clk);
        rst = 0;
        @(posedge clk);
        
        // Run tests
        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            // Apply inputs
            enable = 1;
            op = tb_op[i];
            key = tb_key[i];
            value_in = tb_value_in[i];
            
            @(posedge clk);
            #1; // Small delay for outputs to settle
            
            // Check outputs immediately (for GET operations, check value; for PUT, just check hit)
            if (tb_op[i] == 0) begin
                // GET: check both value and hit
                if (hit !== exp_hit[i]) begin
                    $display("ERROR [Test %0d GET]: Key=%h :: Expected hit=%b, Got=%b", 
                             i, tb_key[i], exp_hit[i], hit);
                    errors = errors + 1;
                end else if (hit == 1 && value_out !== exp_value[i]) begin
                    $display("ERROR [Test %0d GET]: Key=%h :: Expected value=%h, Got=%h", 
                             i, tb_key[i], exp_value[i], value_out);
                    errors = errors + 1;
                end
            end else begin
                // PUT: check hit (should always be 1)
                if (hit !== exp_hit[i]) begin
                    $display("ERROR [Test %0d PUT]: Key=%h :: Expected hit=%b, Got=%b", 
                             i, tb_key[i], exp_hit[i], hit);
                    errors = errors + 1;
                end
            end
        end
        
        // Final Results
        $display("");
        $display("===========================================");
        $display("  Tests Run: %0d", NUM_TESTS);
        $display("===========================================");
        
        if (errors == 0) begin
            $display("TEST_RESULT: PASS");
        end else begin
            $display("TEST_RESULT: FAIL (%0d errors)", errors);
        end
        
        $finish;
    end

endmodule
