---------------------------------------------------------------------------
-- File           : ea_exp_fifo_avalon_st.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : March 16th, 2019
---------------------------------------------------------------------------
-- Description    : This module sotres prepared data for scoreboard
---------------------------------------------------------------------------
--
---------------------------------------------------------------------------
library ieee; 
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_parameters_and_constants.all;
   use work.p_functions.all;
   use work.p_interfaces.all;
   use work.p_patt_mon_interface.all;

entity ea_exp_fifo_avalon_st is 
   port(clk             : in std_logic;
      reset             : in std_logic;
      -- to input monitor
      patt_muxi         : in  TYPE_PATT_MUX_TO_EXP_FIFO_AVALON;
      -- to lfsr monitor
      lfsro             : out TYPE_EXP_FIFO_AVALON_TO_LFSR;
      -- to linear monitor
      linearo           : out TYPE_EXP_FIFO_AVALON_TO_LINEAR;
      -- to/from scoreboard
      scoreboardi       : in  TYPE_AVALON_ST_SINK_TO_SOURCE;
      scoreboardo       : out TYPE_AVALON_ST_SOURCE_TO_SINK 
   );
end ea_exp_fifo_avalon_st;


architecture rtl of ea_exp_fifo_avalon_st is  

   constant C_SOP_EOP_WIDTH              : natural := 2;
   constant C_FIFO_DEPTH                 : natural := C_FIFO_AVALON_ST_DEPTH;

   -----------------------------------------------------------------------------
   -- Dual port RAM, this memory will include data width, sop and eop 
   -----------------------------------------------------------------------------
   subtype TYPE_DPRAM_ELEMENT is std_logic_vector(C_SOP_EOP_WIDTH+C_AVALON_MM_DATA_WIDTH-1 downto 0);
   type TYPE_DPRAM is array (C_FIFO_DEPTH-1 downto 0) of TYPE_DPRAM_ELEMENT;
   signal ram       : TYPE_DPRAM := (others => TYPE_DPRAM_ELEMENT'(others => '-'));

   type TYPE_REGS is record
      fifo_full                          : boolean;
      almost_fifo_full                   : boolean;
      fifo_empty                         : boolean;
      data_out                           : std_logic_vector (C_AVALON_MM_DATA_WIDTH-1 downto 0);
      data_valid                         : boolean;
      sop                                : std_logic;
      eop                                : std_logic;
      mux_sop                            : boolean;
      mux_eop                            : boolean;
      write_index                        : natural;
      read_index                         : natural;
   end record TYPE_REGS;

   constant CONST_REGS_RESET : TYPE_REGS := (
      fifo_full                           => false,
      almost_fifo_full                    => false,
      fifo_empty                          => false,
      data_out                            => (others => '0'),
      data_valid                          => false,
      sop                                 => '0',
      eop                                 => '0',
      mux_sop                             => false,
      mux_eop                             => false,
      write_index                         => 0,
      read_index                          => 0
   );

   signal r : TYPE_REGS;

begin

   -----------------------------------------------------------------------------
   -- Dual port RAM (FIFO Avalon mm slave)
   -----------------------------------------------------------------------------
   seq : process (reset, clk)
      variable s               : TYPE_REGS;

   begin

      if reset = '1' then
         r                               <= CONST_REGS_RESET;

      elsif rising_edge(clk) then

         s                               := r;
         s.data_valid                    := false;
         s.mux_sop                       := patt_muxi.start_of_packet;
         s.mux_eop                       := patt_muxi.end_of_packet;

         -- write data into fifo (ram) memory
         if patt_muxi.req and not s.fifo_full then  
            ram(r.write_index)(C_AVALON_MM_DATA_WIDTH-1 downto 0) <= patt_muxi.data;

            -- write sop into FIFO, during the rising edge start of packet 
            --if patt_muxi.start_of_packet then
            if not r.mux_sop and s.mux_sop then
               ram(r.write_index)(C_AVALON_MM_DATA_WIDTH) <= '1';

            else
               ram(r.write_index)(C_AVALON_MM_DATA_WIDTH) <= '0';

            end if;

            -- trigger eop when input data is writing
            --if patt_muxi.end_of_packet then
            if not r.mux_eop and s.mux_eop then
               ram(r.write_index)(C_SOP_EOP_WIDTH+C_AVALON_MM_DATA_WIDTH-1) <= '1';
         
            else
               ram(r.write_index)(C_SOP_EOP_WIDTH+C_AVALON_MM_DATA_WIDTH-1) <= '0';

            end if;

            s.write_index                := (r.write_index + 1) mod (C_FIFO_DEPTH);

         end if;

         -- read data from fifo memory
         if (scoreboardi.ready = '1') and not r.fifo_empty then
            -- registered address
            s.data_out                   := ram(r.read_index)(C_AVALON_MM_DATA_WIDTH-1 downto 0);
            s.sop                        := ram(r.read_index)(C_AVALON_MM_DATA_WIDTH);                    -- read sop from FIFO
            s.eop                        := ram(r.read_index)(C_SOP_EOP_WIDTH+C_AVALON_MM_DATA_WIDTH-1);  -- read eop from FIFO
            s.read_index                 := (r.read_index + 1) mod (C_FIFO_DEPTH);
            s.data_valid                 := true;

         end if;

         -- check whether fifo is full
         if ((s.write_index+1) mod C_FIFO_DEPTH) = s.read_index then
            s.fifo_full                  := true;
            s.fifo_empty                 := false;

         elsif ((s.write_index+2) mod C_FIFO_DEPTH) = s.read_index then
            s.almost_fifo_full           := true;
            --s.fifo_empty                 := false;

         -- check whether fifo is empty
         elsif s.write_index = s.read_index then
            s.fifo_empty                 := true;
            s.fifo_full                  := false;

         else
            s.fifo_full                  := false;
            s.fifo_empty                 := false;

         end if;

         r     <= s; 

         lfsro             <= (fifo_full   => s.almost_fifo_full);--fifo_full);
         linearo           <= (fifo_full   => s.almost_fifo_full);--fifo_full);
      end if;
   end process;


   scoreboardo             <= (data        => r.data_out,
                               sop         => r.sop,
                               eop         => r.eop,
                               valid       => boolean_to_stdlogic(r.data_valid));

end architecture rtl;      