-------------------------------------------------------------------------------
--
-- 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 frame_rx_if is
   port (
      -- Interface reset
      RESET                : in  std_logic;
      -- Interface clock
      CLK                  : in  std_logic;
      -- RX to USER
      M_AXIS_TVALID        : out std_logic;
      M_AXIS_TREADY        : in  std_logic;
      M_AXIS_TLAST         : out std_logic;
      M_AXIS_TKEEP         : out std_logic_vector(63 downto 0);
      M_AXIS_TDATA         : out std_logic_vector(511 downto 0);
      -- RX from data FIFO
      RD_FIFO_RDEN         : out std_logic;
      RD_FIFO_EMPTY        : in  std_logic;
      RD_FIFO_DATA         : in  std_logic_vector(575 downto 0);
      RD_FIFO_RST_BUSY     : in  std_logic;
      -- RX from tag FIFO
      RT_FIFO_RDEN         : out std_logic;
      RT_FIFO_EMPTY        : in  std_logic;
      RT_FIFO_DATA         : in  std_logic_vector(128 downto 0);
      RT_FIFO_RST_BUSY     : in  std_logic;
      -- TX net inf
      SRC_UDP              : out std_logic_vector(15 downto 0);
      DST_UDP              : out std_logic_vector(15 downto 0);
      SRC_IP               : out std_logic_vector(31 downto 0);
      SRC_MAC              : out std_logic_vector(47 downto 0);
      PACKET_LENGTH        : out std_logic_vector(15 downto 0)
   );
end;

