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

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;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


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

entity frame_tx_if is
   generic(
      g_max_pkt_len        : natural range 64 to 9600 := 9600
   );
   port (
      -- Interface reset
      RESET                : in  std_logic;
      -- Interface clock
      CLK                  : in  std_logic;
      -- TX from USER
      S_AXIS_TVALID        : in  std_logic;
      S_AXIS_TREADY        : out std_logic;
      S_AXIS_TLAST         : in  std_logic;
      S_AXIS_TKEEP         : in  std_logic_vector(63 downto 0);
      S_AXIS_TDATA         : in  std_logic_vector(511 downto 0);
      -- TX info to USER
      PACKET_DIVIDED       : out std_logic;
      -- TX to data FIFO
      TD_FIFO_WREN         : out std_logic;
      TD_FIFO_FULL         : in  std_logic;
      TD_FIFO_DATA         : out std_logic_vector(575 downto 0);
      TD_FIFO_RST_BUSY     : in  std_logic;
      -- TX to tag FIFO
      TT_FIFO_WREN         : out std_logic;
      TT_FIFO_FULL         : in  std_logic;
      TT_FIFO_DATA         : out std_logic_vector(130 downto 0);
      TT_FIFO_RST_BUSY     : in  std_logic;
      -- TX net inf
      SRC_UDP              : in  std_logic_vector(15 downto 0);
      DST_UDP              : in  std_logic_vector(15 downto 0);
      DST_IP               : in  std_logic_vector(31 downto 0);
      DST_MAC              : in  std_logic_vector(47 downto 0);
      -- Host infos
      HOST_IP              : in  std_logic_vector(31 downto 0)
   );
end;

