// Amazon FPGA Hardware Development Kit
//
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Amazon Software License (the "License"). You may not use
// this file except in compliance with the License. A copy of the License is
// located at
//
//    http://aws.amazon.com/asl/
//
// or in the "license" file accompanying this file. This file is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or
// implied. See the License for the specific language governing permissions and
// limitations under the License.

// flop_fifo_in : Flops the input data right away

module flop_fifo_in #(// --------------------------------------------------
                         // Overridable Parameters
                         // --------------------------------------------------
                         parameter WIDTH      = 32,
                         parameter DEPTH      = 3,
                         parameter OUTPUT_REG = 0
                         )
   (
   // --------------------------------------------------
   // Ports
   // --------------------------------------------------
    input              clk,
    input              rst_n,
    input              sync_rst_n,
    input [31:0]       cfg_watermark,
    input              push,
    input [WIDTH-1:0]  push_data,
    input              pop,
    output [WIDTH-1:0] pop_data,
    output             data_valid,
    output             half_full,
    output             watermark
    );

   // --------------------------------------------------
   // Local Parameters
   // --------------------------------------------------

   // --------------------------------------------------
   // Wires and registers
   // --------------------------------------------------

   logic               ss_data_valid;
   logic [WIDTH-1:0]   ss_pop_data;

   logic               ff_data_valid;
   logic [WIDTH-1:0]   ff_pop_data;
   logic [WIDTH-1:0]   ff_fifo [DEPTH-1:0];
   logic [DEPTH-1:0]   ff_valid;
   
   // For every push, keep pushing data into the ff_fifo array
   // For pop's there is no change
`ifdef FPGA_LESS_RST
   always_ff @(posedge clk)
     if (!sync_rst_n) begin
`else   
   always_ff @(posedge clk or negedge rst_n)
     if (!rst_n) begin
        ff_fifo <= '{default:'0};
     end
     else if (!sync_rst_n) begin
`endif
        ff_fifo <= '{default:'0};
     end
     else begin
       for (int i = 0; i < DEPTH; i++) begin
         if (push) begin
            if (i == 0)
              ff_fifo[i] <= push_data;
            else
              ff_fifo[i] <= ff_fifo[i-1];
         end
         else
           ff_fifo[i] <= ff_fifo[i];
       end // for (int i = 0; i < DEPTH; i++)
     end // else: !if(!rst_n)

   // For push & ~pop, valid[0] gets 1. every thing else shifts.
   // For ~push & pop, the most significant valid gets invalidated
   // For push & pop, valid array stays the same
   always_ff @(posedge clk or negedge rst_n)
     if (!rst_n) begin
        ff_valid <= '{default:'0};
     end
     else if (!sync_rst_n) begin
        ff_valid <= '{default:'0};
     end
     else begin
       for (int i = 0; i < DEPTH; i++) begin
         if (push & ~pop) begin
            if (i == 0)
              ff_valid[i] <= 1'b1;
            else
              ff_valid[i] <= ff_valid[i-1];
         end
         else if (~push & pop) begin
            if (i == DEPTH - 1)
              ff_valid[i] <= 1'b0;
            else
              ff_valid[i] <= ff_valid[i+1] ? 1'b1 : 1'b0;
         end
         else
            ff_valid[i] <= ff_valid[i];
       end // for (int i = 0; i < DEPTH; i++)
     end // else: !if(!rst_n)

   // Output data is the most significant data (oldest data)
   always_comb begin
      ss_pop_data = ({WIDTH{1'b0}});
      for (int i = 0; i < DEPTH; i++) begin
         if (ff_valid[i]) 
           ss_pop_data = ff_fifo[i];
      end
   end
   
   // Data is valid as long as entry 0 is valid
   assign ss_data_valid = ff_valid[0];

   always_ff @(posedge clk or negedge rst_n)
     if (!rst_n) begin
        ff_pop_data <= '{default:'0};
        ff_data_valid <= 1'b0;
     end
     else if (!sync_rst_n) begin
        ff_pop_data <= '{default:'0};
        ff_data_valid <= 1'b0;
     end
     else begin
        ff_pop_data <= ss_pop_data;
        ff_data_valid <= ss_data_valid;
     end

   generate
      if (OUTPUT_REG) begin
         assign pop_data = ff_pop_data;
         assign data_valid = ff_data_valid;
      end
      else begin
         assign pop_data = ss_pop_data;
         assign data_valid = ss_data_valid;
      end
   endgenerate

   assign half_full = ff_valid[((DEPTH >> 1)-1)];
   assign watermark = ff_valid[cfg_watermark];

endmodule // epipe_flop_fifo