`include "VX_platform.vh"

module VX_index_queue #(
    parameter DATAW = 1,
    parameter SIZE = 1
) (
    input  wire clk,
    input  wire reset,
    input  wire [DATAW-1:0] write_data,        
    output wire [`LOG2UP(SIZE)-1:0] write_addr,
    input  wire push,    
    input  wire pop,
    output wire full,
    output wire empty,
    input  wire [`LOG2UP(SIZE)-1:0] read_addr,
    output wire [DATAW-1:0] read_data
);
    reg [DATAW-1:0] entries [SIZE-1:0];
    reg [SIZE-1:0] valid;    
    reg [`LOG2UP(SIZE):0] rd_ptr, wr_ptr;

    wire [`LOG2UP(SIZE)-1:0] rd_a, wr_a;
    wire enqueue, dequeue;

    assign rd_a = rd_ptr[`LOG2UP(SIZE)-1:0];
    assign wr_a = wr_ptr[`LOG2UP(SIZE)-1:0];    

    assign empty = (wr_ptr == rd_ptr);
    assign full  = (wr_a == rd_a) && (wr_ptr[`LOG2UP(SIZE)] != rd_ptr[`LOG2UP(SIZE)]);

    assign enqueue = push;       
    assign dequeue = !empty && !valid[rd_a]; // auto-remove when head is invalid

    always @(*) begin
        assert(!push || !full);
    end

    always @(posedge clk) begin
        if (reset) begin
            rd_ptr <= 0;
            wr_ptr <= 0;
            valid  <= 0;     
        end else begin
            if (enqueue)  begin
                valid[wr_a] <= 1;
                wr_ptr      <= wr_ptr + 1;
            end            
            if (dequeue) begin
                rd_ptr <= rd_ptr + 1;
            end
            if (pop) begin
                valid[read_addr] <= 0;
            end
        end

        if (enqueue)  begin
            entries[wr_a] <= write_data;
        end  
    end

    assign write_addr = wr_a;
    assign read_data = entries[read_addr];

endmodule