architecture Behavioral of frame_tx_if is


   -- Swap bytes form LSB ... MSB to MSB ... LSB
   function swap_bytes (vector   : std_logic_vector)
      return std_logic_vector is
         variable swapped_vector    : std_logic_vector(vector'length - 1 downto 0);
         variable vec               : std_logic_vector(vector'length - 1 downto 0);
      begin 
         vec := vector;
         for i in 0 to vec'length/8 - 1 loop
            swapped_vector(swapped_vector'length - i*8 - 1 downto swapped_vector'length - i*8 - 8) := vec((i+1)*8 - 1 downto i*8);
         end loop;
         return swapped_vector;
      end swap_bytes;

   -- Swap bytes in 16 bit words
   function int16_array_byte_swap (vector : std_logic_vector)
      return std_logic_vector is
      variable ret_array   : std_logic_vector(vector'range);
      begin
         for i in 0 to vector'length/16 - 1 loop
            ret_array(i*16 + 15 downto i*16 + 8) := vector(i*16 + 7 downto i*16 + 0);
            ret_array(i*16 + 7 downto i*16 + 0) := vector(i*16 + 15 downto i*16 + 8);
         end loop;
         return ret_array;
      end int16_array_byte_swap;  

   -- Calculate part of checksum
   function checksum_part (data : std_logic_vector; checksum   : std_logic_vector(15 downto 0))
      return std_logic_vector is
      variable ip_hdr_reordered  : std_logic_vector(data'range) := (others => '0');
      variable ret_val_tmp       : std_logic_vector(19 downto 0) := (others => '0');
      variable ret_val           : std_logic_vector(15 downto 0) := (others => '0');
      begin
         ip_hdr_reordered := int16_array_byte_swap(data);
         for i in 0 to ip_hdr_reordered'length/16 - 1 loop
            ret_val_tmp := ret_val_tmp + ip_hdr_reordered(i*16 + 15 downto i*16);
         end loop;
         ret_val_tmp := ret_val_tmp + checksum;
         ret_val := ret_val_tmp(15 downto 0) + ret_val_tmp(19 downto 16);
         return ret_val;
      end checksum_part;  

   -- Calculate checksum from data and precalculated checksum
   function full_checksum (data : std_logic_vector; rest : std_logic_vector(15 downto 0))
      return std_logic_vector is
      variable data_reordered    : std_logic_vector(data'range) := (others => '0');
      variable ret_val_tmp       : std_logic_vector(19 downto 0) := (others => '0');
      variable ret_val           : std_logic_vector(15 downto 0) := (others => '0');
      begin
         data_reordered := int16_array_byte_swap(data);
         for i in 0 to data_reordered'length/16 - 1 loop
            ret_val_tmp := ret_val_tmp + data_reordered(i*16 + 15 downto i*16);
         end loop;
         ret_val_tmp := ret_val_tmp + rest;
         ret_val := ret_val_tmp(15 downto 0) + ret_val_tmp(19 downto 16);
         return not ret_val;
      end full_checksum;   

   -- convert number to tkeep
   function num2tkeep (byte_cnt   : natural range 0 to 64)
      return std_logic_vector is
      variable tkeep          : std_logic_vector(63 downto 0) := (others => '0');
      begin
         if byte_cnt > 0 and byte_cnt < 64 then
            tkeep(byte_cnt-1 downto 0) := (others => '1');
         else
            tkeep := (others => '0');
         end if;
         return tkeep;
      end num2tkeep;
      
   -- convert tkeep to number
   function tkeep2num (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 tkeep2num;
      
   -- Find first zero in 64bit vector
   function tkeep_cut (tkeep : std_logic_vector)
      return std_logic_vector is
      variable cut : std_logic_vector(tkeep'range) := (others => '0');
      begin
         for i in 0 to tkeep'length-1 loop
            if tkeep(i) = '0' then
               return cut;
            end if;
            cut(i) := tkeep(i);
         end loop;
         return cut;
      end tkeep_cut;      

   -- Find first zero in 64bit vector
   function tkeep_invalid (tkeep : std_logic_vector)
      return std_logic is
      constant mask  : std_logic_vector(1 downto 0) := "10";
      variable ret   : std_logic := '0';
      begin
         for i in 0 to tkeep'length-2 loop
            if tkeep(i+1 downto i) = mask then
               ret := ret or '1';
            end if;
         end loop;
         return ret;
      end tkeep_invalid; 

   -- Find first zero in 64bit vector
   function tkeep2mask (vec : std_logic_vector)
      return std_logic_vector is
      variable vector : std_logic_vector(vec'length-1 downto 0) := vec;
      variable mask     : std_logic_vector(vec'length*8 - 1 downto 0) := (others => '0');
      begin
         for i in 0 to vec'length-1 loop
               mask((i+1)*8 - 1 downto i*8) := (others => vec(i));
         end loop;
         return mask;
      end tkeep2mask;

   -- or vector
   function or_vec (vec : std_logic_vector)
      return std_logic is
      variable retval : std_logic := '0';
      begin
         for i in 0 to vec'length-1 loop
               retval := retval or vec(i);
         end loop;
         return retval;
      end or_vec;

   -- and vector
   function and_vec (vec : std_logic_vector)
      return std_logic is
      variable retval : std_logic := '1';
      begin
         for i in 0 to vec'length-1 loop
               retval := retval and vec(i);
         end loop;
         return retval;
      end and_vec;

   constant LC_TKEEP_ALL   : std_logic_vector(63 downto 0) := (others => '1');
   constant LC_TKEEP_NONE   : std_logic_vector(63 downto 0) := (others => '0');
   constant LC_DATA_CNT_ZERO   : std_logic_vector(15 downto 0) := (others => '0');
   constant LC_MINIMAL_TKEEP     : std_logic_vector(63 downto 0) := x"000000000003ffff";

   -- IP header constants 
   constant LC_IP_HDR_ID            : std_logic_vector(15 downto 0) := x"0000";
   constant LC_IP_HDR_FLAG_FRAGOFF  : std_logic_vector(15 downto 0) := x"0000";
   constant LC_IP_HDR_VER_IHL_TOS   : std_logic_vector(15 downto 0) := x"4500";
   constant LC_IP_HDR_TTL_PROTO     : std_logic_vector(15 downto 0) := x"8011";

   -- Headers sizes 
   constant LC_ETH_HEADER_SIZE      : natural := 14*8;
   constant LC_IP_HEADER_SIZE       : natural := 20*8;
   constant LC_UDP_HEADER_SIZE      : natural := 8*8;
   constant LC_HEADER_SIZE          : natural := LC_ETH_HEADER_SIZE + LC_IP_HEADER_SIZE + LC_UDP_HEADER_SIZE;
   
   -- Number of valid bytes in TDATA when TLAST is asserted
   signal tkeep_num              : natural range 0 to 64 := 0;
      
   -- Stored packet route infos - sampled on the start of packet transmision
   signal src_udp_reg              : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_udp_reg              : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_ip_reg               : std_logic_vector(31 downto 0) := (others => '0');
   signal dst_mac_reg              : std_logic_vector(47 downto 0) := (others => '0'); 

   signal src_udp_i              : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_udp_i              : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_ip_i               : std_logic_vector(31 downto 0) := (others => '0');
   signal dst_mac_i              : std_logic_vector(47 downto 0) := (others => '0'); 

   signal data_cnt     : std_logic_vector(15 downto 0) := (others => '0');
   signal data_cnt_ext : std_logic_vector(15 downto 0) := (others => '0');
   signal data_cnt_i   : std_logic_vector(15 downto 0) := (others => '0');
   signal data_cnt_d   : std_logic_vector(15 downto 0) := (others => '0');

   signal discard          : std_logic := '0';
   signal discard_reg      : std_logic := '0';
   
   signal data_wren_i        : std_logic := '0';
   signal tag_wren_i        : std_logic := '0';
   signal fifo_rdy         : std_logic := '0';

   signal ip_hdr_len             : std_logic_vector(15 downto 0) := (others => '0'); 
   
   signal half_ip_header_tmp     : std_logic_vector(63 downto 0) := (others => '0'); 
   signal half_if_header_chksum  : std_logic_vector(15 downto 0) := (others => '0');
   
   signal s_axis_tvalid_reg    : std_logic := '0';
   signal s_axis_tlast_reg     : std_logic := '0';
   signal s_axis_tkeep_reg     : std_logic_vector(63 downto 0) := (others => '0');
   signal s_axis_tdata_reg     : std_logic_vector(511 downto 0) := (others => '0');
   
   signal data_length_comp     : std_logic_vector(1 downto 0) := (others => '0');

   signal packet_divided_i       : std_logic := '0';
   signal data_cnt_zero          : std_logic := '0';
   
   signal divide_pkt             : std_logic := '0';
   
   
   signal s_axis_tkeep_i         : std_logic_vector(63 downto 0) := (others => '0');
   signal s_axis_tdata_i         : std_logic_vector(511 downto 0) := (others => '0');
   
   constant LC_CONST_HDR_PART    : std_logic_vector(15 downto 0) := checksum_part(swap_bytes(LC_IP_HDR_VER_IHL_TOS & LC_IP_HDR_ID & LC_IP_HDR_FLAG_FRAGOFF & LC_IP_HDR_TTL_PROTO), x"0000");
   
   
   signal m_axis_tvalid_i  : std_logic := '0';             

   signal s_axis_tvalid_d  : std_logic := '0';                    
   signal s_axis_tlast_d   : std_logic := '0';     
   signal s_axis_tkeep_d   : std_logic_vector(63 downto 0) := (others => '0');
   signal s_axis_tdata_d   : std_logic_vector(511 downto 0) := (others => '0');

   signal s_axis_tvalid_dd : std_logic := '0';                       
   signal s_axis_tlast_dd  : std_logic := '0';        
   signal s_axis_tkeep_dd  : std_logic_vector(63 downto 0) := (others => '0');
   signal s_axis_tdata_dd  : std_logic_vector(511 downto 0) := (others => '0');
   
   signal src_udp_d        : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_udp_d        : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_ip_d         : std_logic_vector(31 downto 0) := (others => '0');
   signal dst_mac_d        : std_logic_vector(47 downto 0) := (others => '0');
   signal src_udp_dd       : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_udp_dd       : std_logic_vector(15 downto 0) := (others => '0');
   signal dst_ip_dd        : std_logic_vector(31 downto 0) := (others => '0');
   signal dst_mac_dd       : std_logic_vector(47 downto 0) := (others => '0');
   
   signal ip_hdr_checksum_tmp    : std_logic_vector(15 downto 0) := (others => '0');
   signal ip_hdr_checksum        : std_logic_vector(15 downto 0) := (others => '0');
   signal ip_hdr_checksum_reg    : std_logic_vector(15 downto 0) := (others => '0');
   signal ip_hdr_checksum_reg_d  : std_logic_vector(15 downto 0) := (others => '0');
   signal ip_data_cnt      : std_logic_vector(15 downto 0) := (others => '0');
   
   signal tag              : std_logic_vector(TT_FIFO_DATA'range) := (others => '0');
   signal tag_reg          : std_logic_vector(TT_FIFO_DATA'range) := (others => '0');
   signal data_cnt_reg     : std_logic_vector(15 downto 0) := (others => '0');                 
   signal tag_wr           : std_logic := '0';                   
   signal data_cnt_zero_reg : std_logic := '0';     
   
   
   signal tkeep_err              : std_logic := '0';
   signal tkeep_all_zeros        : std_logic := '0';
   signal tkeep_all_ones         : std_logic := '0'; 
   signal tkeep_all_ones_prev    : std_logic := '0'; 
   signal valid_bytes_num        : natural range 0 to 64 := 0;
   
   signal data_length_comp_d     : std_logic_vector(1 downto 0) := (others => '0');
   
   signal discard_d              : std_logic := '0';
   signal tkeep_42b_all_zeros   : std_logic := '0';
   signal tkeep_46b_all_zeros   : std_logic := '0';
   signal data_cnt_zero_prev     : std_logic := '0';
   
   
begin 

   -- FIFOs are ready to receive
   fifo_rdy <= not TD_FIFO_FULL and not TT_FIFO_FULL and not TD_FIFO_RST_BUSY and not TT_FIFO_RST_BUSY;
  

   -- Register AXI Stream
   -- registerm additional packet informations
   -- Get some extra signals which are going to be used in the next part
   -- instead of counter comparsion
   process(CLK) 
      variable tmp_42b_vec     : std_logic_vector(41 downto 0);
      variable tmp_46b_vec     : std_logic_vector(45 downto 0);
      begin 
         if rising_edge(CLK) then
            tmp_42b_vec := s_axis_tkeep_d(s_axis_tkeep_d'left downto s_axis_tkeep_d'length-42);
            tmp_46b_vec := s_axis_tkeep_d(s_axis_tkeep_d'left downto s_axis_tkeep_d'length-46);
            if RESET = '1' then
               -- data to fifo
               s_axis_tvalid_dd <= '0';            
               s_axis_tlast_dd  <= '0';            
               s_axis_tkeep_dd  <= (others => '0');
               s_axis_tdata_dd  <= (others => '0');
               -- registered data
               s_axis_tvalid_d <= '0';            
               s_axis_tlast_d  <= '0';            
               s_axis_tkeep_d  <= (others => '0');
               s_axis_tdata_d  <= (others => '0');
               -- registered infos
               src_udp_d     <= (others => '0');
               dst_udp_d     <= (others => '0');
               dst_ip_d      <= (others => '0');
               dst_mac_d     <= (others => '0');
               -- infos to fifo
               src_udp_dd    <= (others => '0');
               dst_udp_dd    <= (others => '0');
               dst_ip_dd     <= (others => '0');
               dst_mac_dd    <= (others => '0');
            else     
               if fifo_rdy = '1' or s_axis_tvalid_dd = '0' then
                  -- One cycle delayed data
                  s_axis_tvalid_d <= S_AXIS_TVALID;
                  s_axis_tlast_d <= S_AXIS_TLAST;
                  s_axis_tkeep_d <= S_AXIS_TKEEP;
                  s_axis_tdata_d <= S_AXIS_TDATA;
                  -- output data
                  s_axis_tvalid_dd <= s_axis_tvalid_d;
                  s_axis_tlast_dd  <= s_axis_tlast_d;  
                  s_axis_tkeep_dd  <= s_axis_tkeep_d;  
                  s_axis_tdata_dd  <= s_axis_tdata_d; 
                  -- only cycle delayed infos
                  src_udp_d   <= SRC_UDP;
                  dst_udp_d   <= DST_UDP;
                  dst_ip_d    <= DST_IP;
                  dst_mac_d   <= DST_MAC;
                  -- output infos
                  src_udp_dd  <= src_udp_d;
                  dst_udp_dd  <= dst_udp_d;
                  dst_ip_dd   <= dst_ip_d;
                  dst_mac_dd  <= dst_mac_d;
                  --
                  tkeep_err <= tkeep_invalid(s_axis_tkeep_d);
                  tkeep_all_ones <= and_vec(s_axis_tkeep_d);
                  tkeep_all_zeros <= not or_vec(s_axis_tkeep_d);
                  valid_bytes_num <= tkeep2num(s_axis_tkeep_d);
                  tkeep_42b_all_zeros <= not or_vec(tmp_42b_vec);
                  tkeep_46b_all_zeros <= not or_vec(tmp_46b_vec);
                  data_cnt_zero_prev <= data_cnt_zero;
               end if;
            end if;
         end if;
      end process;

   -- Save packet routing informations on it's start
   -- Register discard signal
   -- Counting stored bytes
   reg_pkt_infos_proc: process(CLK)
      variable tmp_cnt    : std_logic_vector(15 downto 0) := (others => '0');
      begin
         if rising_edge(CLK) then
            if RESET = '1' then
               data_cnt <= (others => '0');
               discard_reg <= '0';
               data_cnt_zero <= '1';
               src_udp_reg <= (others => '0');
               dst_udp_reg <= (others => '0');
               dst_ip_reg <= (others => '0'); 
               dst_mac_reg <= (others => '0');
               divide_pkt <= '0';
            else
               -- Register discard signal if there is not end of packet
               if (s_axis_tvalid_dd and s_axis_tlast_dd and fifo_rdy) = '1' then
                  discard_reg <= '0';
               elsif discard = '1' then
                  discard_reg <= '1';
               end if;
               -- Count bytes already written in fifo
               if data_wren_i = '1' then --and tag_wren_i = '0' then 
                  if tag_wren_i = '0' then 
                     data_cnt_zero <= '0';      
                     data_cnt <= data_cnt + std_logic_vector(to_unsigned(64, data_cnt'length));
                  else
                     data_cnt <= (others => '0');
                     data_cnt_zero <= '1';
                  end if;
               end if;
               -- Save packet informations on it's start
               -- Save precalculated part of IP header
               if data_cnt_zero = '1' then
                  src_udp_reg <= SRC_UDP;
                  dst_udp_reg <= DST_UDP;
                  dst_ip_reg <= DST_IP;  
                  dst_mac_reg <= DST_MAC;     
               end if;
               -- Divide packet so it meet maximum transfer unit (MTU)
               if packet_divided_i = '1' then 
                  divide_pkt <= '0';
               elsif data_cnt + 3*64 + LC_HEADER_SIZE/8 > g_max_pkt_len then 
                  divide_pkt <= '1';
               end if;               
            end if;
         end if;
      end process;
    
   -- Write into tag and data fifo 
   write_fifo_proc: process(fifo_rdy, s_axis_tvalid_dd, s_axis_tlast_dd, s_axis_tkeep_dd, discard_reg, 
                     data_cnt, data_cnt_zero, tkeep_err, tkeep_all_ones, tkeep_all_zeros, divide_pkt)
      begin
         discard <= '0';
         data_wren_i <= '0';
         tag_wren_i <= '0';
         packet_divided_i <= '0';
         -- Write to FIFO is possible when it is ready and packet is not supposed to be discarted
         if fifo_rdy = '1' and discard_reg = '0' then
            -- Write 64 bytes to FIFO if S_AXIS_TLAST is low and S_AXIS_TKEEP is all high
            -- Discard packet if S_AXIS_TLAST is low and S_AXIS_TKEEP is not all high
            if s_axis_tvalid_dd = '1' and s_axis_tlast_dd = '0' then
               if tkeep_all_ones = '1' then
                  data_wren_i <= '1';
                  if divide_pkt = '1' then 
                     packet_divided_i <= '1';
                     tag_wren_i <= '1';
                  end if;
               else
                  discard <= '1';
                  tag_wren_i <= '1';
               end if;
            -- Write rest to FIFO if S_AXIS_TLAST is high
            -- Discard packet if S_AXIS_TKEEP is not all zeros and no bytes were previously written
            elsif s_axis_tvalid_dd = '1' and s_axis_tlast_dd = '1' then
               if (tkeep_all_zeros = '1' and data_cnt_zero = '1') or tkeep_err = '1' then
                   discard <= '1';
               elsif tkeep_all_zeros = '0' then
                   data_wren_i <= '1';
               end if;
               tag_wren_i <= '1';
            end if;
         end if;
      end process;

   -- update number of bytes to write
   data_cnt_i <= (data_cnt + valid_bytes_num) when discard = '0' else data_cnt;
   
   -- Precalculating conditions
   -- data_len = 0      will not be sent - is not going to be written into the fifo
   -- data_len < 60     padding is added and will be send in one cycle
   -- data_len <= 64    will be send in one cycle
   -- data_len <= 2*64  will be send in two cycles
   -- data_len > 64     will be send in more than two cycles
   -- (+1 because < is faster than <=)
   data_length_comp <=  "01" when data_cnt_zero = '1' and tkeep_42b_all_zeros = '1' else
                        "10" when (data_cnt_zero_prev = '1' and tkeep_42b_all_zeros = '1') or (data_cnt_zero = '1' and tkeep_42b_all_zeros = '0') else "11";

   -- Packet data and tkeep         
   s_axis_tkeep_i <= LC_MINIMAL_TKEEP when data_cnt_zero = '1' and tkeep_46b_all_zeros = '1' else s_axis_tkeep_dd;
   s_axis_tdata_i <= (tkeep2mask(s_axis_tkeep_dd) and s_axis_tdata_dd);

   -- Delay signals used to write into tag fifo
   -- Experiment which could improve tight timing
   tag_data_delay_proc: process(CLK)
      begin 
         if rising_edge(CLK) then
            if RESET = '1' then 
               tag_reg <= (others => '0');
               ip_data_cnt <= (others => '0');
               tag_wr <= '0';
               src_udp_i <= (others => '0');
               dst_udp_i <= (others => '0');
               dst_ip_i  <= (others => '0');
               dst_mac_i <= (others => '0');
               data_cnt_d <= (others => '0');
               data_length_comp_d <= (others => '0');
               discard_d <= '0';
            else
               tag_reg <= tag;
               tag_wr <= tag_wren_i;
               data_length_comp_d <= data_length_comp;
               discard_d <= discard;
               data_cnt_d <= data_cnt_i;
               ip_data_cnt <= data_cnt_i + LC_IP_HEADER_SIZE/8+LC_UDP_HEADER_SIZE/8;
               if data_cnt_zero = '1' then 
                  src_udp_i <= src_udp_dd;
                  dst_udp_i <= dst_udp_dd;
                  dst_ip_i  <= dst_ip_dd; 
                  dst_mac_i <= dst_mac_dd;
               else
                  src_udp_i <= src_udp_reg;
                  dst_udp_i <= dst_udp_reg;
                  dst_ip_i  <= dst_ip_reg; 
                  dst_mac_i <= dst_mac_reg;
               end if;               
            end if;
         end if;
      end process;   


   -- Assign data to data fifo interface
   TD_FIFO_DATA <= s_axis_tkeep_i & s_axis_tdata_i;
   -- Assing data to tag fifo interface
   TT_FIFO_DATA <= data_length_comp_d & not discard_d & data_cnt_d & src_udp_i & dst_udp_i & dst_ip_i & dst_mac_i;
   -- FIFOs write enable
   TD_FIFO_WREN <= data_wren_i;
   TT_FIFO_WREN <= tag_wr;
   -- AXI Stream ready
   S_AXIS_TREADY <= (not s_axis_tvalid_dd or fifo_rdy) and not RESET;
   -- Packet divided to user
   PACKET_DIVIDED <= packet_divided_i;

end;
