/*****************************************************************************
 * Function: Synchronous FIFO
 * Copyright: Lambda Project Authors. ALl rights Reserved.
 * License:  MIT (see LICENSE file in Lambda repository)
 *
 * Docs:
 *
 * This is a wrapper for selecting from a set of hardened memory macros.
 *
 ****************************************************************************/

module la_syncfifo #(
    parameter DW    = 32,        // Memory width
    parameter DEPTH = 4,         // FIFO depth
    parameter NS    = 1,         // Number of power supplies
    parameter CHAOS = 1,         // generates random full logic when set
    parameter CTRLW = 1,         // width of asic ctrl interface
    parameter TESTW = 1,         // width of asic test interface
    parameter TYPE  = "DEFAULT"  // Pass through variable for hard macro
) (  // common clock, reset, power, ctrl
    input              clk,
    input              nreset,
    input              vss,        // ground signal
    input  [   NS-1:0] vdd,        // supplies
    input              chaosmode,  // randomly assert fifo full when set
    input  [CTRLW-1:0] ctrl,       // pass through ASIC control interface
    input  [TESTW-1:0] test,       // pass through ASIC test interface
    // write input
    input              wr_en,      // write fifo
    input  [   DW-1:0] wr_din,     // data to write
    output             wr_full,    // fifo full
    // read output
    input              rd_en,      // read fifo
    output [   DW-1:0] rd_dout,    // output data
    output             rd_empty    // fifo is empty
);

    // local params
    parameter AW = $clog2(DEPTH);

    // local wires
    reg  [AW:0] wr_addr;
    wire [AW:0] wr_addr_nxt;
    reg  [AW:0] rd_addr;
    wire [AW:0] rd_addr_nxt;
    wire        fifo_read;
    wire        fifo_write;
    wire        chaosfull;
    wire        rd_wrap_around;
    wire        wr_wrap_around;

    //############################
    // FIFO Empty/Full
    //############################

    // support any fifo depth
    assign wr_full = (chaosfull & chaosmode) | {~wr_addr[AW], wr_addr[AW-1:0]} == rd_addr[AW:0];

    assign rd_empty = wr_addr[AW:0] == rd_addr[AW:0];

    assign fifo_read = rd_en & ~rd_empty;

    assign fifo_write = wr_en & ~wr_full;

    //############################
    // FIFO Pointers - wrap around DEPTH-1
    //############################
    assign rd_wrap_around = rd_addr[AW-1:0] == (DEPTH[AW-1:0] - 1'b1);
    assign wr_wrap_around = wr_addr[AW-1:0] == (DEPTH[AW-1:0] - 1'b1);

    assign rd_addr_nxt[AW] = rd_wrap_around ? ~rd_addr[AW] : rd_addr[AW];
    assign rd_addr_nxt[AW-1:0] = rd_wrap_around ? 'b0 : (rd_addr[AW-1:0] + 1);

    assign wr_addr_nxt[AW]     = (wr_addr[AW-1:0] == (DEPTH[AW-1:0]-1'b1)) ? ~wr_addr[AW] : wr_addr[AW];
    assign wr_addr_nxt[AW-1:0] = (wr_addr[AW-1:0] == (DEPTH[AW-1:0]-1'b1)) ? 'b0 : (wr_addr[AW-1:0] + 1);

    always @(posedge clk or negedge nreset)
        if (~nreset) begin
            wr_addr[AW:0] <= 'd0;
            rd_addr[AW:0] <= 'b0;
        end else if (fifo_write & fifo_read) begin
            wr_addr[AW:0] <= wr_addr_nxt[AW:0];
            rd_addr[AW:0] <= rd_addr_nxt[AW:0];
        end else if (fifo_write) begin
            wr_addr[AW:0] <= wr_addr_nxt[AW:0];
        end else if (fifo_read) begin
            rd_addr[AW:0] <= rd_addr_nxt[AW:0];
        end

    //###########################
    //# Dual Port Memory
    //###########################

    reg [DW-1:0] ram[DEPTH-1:0];

    // Write port (FIFO input)
    always @(posedge clk) if (wr_en & ~wr_full) ram[wr_addr[AW-1:0]] <= wr_din[DW-1:0];

    // Read port (FIFO output)
    assign rd_dout[DW-1:0] = ram[rd_addr[AW-1:0]];


    //############################
    // Randomly Asserts FIFO full
    //############################

    generate
        if (CHAOS) begin
            // TODO: implement LFSR
            reg chaosreg;
            always @(posedge clk or negedge nreset)
                if (~nreset) chaosreg <= 1'b0;
                else chaosreg <= ~chaosreg;
            assign chaosfull = chaosreg;
        end else begin
            assign chaosfull = 1'b0;
        end
    endgenerate

endmodule
