// Copyright © 2019-2023
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

`include "VX_platform.vh"

module VX_ipdom_stack #(
    parameter WIDTH   = 1,
    parameter DEPTH   = 1,
    parameter OUT_REG = 0,
    parameter ADDRW   = `LOG2UP(DEPTH)
) (
    input  wire             clk,
    input  wire             reset,
    input  wire [WIDTH-1:0] q0,
    input  wire [WIDTH-1:0] q1,
    output wire [WIDTH-1:0] d,
    output wire             d_set,    
    input  wire             push,
    input  wire             pop,    
    output wire             empty,
    output wire             full
);
    reg slot_set [DEPTH-1:0];
    
    reg [ADDRW-1:0] rd_ptr, wr_ptr;

    reg empty_r, full_r;

    wire [WIDTH-1:0] d0, d1;

    wire d_set_n = slot_set[rd_ptr];

    always @(posedge clk) begin
        if (reset) begin   
            rd_ptr  <= '0;
            wr_ptr  <= '0;
            empty_r <= 1;
            full_r  <= 0; 
        end else begin
            `ASSERT(~push || ~full, ("runtime error: writing to a full stack!"));
            `ASSERT(~pop || ~empty, ("runtime error: reading an empty stack!"));
            `ASSERT(~push || ~pop,  ("runtime error: push and pop in same cycle not supported!"));
            if (push) begin                
                rd_ptr  <= wr_ptr;
                wr_ptr  <= wr_ptr + ADDRW'(1);                
                empty_r <= 0;
                full_r  <= (ADDRW'(DEPTH-1) == wr_ptr);
            end else if (pop) begin                   
                wr_ptr  <= wr_ptr - ADDRW'(d_set_n);
                rd_ptr  <= rd_ptr - ADDRW'(d_set_n);
                empty_r <= (rd_ptr == 0) && (d_set_n == 1);
                full_r  <= 0;
            end
        end
    end    

    VX_dp_ram #(
        .DATAW   (WIDTH * 2),
        .SIZE    (DEPTH),
        .OUT_REG (OUT_REG ? 1 : 0),
        .LUTRAM  (OUT_REG ? 0 : 1)
    ) store (
        .clk   (clk),
        .read  (1'b1),
        .write (push),        
        `UNUSED_PIN (wren),               
        .waddr (wr_ptr),
        .wdata ({q1, q0}),
        .raddr (rd_ptr),
        .rdata ({d1, d0})
    );
    
    always @(posedge clk) begin
        if (push) begin
            slot_set[wr_ptr] <= 0;   
        end else if (pop) begin            
            slot_set[rd_ptr] <= 1;
        end
    end

    wire d_set_r;
    VX_pipe_register #(
        .DATAW (1),
        .DEPTH (OUT_REG)
    ) pipe_reg (
        .clk      (clk),
        .reset    (reset),
        .enable   (1'b1),
        .data_in  (d_set_n),
        .data_out (d_set_r)
    );

    assign d     = d_set_r ? d0 : d1;
    assign d_set = ~d_set_r;
    assign empty = empty_r;
    assign full  = full_r;

endmodule
