-------------------------------------------------------------------------------
--
-- Project name: UDP_100G
--
-- (C) Copyright 2019 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Jakub Hadamek (j.hadamek@dfcdesign.cz)
--
-------------------------------------------------------------------------------
--
-- This file is part of UDP_100G IP core
-- 
-------------------------------------------------------------------------------



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

Library xpm;
use xpm.vcomponents.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity arp_stack is
   generic (
      g_stack_length       : natural range 0 to 16 := 0
   );
   port (
      -- Interface reset
      RESET                : in  std_logic;
      -- Interface clock
      CLK                  : in  std_logic;
      -- ARP request input
      ARP_REQUEST_IN       : in  std_logic;
      ARP_SRC_MAC_IN       : in  std_logic_vector(47 downto 0);
      ARP_SRC_IP_IN        : in  std_logic_vector(31 downto 0);
      -- ARP request output
      ARP_REQUEST          : out std_logic;
      ARP_ACCEPTED         : in  std_logic;
      ARP_SRC_MAC          : out std_logic_vector(47 downto 0);
      ARP_SRC_IP           : out std_logic_vector(31 downto 0)
   );
end;

architecture Behavioral of arp_stack is
   constant LC_STACK_WIDTH    : natural := 48 + 32;
   type arp_stack_type is array (natural range <>) of std_logic_vector(LC_STACK_WIDTH-1 downto 0);
   
   signal arp_stack           : arp_stack_type(g_stack_length-1 downto 0) := (others => (others => '0'));
   signal arp_stack_cnt       : natural range 0 to g_stack_length := 0;
   
begin
   
   process(CLK) 
      begin
         if rising_edge(CLK) then
            if RESET = '1' then 
               arp_stack <= (others => (others => '0'));
               arp_stack_cnt <= 0;
            else
               -- Increment request count
               -- push new request to the stack
               if ARP_REQUEST_IN = '1' and ARP_ACCEPTED = '0' then
                  -- update stack counter when it is lower than it's depth
                  -- otherwise throw away oldest request
                  if arp_stack_cnt < g_stack_length then 
                     arp_stack_cnt <= arp_stack_cnt + 1;
                  end if;
                  -- push new request to the stack
                  for i in 0 to g_stack_length-2 loop
                     arp_stack(i+1) <= arp_stack(i);
                  end loop;
                  arp_stack(0) <= ARP_SRC_MAC_IN & ARP_SRC_IP_IN;
               -- don't change request count
               -- change first request 
               elsif ARP_REQUEST_IN = '1' and ARP_ACCEPTED = '1' then
                  arp_stack(0) <= ARP_SRC_MAC_IN & ARP_SRC_IP_IN;
               -- Decrement request count
               -- pop top request from the stack
               elsif ARP_REQUEST_IN = '0' and ARP_ACCEPTED = '1' then  
                  -- update stack counter when it is higher than zero
                  if arp_stack_cnt > 0 then 
                     arp_stack_cnt <= arp_stack_cnt - 1;
                  end if;
                  -- pop newest request out of stack
                  for i in 0 to g_stack_length-2 loop
                     arp_stack(i) <= arp_stack(i+1);
                  end loop;
               end if;
            end if;
         end if;
      end process;

   ARP_REQUEST <= '1' when arp_stack_cnt > 0 else '0';
   ARP_SRC_MAC <= arp_stack(0)(ARP_SRC_MAC'left + ARP_SRC_IP'length downto ARP_SRC_IP'length);
   ARP_SRC_IP  <= arp_stack(0)(ARP_SRC_IP'left downto 0);



end;
   
