module mem_cmd_fifo #(parameter integer WIDTH = 550) (
    input              clock                   // Clock - samples & launches data on rising edge
  , input              reset                   // When 1, reset registers to an empty FIFO state

  , input  [WIDTH-1:0] data_in                 // Vector of command information to save
  , input              data_in_valid           // When 1, load 'resp_in' into the FIFO
  , output [3:0]       data_buffers_available  // Number of buffer slots available (1 buffer holds response and data FLIT (if used))
  , output             data_fifo_full          // : when 0 Fifo is full -  active low
    // Output from FIFO
  , input              data_sent               // When 1, increment read FIFO pointer to present the next FIFO entry
  , output [WIDTH-1:0] data_out                // Command information at head of FIFO
  , output             data_out_valid          // When 1, 'cmd_out' contains valid information
 
    // Error conditions
  , output             fifo_overflow           // When 1, error FIFO was full when another 'data_valid' arrived
) ;


// ==============================================================================================================================
// @@@  Implement FIFO
// ==============================================================================================================================

reg [WIDTH-1:0] datafifo [7:0];   // 16 row array

reg       [2:0] wrptr;             // Write pointer into array, points to next free entry
reg       [2:0] rdptr;             // Read  pointer from array, points to oldest valid entry

reg      [7:0] datafifo_val;      // 1=associated entry is used, 0=associated entry is open and can be overwritten

wire fifo_full;
assign fifo_full = (datafifo_val == 8'hFF) ? 1'b1 : 1'b0;

// Manage write pointer
always @(posedge(clock))
  begin
    if (reset == 1'b1) 
      wrptr <= 3'b000;                                      // Don't allow it to become same with read
    else if (data_in_valid == 1'b1 && wrptr != 3'b111 && ((fifo_full == 1'b0) || ((fifo_full == 1'b1) && (data_sent == 1'b1))))  //as long as not full   
      wrptr <= wrptr + 3'b001;                              // Add an entry, so increment
    else if (data_in_valid == 1'b1 && wrptr == 3'b111  && ((fifo_full == 1'b0) || ((fifo_full == 1'b1) && (data_sent == 1'b1))))   
      wrptr <= 3'b000;                                      // Add an entry, but wrap around to increment
    else
      wrptr <= wrptr;                                        // Hold value, nothing to load
  end



// Manage read pointer never overtake or be equal to  write pointer 
always @(posedge(clock))
  begin
    if (reset == 1'b1)
      rdptr <= 3'b000;                                      // Initialize to location 0
    else if (data_sent == 1'b1 && rdptr != 3'b111)
      rdptr <= rdptr + 3'b001;                              // Move to next location, this one is complete
    else if (data_sent == 1'b1 && rdptr == 3'b111)
      rdptr <= 3'b000;                                      // Move to next location, but wrap around
    else
      rdptr <= rdptr;                                        // Hold value, no operation or operation is still in progress
  end

// Manage valid indicator
always @(posedge(clock))
  begin
    if (reset == 1'b1)  
      datafifo_val <= 8'h00;                                                     // Initialize to all 0, all rows are free
    else if (data_in_valid == 1'b1 && data_sent == 1'b0 && (fifo_full == 1'b0)) //if full is 1 (i.e. table is full, do not receive.  
      datafifo_val <= datafifo_val | (8'h01 << wrptr);                           // When loading, set wrptr bit to 1
    else if (data_in_valid == 1'b0 && data_sent == 1'b1)
      datafifo_val <= datafifo_val & ~(8'h01 << rdptr);                          // When done, set rdptr bit to 0 
    else if (data_in_valid == 1'b1 && data_sent == 1'b1 && (fifo_full == 1'b0)) //receive and send - fifo should not be full to apply these else val will be incorrectly updated
      datafifo_val <= (datafifo_val | (8'h01 << wrptr)) & ~(8'h01 << rdptr);  // Both set and clear a bit 
    else
      datafifo_val <= datafifo_val;                                                 // Hold value if neither bit is set
  end


// Manage row contents
// Note: Contents of array row will be 'X' until written the first time, but this should be OK.
//As long as it is full don't accept anything. improve to implemente a  moving full window.  
always @(posedge(clock)) begin
  if ((data_in_valid == 1'b1 && ((fifo_full == 1'b0) || ((fifo_full == 1'b1) && (data_sent == 1'b1))))) datafifo[wrptr] <= data_in;   // Use code format recommended by Vivado
end
//  datafifo[wrptr] <= (resp_in_valid == 1'b1) ? resp_in : respfifo[wrptr];  // Load or hold array row

// Manage data output and valid. If valid is not set, drive 0's which should decode to NOP.
// Note: DISTRIBUTED arrays do not latch the output.
// Provide data directly out of array (if timing permits) to align with changing of rdptr with dataect to resp_sent
assign data_out = (datafifo_val[rdptr] == 1'b1) ? datafifo[rdptr] : {WIDTH{1'b0}};  

// Provide data directly out of array (if timing permits) to align with changing of rdptr with dataect to resp_sent
assign data_out_valid = datafifo_val[rdptr];  

//fifo_full signal added active low _n
assign data_fifo_full = ((fifo_full == 1'b0) || ((fifo_full == 1'b1) && (data_sent == 1'b1))) ? 1'b1 : 1'b0;

// Check for overflow
assign fifo_overflow   = 1'b0;//(data_in_valid == 1'b1 && datafifo_val == 16'hFFFF) ? 1'b1 : 1'b0;

// Determine number of available buffers
// Note: wrptr points to the next location to write (one past the last written entry)
//       rdptr points to the next valid location
reg [3:0] data_buffers_used;
always @(*)   // Combinational
  if (datafifo_val == 8'h0000)               // Empty FIFO
    data_buffers_used = 4'b0000; 
  else if (wrptr > rdptr)                     // Pointers are not wrapped with dataect to each other
    data_buffers_used = {1'b0,wrptr} - {1'b0,rdptr};      // Bit extension circumvents IBM 'tvc' tool warnings 
  else if (wrptr < rdptr)                     // Write pointer wrapped around
    data_buffers_used = (4'b1000 + {1'b0,wrptr}) - {1'b0,rdptr};   
  else // wrptr == rdptr                      // Pointers are equal, but FIFO is not empty so it must be full
    data_buffers_used = 4'b1000; 
assign data_buffers_available = 4'b1000 - data_buffers_used;

endmodule