architecture Behavioral of frame_rx_if is

   -- Find first zero in 64bit vector
   function vec_first_zero (vector : std_logic_vector)
      return natural is
      begin
         for i in 0 to vector'length-1 loop
            if vector(i) = '0' then
               return i;
            end if;
         end loop;
         return vector'length;
      end vec_first_zero;

   -- Find first zero in 64bit vector
   function sub_to_positive (vec0   : natural;
                             vec1   : natural)
      return natural is
      variable ret_val  : natural;
      begin
         if vec1 > vec0 then
            ret_val := 0;
         else
            ret_val := vec0 - vec1;
         end if;
         return ret_val;
      end sub_to_positive;
   
   -- Number of valid bytes in TDATA when TLAST is asserted
   signal tkeep_num              : natural range 0 to 64 := 0;
   
   -- Packet transmision was started
   signal packet_started         : std_logic := '0';
   
   -- Stored packet route infos - sampled on the start of packet transmision
   signal rx_src_udp             : std_logic_vector(15 downto 0) := (others => '0');
   signal rx_dst_udp             : std_logic_vector(15 downto 0) := (others => '0');
   signal rx_src_ip              : std_logic_vector(31 downto 0) := (others => '0');
   signal rx_src_mac             : std_logic_vector(47 downto 0) := (others => '0'); 
   signal pkt_ok                 : std_logic := '0';
   signal pkt_size               : std_logic_vector(15 downto 0) := (others => '0'); 
   signal pkt_size_cnt           : natural range 0 to 65536 := 0;
   signal pkt_size_cnt_pre       : natural range 0 to 65536 := M_AXIS_TDATA'length/8;
   
   signal fifo_not_empty         : std_logic := '0';
   signal rx_data_fifo_rden_i    : std_logic := '0';
   
   signal rx_tkeep               : std_logic_vector(63 downto 0);
   signal rx_tdata               : std_logic_vector(511 downto 0);

   signal data_fifo_empty        : std_logic := '0';
   signal tag_fifo_empty         : std_logic := '0';
   
   
   
   signal m_axis_tvalid_ii       : std_logic := '0';
   signal m_axis_tlast_ii        : std_logic := '0';
   signal m_axis_tkeep_ii        : std_logic_vector(63 downto 0) := (others => '0');
   signal m_axis_tdata_ii        : std_logic_vector(511 downto 0) := (others => '0');   
   
   signal m_axis_tvalid_i        : std_logic := '0';
   signal m_axis_tlast_i         : std_logic := '0';
   signal last                   : std_logic := '0';
   signal m_axis_tkeep_i         : std_logic_vector(63 downto 0) := (others => '0');
   signal m_axis_tdata_i         : std_logic_vector(511 downto 0) := (others => '0');   
   signal stream_start           : std_logic := '0';
   
   signal data_valid       : std_logic := '0';
   signal data_valid_d     : std_logic := '0';
   signal tag_valid        : std_logic := '0';
   signal tag_valid_d      : std_logic := '0';
   signal rd_fifo_en_i     : std_logic;
   signal rt_fifo_en_i     : std_logic;
   
   
   
   signal tag_rd_en        : std_logic;
   signal data_rd_en       : std_logic;
   signal tag_d            : std_logic_vector(RT_FIFO_DATA'range) := (others => '0');
   signal tag_dd           : std_logic_vector(RT_FIFO_DATA'range) := (others => '0');
   signal tag_empty_d      : std_logic;
   signal tag_empty_dd     : std_logic;
   signal data_d           : std_logic_vector(RD_FIFO_DATA'range) := (others => '0');
   signal data_dd          : std_logic_vector(RD_FIFO_DATA'range) := (others => '0');
   signal data_empty_d     : std_logic;
   signal data_empty_dd    : std_logic;
   
   signal count_condition  : std_logic := '0';
   
begin

   -- register tag from fifo
   tag_fifo_if_inst: process(CLK) 
      begin
         if rising_edge(CLK) then
            if RESET = '1' then 
            tag_d <= (others => '0');
            tag_dd <= (others => '0');
            tag_empty_d <= '1';
            tag_empty_dd <= '1';
            else
               if tag_rd_en = '1' or (tag_empty_d and not tag_fifo_empty) = '1' then 
                  -- data
                  tag_d <= RT_FIFO_DATA;
                  tag_dd <= tag_d;
                  -- empty
                  tag_empty_d <= RT_FIFO_EMPTY;
                  tag_empty_dd <= tag_empty_d;
               end if;
            end if;
         end if;
      end process; 

   -- register data from fifo
   data_fifo_if_inst: process(CLK) 
      begin
         if rising_edge(CLK) then
            if RESET = '1' then 
            data_d <= (others => '0');
            data_dd <= (others => '0');
            data_empty_d <= '1';
            data_empty_dd <= '1';
            else
               if data_rd_en = '1' or (data_empty_d and not data_fifo_empty) = '1' then 
                  -- data
                  data_d <= RD_FIFO_DATA;
                  data_dd <= data_d;
                  -- empty
                  data_empty_d <= RD_FIFO_EMPTY;
                  data_empty_dd <= data_empty_d;
               end if;
            end if;
         end if;
      end process;
   
   -- FIFO is not ready if rst_busy is high
   data_fifo_empty <=  RD_FIFO_EMPTY or RD_FIFO_RST_BUSY;
   tag_fifo_empty <= RT_FIFO_EMPTY or RT_FIFO_RST_BUSY;

   -- Neither one FIFO is empty
   fifo_not_empty <= not data_empty_d and not tag_empty_d;    
     
   tag_rd_en <= '1' when ((data_rd_en = '1' and (pkt_size_cnt + rx_tdata'length/8 >= pkt_size)) or (tag_empty_d and not tag_fifo_empty) = '1') else '0';
   data_rd_en <= (fifo_not_empty and (M_AXIS_TREADY or not pkt_ok)) or (data_empty_d and not data_fifo_empty);

--   tag_rd_en <= '1' when data_rd_en = '1' and (pkt_size_cnt + rx_tdata'length/8 >= pkt_size) else '0';
--   data_rd_en <= fifo_not_empty and (M_AXIS_TREADY or not pkt_ok);
   
--   RD_FIFO_RDEN <= data_rd_en or (data_empty_d and not data_fifo_empty);
--   RT_FIFO_RDEN <= tag_rd_en or (tag_empty_d and not tag_fifo_empty);
   
   -- Parse packet infos
   pkt_ok      <= tag_d(128)            when tag_empty_d = '0' else '0';           
   pkt_size    <= tag_d(127 downto 112) when tag_empty_d = '0' else (others => '0');
   rx_src_udp  <= tag_d(111 downto 96)  when tag_empty_d = '0' else (others => '0'); 
   rx_dst_udp  <= tag_d(95 downto 80)   when tag_empty_d = '0' else (others => '0');  
   rx_src_ip   <= tag_d(79 downto 48)   when tag_empty_d = '0' else (others => '0');  
   rx_src_mac  <= tag_d(47 downto 0)    when tag_empty_d = '0' else (others => '0');      
   
   -- Parse tkeep and tdata
   rx_tkeep <= data_d(575 downto 512);
   rx_tdata <= data_d(511 downto 0);
   
   
   count_condition <= fifo_not_empty and (M_AXIS_TREADY or not pkt_ok);
   
   -- Counter process
   pkt_size_cnt_proc: process(CLK) 
      begin 
         if rising_edge(CLK) then
            if RESET = '1' then
               pkt_size_cnt <= 0;
               pkt_size_cnt_pre <= rx_tdata'length/8;
            else
               if count_condition = '1' then
                  if pkt_size_cnt + rx_tdata'length/8 < pkt_size then
                     pkt_size_cnt <= pkt_size_cnt + vec_first_zero(rx_tkeep);
                     pkt_size_cnt_pre <= pkt_size_cnt_pre + vec_first_zero(rx_tkeep);
                  else
                     pkt_size_cnt <= 0;
                     pkt_size_cnt_pre <= rx_tdata'length/8;
                  end if;
               end if;
            end if;
         end if;
      end process;

   SRC_UDP <= rx_src_udp      when pkt_ok = '1' else (others => '0');
   DST_UDP <= rx_dst_udp      when pkt_ok = '1' else (others => '0');
   SRC_IP  <= rx_src_ip       when pkt_ok = '1' else (others => '0');
   SRC_MAC <= rx_src_mac      when pkt_ok = '1' else (others => '0');
   PACKET_LENGTH <= pkt_size  when pkt_ok = '1' else (others => '0');

   
   M_AXIS_TVALID <= fifo_not_empty  when pkt_ok = '1' else '0';
   m_axis_tlast_i <= '1' when pkt_ok = '1' and pkt_size_cnt_pre >= pkt_size else '0';
   M_AXIS_TLAST <= m_axis_tlast_i;
   M_AXIS_TKEEP <= rx_tkeep         when pkt_ok = '1' else (others => '0');
   M_AXIS_TDATA <= rx_tdata         when pkt_ok = '1' and fifo_not_empty = '1' else (others => '0');
         
   RD_FIFO_RDEN <= data_rd_en;
   RT_FIFO_RDEN <= tag_rd_en;
   
   
   
   
   



   
--   -- FIFO is not ready if rst_busy is high
--   data_fifo_empty <=  RD_FIFO_EMPTY or RD_FIFO_RST_BUSY;
--   tag_fifo_empty <= RT_FIFO_EMPTY or RT_FIFO_RST_BUSY;
   
--   -- Parse packet infos
--   pkt_ok      <= RT_FIFO_DATA(128)            when tag_fifo_empty = '0' else '0';           
--   pkt_size    <= RT_FIFO_DATA(127 downto 112) when tag_fifo_empty = '0' else (others => '0');
--   rx_src_udp  <= RT_FIFO_DATA(111 downto 96)  when tag_fifo_empty = '0' else (others => '0'); 
--   rx_dst_udp  <= RT_FIFO_DATA(95 downto 80)   when tag_fifo_empty = '0' else (others => '0');  
--   rx_src_ip   <= RT_FIFO_DATA(79 downto 48)   when tag_fifo_empty = '0' else (others => '0');  
--   rx_src_mac  <= RT_FIFO_DATA(47 downto 0)    when tag_fifo_empty = '0' else (others => '0');      
   
--   -- Neither one FIFO is empty
--   fifo_not_empty <= not data_fifo_empty and not tag_fifo_empty;
   
--   -- Parse tkeep and tdata
--   rx_tkeep <= RD_FIFO_DATA(575 downto 512);
--   rx_tdata <= RD_FIFO_DATA(511 downto 0);
   
--   -- Counter process
--   pkt_size_cnt_proc: process(CLK) 
--      begin 
--         if rising_edge(CLK) then
--            if RESET = '1' then
--               pkt_size_cnt <= 0;
--            else
--               if rx_data_fifo_rden_i = '1' then
--                  if pkt_size_cnt + rx_tdata'length/8 < pkt_size then
--                     pkt_size_cnt <= pkt_size_cnt + vec_first_zero(rx_tkeep);
--                  else
--                     pkt_size_cnt <= 0;
--                  end if;
--               end if;
--            end if;
--         end if;
--      end process;

--   rx_data_fifo_rden_i <= fifo_not_empty and (M_AXIS_TREADY or not pkt_ok);
--   SRC_UDP <= rx_src_udp      when pkt_ok = '1' else (others => '0');
--   DST_UDP <= rx_dst_udp      when pkt_ok = '1' else (others => '0');
--   SRC_IP  <= rx_src_ip       when pkt_ok = '1' else (others => '0');
--   SRC_MAC <= rx_src_mac      when pkt_ok = '1' else (others => '0');
--   PACKET_LENGTH <= pkt_size  when pkt_ok = '1' else (others => '0');
   
--   RD_FIFO_RDEN <= rx_data_fifo_rden_i;
--   RT_FIFO_RDEN <= '1' when rx_data_fifo_rden_i = '1' and (pkt_size_cnt + rx_tdata'length/8 >= pkt_size) else '0';
   
--   M_AXIS_TVALID <= fifo_not_empty  when pkt_ok = '1' else '0';
--   m_axis_tlast_i <= '1' when pkt_ok = '1' and pkt_size_cnt + rx_tdata'length/8 >= pkt_size else '0';
--   M_AXIS_TLAST <= m_axis_tlast_i;
--   M_AXIS_TKEEP <= rx_tkeep         when pkt_ok = '1' else (others => '0');
--   M_AXIS_TDATA <= rx_tdata         when pkt_ok = '1' and fifo_not_empty = '1' else (others => '0');
   
end;
