---------------------------------------------------------------------------
-- File           : ea_cmd_gen_fifo.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : March 14th, 2019
---------------------------------------------------------------------------
-- Description    : Fifo commands needs to store input commands from avalon 
-- memory master. Fifo depth is 16 words (commands).
--
-- Avalon mm interface to fifo interface:
--       address       <= masteri.address,
--       write_req     <= masteri.write,
--       data_in       <= masteri.writedata,
--       new_package   <= masteri.beginbursttransfer
--       write_count   <= masteri.burstcount);
---------------------------------------------------------------------------
library ieee, work;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use std.textio.all;
   use ieee.std_logic_textio.all;
   use work.p_parameters_and_constants.all;
   use work.p_functions.all;
   use work.p_interfaces.all;
   use work.p_addresses.all;
   use work.p_generator_interface.all;

entity ea_cmd_gen_fifo is
   port  (
      clk               : in  std_logic;
      reset             : in  std_logic;
      -- to/from input generator
      masteri           : in  TYPE_MASTER_TO_FIFO_CMD;
      mastero           : out TYPE_FIFO_CMD_TO_MASTER;
      -- to/from command decoder
      cmd_decoderi      : in  TYPE_CMD_DEC_TO_FIFO_CMD;
      cmd_decodero      : out TYPE_FIFO_CMD_TO_CMD_DEC
   );
end entity ea_cmd_gen_fifo;

architecture rtl of ea_cmd_gen_fifo is
   -----------------------------------------------------------------------------
   -- Dual port RAM
   -----------------------------------------------------------------------------
   subtype TYPE_DPRAM_ELEMENT is std_logic_vector(C_AVALON_MM_DATA_WIDTH-1 downto 0);
   type TYPE_DPRAM is array (2**C_AVALON_MM_ADDR_WIDTH-1 downto 0) of TYPE_DPRAM_ELEMENT;
   signal ram       : TYPE_DPRAM := (others => TYPE_DPRAM_ELEMENT'(others => '-'));

   constant C_FIFO_DEPTH  : natural := 2**C_AVALON_MM_ADDR_WIDTH;

   -- fifo registers 
   type TYPE_REGS is record
      --fifo_free_mem : natural;
      write_index   : natural;
      read_index    : natural;
      write_count   : natural;
      burst_req     : boolean;
      fifo_full     : boolean;
      fifo_empty    : boolean;
      data_out      : std_logic_vector (C_AVALON_MM_DATA_WIDTH-1 downto 0); 
      data_valid    : boolean;
   end record TYPE_REGS;

   constant CONST_REGS_RESET : TYPE_REGS := (
      --fifo_free_mem  => C_FIFO_DEPTH,
      write_index   => 0,
      read_index    => 0,
      write_count   => 0,
      burst_req     => false,
      fifo_full     => false,
      fifo_empty    => false,
      data_out      => (others => '0'),
      data_valid    => false
   );

   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;

         -- update counter for new burst transaction
         if masteri.write_req = '1' and masteri.new_package = '1' and not s.fifo_full then
            s.write_count  := to_integer(unsigned(masteri.write_count));---1;
            s.burst_req    := true;

         -- write burst data from input port 
         elsif r.write_count > 0 then
            s.write_count  := r.write_count - 1;
            s.burst_req    := true;

         -- stop transaction
         else 
            s.burst_req    := false;
          
         end if;

         -- write data into fifo (ram) memory
         if s.burst_req and not r.fifo_full then
            ram(r.write_index) <= masteri.data_in;
            s.write_index  := (r.write_index + 1) mod (C_FIFO_DEPTH);

         end if;

         -- read data from fifo memory
         if cmd_decoderi.read_req and not r.fifo_empty then
            -- registered address
            s.data_out     := ram(r.read_index);
            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;

         -- 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; 

      end if;
   end process;

   -- cmd data 
   cmd_decodero               <= (data_out    => r.data_out,
                                  fifo_empty  => r.fifo_empty,
                                  valid       => r.data_valid);

   mastero.fifo_full          <= boolean_to_stdlogic(r.fifo_full);

end architecture rtl;