// Key-Value Store - Reference Solution
// 8-entry cache with GET/PUT operations and round-robin replacement.

module kv_store (
    input  wire        clk,
    input  wire        rst,
    input  wire        enable,
    input  wire        op,           // 0=GET, 1=PUT
    input  wire [7:0]  key,
    input  wire [15:0] value_in,
    output reg  [15:0] value_out,
    output reg         hit
);

    // Storage: 8 entries
    reg        valid  [0:7];  // Entry valid flags
    reg [7:0]  keys   [0:7];  // Stored keys
    reg [15:0] values [0:7];  // Stored values
    
    // Round-robin replacement pointer
    reg [2:0] next_slot;
    
    // Key match signals (combinational)
    wire [7:0] key_match;
    genvar g;
    generate
        for (g = 0; g < 8; g = g + 1) begin : match_gen
            assign key_match[g] = valid[g] && (keys[g] == key);
        end
    endgenerate
    
    // Find first empty slot (priority encoder)
    wire [7:0] empty_slot;
    assign empty_slot[0] = ~valid[0];
    assign empty_slot[1] = ~valid[1] & valid[0];
    assign empty_slot[2] = ~valid[2] & valid[1] & valid[0];
    assign empty_slot[3] = ~valid[3] & valid[2] & valid[1] & valid[0];
    assign empty_slot[4] = ~valid[4] & valid[3] & valid[2] & valid[1] & valid[0];
    assign empty_slot[5] = ~valid[5] & valid[4] & valid[3] & valid[2] & valid[1] & valid[0];
    assign empty_slot[6] = ~valid[6] & valid[5] & valid[4] & valid[3] & valid[2] & valid[1] & valid[0];
    assign empty_slot[7] = ~valid[7] & valid[6] & valid[5] & valid[4] & valid[3] & valid[2] & valid[1] & valid[0];
    
    wire store_full = valid[0] & valid[1] & valid[2] & valid[3] & 
                      valid[4] & valid[5] & valid[6] & valid[7];
    
    // Match index encoder
    wire [2:0] match_idx;
    assign match_idx = key_match[0] ? 3'd0 :
                       key_match[1] ? 3'd1 :
                       key_match[2] ? 3'd2 :
                       key_match[3] ? 3'd3 :
                       key_match[4] ? 3'd4 :
                       key_match[5] ? 3'd5 :
                       key_match[6] ? 3'd6 : 3'd7;
                       
    // Empty index encoder
    wire [2:0] empty_idx;
    assign empty_idx = empty_slot[0] ? 3'd0 :
                       empty_slot[1] ? 3'd1 :
                       empty_slot[2] ? 3'd2 :
                       empty_slot[3] ? 3'd3 :
                       empty_slot[4] ? 3'd4 :
                       empty_slot[5] ? 3'd5 :
                       empty_slot[6] ? 3'd6 : 3'd7;
    
    wire any_match = |key_match;
    wire any_empty = |empty_slot;
    
    integer i;
    
    always @(posedge clk) begin
        if (rst) begin
            // Reset all entries
            for (i = 0; i < 8; i = i + 1) begin
                valid[i]  <= 1'b0;
                keys[i]   <= 8'b0;
                values[i] <= 16'b0;
            end
            next_slot <= 3'd0;
            value_out <= 16'b0;
            hit <= 1'b0;
        end else if (enable) begin
            if (op == 1'b0) begin
                // GET operation
                if (any_match) begin
                    value_out <= values[match_idx];
                    hit <= 1'b1;
                end else begin
                    value_out <= 16'b0;
                    hit <= 1'b0;
                end
            end else begin
                // PUT operation
                if (any_match) begin
                    // Key exists: update value in-place
                    values[match_idx] <= value_in;
                    hit <= 1'b1;
                end else if (any_empty) begin
                    // Key is new: insert in first empty slot
                    valid[empty_idx]  <= 1'b1;
                    keys[empty_idx]   <= key;
                    values[empty_idx] <= value_in;
                    hit <= 1'b1;
                end else begin
                    // Store is full: round-robin replacement
                    keys[next_slot]   <= key;
                    values[next_slot] <= value_in;
                    next_slot <= next_slot + 1;  // Wraps automatically (3-bit)
                    hit <= 1'b1;
                end
            end
        end
    end

endmodule
