-------------------------------------------------------------------------------
--
-- Project name: UDP_100G_SWITCH
--
-- (C) Copyright 2019 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Jakub Hadamek (j.hadamek@dfcdesign.cz)
--
-------------------------------------------------------------------------------
-- 
-------------------------------------------------------------------------------



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.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 udp_registers is
   generic (
      g_udp_port              : std_logic_vector(15 downto 0);
      g_debug                 : boolean := false
   );
   port (
      ------------   UDP 100G Interface    ---------
      -- Core clock and reset
      RESET                   : in  std_logic := '0';
      CLK                     : in  std_logic := '0';
      -- Host info
      HOST_MAC                : in  std_logic_vector(47 downto 0) := (others => '0');
      HOST_IP                 : in  std_logic_vector(31 downto 0) := (others => '0');
      HOST_NET_MASK           : in  std_logic_vector(31 downto 0) := (others => '0');
      -- RX Datapath --
      S_AXIS_TVALID           : in  std_logic := '0';
      S_AXIS_TREADY           : out std_logic := '0';
      S_AXIS_TLAST            : in  std_logic := '0';
      S_AXIS_TKEEP            : in  std_logic_vector(63 downto 0) := (others => '0');
      S_AXIS_TDATA            : in  std_logic_vector(511 downto 0) := (others => '0');
      -- RX net info
      RX_DST_UDP              : out std_logic_vector(15 downto 0) := (others => '0');
      RX_SRC_UDP              : in  std_logic_vector(15 downto 0) := (others => '0');
      RX_SRC_IP               : in  std_logic_vector(31 downto 0) := (others => '0');
      RX_SRC_MAC              : in  std_logic_vector(47 downto 0) := (others => '0');
      RX_PACKET_LENGTH        : in  std_logic_vector(15 downto 0) := (others => '0');
      -- TX Datapath --
      M_AXIS_TVALID           : out std_logic := '0';
      M_AXIS_TREADY           : in  std_logic := '0';
      M_AXIS_TLAST            : out std_logic := '0';
      M_AXIS_TKEEP            : out std_logic_vector(63 downto 0) := (others => '0');
      M_AXIS_TDATA            : out std_logic_vector(511 downto 0) := (others => '0');
      -- TX net info
      TX_SRC_UDP              : out std_logic_vector(15 downto 0) := (others => '0');
      TX_DST_UDP              : out std_logic_vector(15 downto 0) := (others => '0');
      TX_DST_IP               : out std_logic_vector(31 downto 0) := (others => '0');
      TX_DST_MAC              : out std_logic_vector(47 downto 0) := (others => '0');
      -- control signals
      REG_CHANNEL_RUN         : out std_logic := '0';
      REG_CHANNEL_CLEAR       : out std_logic := '0';
      REG_CHANNEL_OVERFLOW    : in  std_logic := '0';     
      REG_CHANNEL_WORD_CNT    : in  std_logic_vector(63 downto 0) := (others => '0');
      -- packets related signals
      -- mac/ip/udp
      REG_ROCE_TX_DEST_IP     : out std_logic_vector(31 downto 0);
      REG_ROCE_TX_DEST_MAC    : out std_logic_vector(47 downto 0);
      REG_ROCE_TX_DEST_UDP    : out std_logic_vector(15 downto 0);
      -- roce
      REG_ROCE_TX_DEST_QP     : out std_logic_vector(23 downto 0);
      REG_ROCE_TX_PSN         : out std_logic_vector(23 downto 0) := (others => '0');
      REG_ROCE_TX_Q_KEY       : out std_logic_vector(31 downto 0)
    );
end udp_registers;

architecture Behavioral of udp_registers is

   function or_vec(vec     : std_logic_vector)
                 return std_logic is
      variable ret : std_logic := '0';
   begin
      for i in 0 to vec'left loop
         ret := ret or vec(i);
      end loop;
      return ret;
   end or_vec;


   function and_vec32(vec     : std_logic_vector(31 downto 0))
                 return std_logic is
      variable tmp_vec  : std_logic_vector(vec'range) := vec;
      variable ret : std_logic := '1';
   begin
      for i in 0 to tmp_vec'left loop
         ret := ret and tmp_vec(i);
      end loop;
      return ret;
   end and_vec32;

   function and_vec64(vec     : std_logic_vector(63 downto 0))
                 return std_logic is
      variable tmp_vec  : std_logic_vector(vec'range) := vec;
      variable ret : std_logic := '1';
   begin
      for i in 0 to tmp_vec'left loop
         ret := ret and tmp_vec(i);
      end loop;
      return ret;
   end and_vec64;

   function and_vec8(vec     : std_logic_vector(7 downto 0))
                 return std_logic is
      variable tmp_vec  : std_logic_vector(vec'range) := vec;
      variable ret : std_logic := '1';
   begin
      for i in 0 to tmp_vec'left loop
         ret := ret and tmp_vec(i);
      end loop;
      return ret;
   end and_vec8;

   function and_vec4(vec     : std_logic_vector(3 downto 0))
                 return std_logic is
      variable tmp_vec  : std_logic_vector(vec'range) := vec;
      variable ret : std_logic := '1';
   begin
      for i in 0 to tmp_vec'left loop
         ret := ret and tmp_vec(i);
      end loop;
      return ret;
   end and_vec4;

   constant LC_REG_ADDR_WIDTH             : natural := 32;
   constant LC_REG_WIDTH                  : natural := 32;
   constant LC_MAX_REGS_MESSEGE           : natural := 7;
   
   -- magic number
   constant LC_MAGIC_NUMBER               : std_logic_vector(27 downto 0) := x"dfc1dfc";
   -- message type
   constant LC_WRITE                      : std_logic_vector(3 downto 0) := "0001";
   constant LC_READ                       : std_logic_vector(3 downto 0) := "0010";
   constant LC_INFO                       : std_logic_vector(3 downto 0) := "0011";
   constant LC_RESP                       : std_logic_vector(3 downto 0) := "0100";
   constant LC_ERR                        : std_logic_vector(3 downto 0) := "1000";
   -- register addresses
   constant LC_REG_DST_IP                 : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DST_MAC_HIGH           : std_logic_vector(31 downto 0) := x"00000001";
   constant LC_REG_DST_MAC_LOW            : std_logic_vector(31 downto 0) := x"00000002";
   constant LC_REG_DST_UDP                : std_logic_vector(31 downto 0) := x"00000003";
   constant LC_REG_DST_QP                 : std_logic_vector(31 downto 0) := x"00000004";
   constant LC_REG_FIRST_PSN              : std_logic_vector(31 downto 0) := x"00000005";
   constant LC_REG_Q_KEY                  : std_logic_vector(31 downto 0) := x"00000006";
   constant LC_REG_CONTROL                : std_logic_vector(31 downto 0) := x"00000007";
   constant LC_REG_STATUS                 : std_logic_vector(31 downto 0) := x"00000008";
   constant LC_REG_STAT_WORD_CNT_HIGH     : std_logic_vector(31 downto 0) := x"00000009";
   constant LC_REG_STAT_WORD_CNT_LOW      : std_logic_vector(31 downto 0) := x"0000000a";
   constant LC_REG_ID                     : std_logic_vector(31 downto 0) := x"0000000b";
   -- registers default values
   constant LC_REG_DEF_DST_IP             : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_DST_MAC_HIGH       : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_DST_MAC_LOW        : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_DST_UDP            : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_DST_QP             : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_FIRST_PSN          : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_Q_KEY              : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_CONTROL            : std_logic_vector(31 downto 0) := x"00000000";
   constant LC_REG_DEF_ID                 : std_logic_vector(31 downto 0) := x"dfc0cec0";
   -- error codes
   constant LC_ERR_REG_NOT_EXIST          : std_logic_vector(31 downto 0) := x"00000001";
   constant LC_ERR_OVERFLOW               : std_logic_vector(31 downto 0) := x"00000010";
   constant LC_ERR_READ_ONLY              : std_logic_vector(31 downto 0) := x"00000100";
   constant LC_ERR_OK                     : std_logic_vector(31 downto 0) := x"00000000";
   -- unknown register value
   constant LC_REG_UNKNOWN                : std_logic_vector(31 downto 0) := x"11111111";


   type std_logic_vector32_array is array (natural range <>) of std_logic_vector(31 downto 0);
   type std_logic_vector64_array is array (natural range <>) of std_logic_vector(63 downto 0);
   
   type rx_fsm_t is (RCV_ST, DISCARD_ST);
   type tx_fsm_t is (IDLE_ST, SEND_ST);
   signal rx_fsm                 : rx_fsm_t := RCV_ST;
   signal tx_fsm                 : tx_fsm_t := IDLE_ST;   
   
   -- packet related registers (mac, ip, udp, qp, psn, q_key)
   signal reg_dst_ip             : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_dst_mac_high       : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_dst_mac_low        : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_dst_udp            : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_dst_qp             : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_first_psn          : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_q_key              : std_logic_vector(31 downto 0) := (others => '0');
   -- control register (run, clear)
   signal reg_control            : std_logic_vector(31 downto 0) := (others => '0');
   -- status registers (overflow)
   signal reg_status             : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_stat_word_cnt_high : std_logic_vector(31 downto 0) := (others => '0');
   signal reg_stat_word_cnt_low  : std_logic_vector(31 downto 0) := (others => '0');
   
   
   -- read related signals
   signal read_registers         : std_logic_vector32_array(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => (others => '0'));
   signal read_registers_val     : std_logic_vector(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => '0');
   signal read_registers_reg     : std_logic_vector32_array(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => (others => '0'));
   signal read_registers_val_reg : std_logic_vector(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => '0');
   signal read_request           : std_logic := '0';
   signal read_resp_done         : std_logic := '0';
   
   -- Info related signals
   signal info_request           : std_logic := '0';
   signal info_resp_done         : std_logic := '0';
   
   -- write related signals
   signal write_registers        : std_logic_vector64_array(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => (others => '0'));
   signal write_registers_val    : std_logic_vector(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => '0');
   signal write_registers_reg    : std_logic_vector64_array(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => (others => '0'));
   signal write_registers_val_reg    : std_logic_vector(LC_MAX_REGS_MESSEGE-1 downto 0) := (others => '0');
   signal write_request           : std_logic := '0';
   signal write_resp_done         : std_logic := '0';
   
   -- overflow related signald
   signal overflow_detected      : std_logic := '0';
   signal overflow_d             : std_logic := '0';
   signal overflow_send_done     : std_logic := '0';
   signal sending_overflow       : std_logic := '0';
   
   -- Inputs and Outputs related signals
   signal m_axis_tvalid_i        : std_logic := '0';
   signal m_axis_tlast_i         : std_logic := '0';   
   signal s_axis_tready_i        : std_logic := '0';
   signal data_valid             : std_logic := '0';
   signal data_length_valid      : std_logic := '0';
begin
  
   -- every message length is multiply of 4 bytes
   -- maximum message length is 7 registers (4 bytes 
   -- address and 4 bytes data) + 4 bytes magic number 
   data_length_valid <= '1' when RX_PACKET_LENGTH(1 downto 0) = "00" and 
      RX_PACKET_LENGTH(RX_PACKET_LENGTH'left downto 3) < LC_MAX_REGS_MESSEGE+1 else '0';
   data_valid <= S_AXIS_TVALID and data_length_valid;
   
   write_regs_assign: for i in 0 to write_registers'length-1 generate
      write_registers(i) <= S_AXIS_TDATA((i+1)*64+32-1 downto i*64+32);
      write_registers_val(i) <= and_vec8(S_AXIS_TKEEP((i+1)*64/8+32/8-1 downto i*64/8+32/8));
   end generate;
   
   read_regs_assign: for i in 0 to read_registers'length-1 generate
      read_registers(i) <= S_AXIS_TDATA((i+1)*32+32-1 downto i*32+32);
      read_registers_val(i) <= and_vec4(S_AXIS_TKEEP((i+1)*32/8+32/8-1 downto i*32/8+32/8));
   end generate;
      
   rx_proc: process(CLK) 
      variable write_err      : std_logic := '0';
      variable read_err       : std_logic := '0';
      begin 
         if rising_edge(CLK) then
            write_err := '0';
            if RESET = '1' then 
               s_axis_tready_i <= '0';
               rx_fsm <= RCV_ST;
               read_request <= '0';
               write_request <= '0';
               info_request <= '0';
               TX_DST_UDP <= (others => '0');
               TX_DST_IP  <= (others => '0');
               TX_DST_MAC <= (others => '0');
               read_registers_reg <= (others => (others => '0'));
               read_registers_val_reg  <= (others => '0');
               write_registers_reg <= (others => (others => '0'));
               write_registers_val_reg  <= (others => '0');
            else
               read_registers_reg <= read_registers;
               read_registers_val_reg  <= read_registers_val;
               write_registers_reg <= write_registers;
               write_registers_val_reg  <= write_registers_val;
            
               s_axis_tready_i <= '0';
               if data_valid = '1' then
                  TX_DST_UDP <= RX_SRC_UDP;
                  TX_DST_IP  <= RX_SRC_IP;
                  TX_DST_MAC <= RX_SRC_MAC;
                  case rx_fsm is 
                     when RCV_ST =>       
                        -- Write to registers    
                        if S_AXIS_TDATA(31 downto 0) = (LC_MAGIC_NUMBER & LC_WRITE) then
                           if write_resp_done = '1' and write_request = '1' then 
                              write_request <= '0';
                              s_axis_tready_i <= '1';
                              rx_fsm <= DISCARD_ST;
                           else
                              write_request <= '1';
                           end if;
                        -- Read from registers
                        elsif S_AXIS_TDATA(31 downto 0) = (LC_MAGIC_NUMBER & LC_READ) then
                           if read_resp_done = '1' and read_request = '1' then 
                              read_request <= '0';
                              s_axis_tready_i <= '1';
                              rx_fsm <= DISCARD_ST;
                           else
                              read_request <= '1';
                           end if;
                        -- Get info
                        elsif S_AXIS_TDATA(31 downto 0) = (LC_MAGIC_NUMBER & LC_INFO) then
                           if info_resp_done = '1' and info_request = '1' then 
                              info_request <= '0';
                              s_axis_tready_i <= '1';
                              rx_fsm <= DISCARD_ST;
                           else
                              info_request <= '1';
                           end if;
                        -- Discard current packet
                        else
                           rx_fsm <= DISCARD_ST;
                        end if;
                     
                     when DISCARD_ST =>
                        if (S_AXIS_TVALID and S_AXIS_TLAST and s_axis_tready_i) = '1' then
                           rx_fsm <= RCV_ST;
                        else
                           s_axis_tready_i <= '1';
                        end if;
                  end case;
               end if; -- data valid
            end if; -- reset
         end if; -- rising_edge
      end process;

   overflow_detect_proc: process(CLK) 
      begin 
         if rising_edge(CLK) then
            if RESET = '1' then 
               overflow_detected <= '0'; 
               overflow_d <= '0';
            else
               -- delay overflow signal
               overflow_d <= REG_CHANNEL_OVERFLOW;
               -- check if fifo overflowed
               if (not overflow_d and REG_CHANNEL_OVERFLOW) = '1' then
                  overflow_detected <= '1';
               elsif overflow_send_done = '1' then
                  overflow_detected <= '0';
               end if;
            end if; 
         end if;
      end process;  

   
   tx_proc: process(CLK) 
      variable pos   : natural range 0 to M_AXIS_TDATA'left := 0;
      variable tkeep_pos   : natural range 0 to M_AXIS_TKEEP'left := 0;
      begin 
         if rising_edge(CLK) then
            pos := 0;
            if RESET = '1' then 
               read_resp_done <= '0';
               overflow_send_done <= '0';
               write_resp_done <= '0';
               info_resp_done <= '0';
               m_axis_tvalid_i <= '0';
               sending_overflow <= '0';
               M_AXIS_TLAST <= '0';
               M_AXIS_TKEEP <= (others => '0');
               M_AXIS_TDATA <= (others => '0');   
               reg_dst_ip <= LC_REG_DEF_DST_IP;
               reg_dst_mac_high <= LC_REG_DEF_DST_MAC_HIGH;
               reg_dst_mac_low <= LC_REG_DEF_DST_MAC_LOW;
               reg_dst_udp <= LC_REG_DEF_DST_UDP;
               reg_dst_qp <= LC_REG_DEF_DST_QP;
               reg_first_psn <= LC_REG_DEF_FIRST_PSN;
               reg_q_key <= LC_REG_DEF_Q_KEY;
               reg_control <= LC_REG_DEF_CONTROL;
            else
               read_resp_done <= '0';
               write_resp_done <= '0';
               overflow_send_done <= '0';
               info_resp_done <= '0';
               m_axis_tvalid_i <= '0';
               M_AXIS_TLAST <= '0';
               M_AXIS_TKEEP <= (others => '0');
               M_AXIS_TDATA <= (others => '0');                      
               -- send write respond
               if (not sending_overflow and write_request and not write_resp_done) = '1' then
                  if m_axis_tvalid_i = '1' and M_AXIS_TREADY = '1' then
                     write_resp_done <= '1';
                  else
                     m_axis_tvalid_i <= '1';
                     M_AXIS_TLAST <= '1';
                     M_AXIS_TDATA(31 downto 0) <= LC_MAGIC_NUMBER & LC_RESP;
                     M_AXIS_TKEEP(3 downto 0) <= "1111";
                     for i in 0 to LC_MAX_REGS_MESSEGE-1 loop
                        pos := 64*i + 32;
                        if write_registers_val_reg(i) = '1' then
                           case write_registers_reg(i)(31 downto 0) is
                              when LC_REG_DST_IP =>       
                                 reg_dst_ip <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_IP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_DST_MAC_HIGH =>
                                 reg_dst_mac_high <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_MAC_HIGH;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_DST_MAC_LOW =>
                                 reg_dst_mac_low <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_MAC_LOW;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_DST_UDP =>
                                 reg_dst_udp <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_UDP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_DST_QP =>
                                 reg_dst_qp <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_QP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_FIRST_PSN =>
                                 reg_first_psn <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_FIRST_PSN;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_Q_KEY =>
                                 reg_q_key <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_Q_KEY;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_CONTROL =>
                                 reg_control <= write_registers_reg(i)(63 downto 32);
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_CONTROL;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_OK;
                              when LC_REG_STATUS => 
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STATUS;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_READ_ONLY;
                              when LC_REG_STAT_WORD_CNT_HIGH => 
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STAT_WORD_CNT_HIGH;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_READ_ONLY;
                              when LC_REG_STAT_WORD_CNT_LOW => 
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STAT_WORD_CNT_LOW;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_READ_ONLY;
                              when LC_REG_ID => 
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_ID;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_ERR_READ_ONLY;
                              when others =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_UNKNOWN;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= write_registers_reg(i)(31 downto 0);
                           end case;
                        end if;
                     end loop;
                  end if;               
               -- send read data
               elsif (not sending_overflow and read_request and not read_resp_done) = '1' then
                  if m_axis_tvalid_i = '1' and M_AXIS_TREADY = '1' then
                     read_resp_done <= '1';
                  else
                     m_axis_tvalid_i <= '1';
                     M_AXIS_TLAST <= '1';
                     M_AXIS_TDATA(31 downto 0) <= LC_MAGIC_NUMBER & LC_RESP;
                     M_AXIS_TKEEP(3 downto 0) <= "1111";
                     for i in 0 to LC_MAX_REGS_MESSEGE-1 loop
                        pos := 64*i + 32;
                        if read_registers_val_reg(i) = '1' then
                           case read_registers_reg(i)(31 downto 0) is
                              when LC_REG_DST_IP =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_IP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_dst_ip;
                              when LC_REG_DST_MAC_HIGH =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_MAC_HIGH;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_dst_mac_high;
                              when LC_REG_DST_MAC_LOW =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_MAC_LOW;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_dst_mac_low;
                              when LC_REG_DST_UDP =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_UDP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_dst_udp;
                              when LC_REG_DST_QP =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_DST_QP;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_dst_qp;
                              when LC_REG_FIRST_PSN =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_FIRST_PSN;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_first_psn;
                              when LC_REG_Q_KEY =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_Q_KEY;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_q_key;
                              when LC_REG_CONTROL =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_CONTROL;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_control;
                              when LC_REG_STATUS =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STATUS;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_status;            
                              when LC_REG_STAT_WORD_CNT_HIGH =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STAT_WORD_CNT_HIGH;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_stat_word_cnt_high;
                              when LC_REG_STAT_WORD_CNT_LOW =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_STAT_WORD_CNT_LOW;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= reg_stat_word_cnt_low; 
                              when LC_REG_ID =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_ID;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= LC_REG_DEF_ID; 
                              when others =>
                                 M_AXIS_TKEEP(7 + pos/8 downto pos/8) <= (others => '1');
                                 M_AXIS_TDATA(31 + pos downto pos) <= LC_REG_UNKNOWN;
                                 M_AXIS_TDATA(63 + pos downto 32 + pos) <= read_registers_reg(i)(31 downto 0); 
                           end case;
                        end if;
                     end loop;  
                  end if;  
               -- send info
               elsif (not sending_overflow and info_request and not info_resp_done) = '1' then
                  if m_axis_tvalid_i = '1' and M_AXIS_TREADY = '1' then
                     info_resp_done <= '1';
                  else
                     m_axis_tvalid_i <= '1';
                     M_AXIS_TLAST <= '1';
                     M_AXIS_TDATA(159 downto 0) <= HOST_NET_MASK & HOST_IP & x"0000" & HOST_MAC & LC_MAGIC_NUMBER & LC_RESP;
                     M_AXIS_TKEEP(19 downto 0) <= (others => '1');
                  end if;                    
               elsif (overflow_detected and not overflow_send_done) = '1' then 
                  if m_axis_tvalid_i = '1' and M_AXIS_TREADY = '1' then
                     overflow_send_done <= '1';
                     sending_overflow <= '0';
                  else
                     sending_overflow <= '1';
                     m_axis_tvalid_i <= '1';
                     M_AXIS_TLAST <= '1';
                     M_AXIS_TDATA(63 downto 0) <= LC_ERR_OVERFLOW & LC_MAGIC_NUMBER & LC_ERR;   
                     M_AXIS_TKEEP(64/8-1 downto 0) <= (others => '1');
                  end if;      
               end if;
            end if; 
         end if;
      end process;   


   udp_reg_input_reg_proc: process(CLK) 
      begin 
         if rising_edge(CLK) then
            if RESET = '1' then 
               reg_status <= (others => '0'); 
               reg_stat_word_cnt_high <= (others => '0'); 
               reg_stat_word_cnt_low <= (others => '0'); 
            else
               reg_status(reg_status'left downto 1) <= (others => '0'); 
               reg_status(0) <= REG_CHANNEL_OVERFLOW; 
               reg_stat_word_cnt_high <= REG_CHANNEL_WORD_CNT(63 downto 32); 
               reg_stat_word_cnt_low <= REG_CHANNEL_WORD_CNT(31 downto 0); 
            end if; 
         end if;
      end process;  

   
      -- control and status signals
      REG_CHANNEL_RUN         <= reg_control(0);
      REG_CHANNEL_CLEAR       <= reg_control(1);

      -- packets related signals
      -- mac/ip/udp
      REG_ROCE_TX_DEST_IP     <= reg_dst_ip;
      REG_ROCE_TX_DEST_MAC    <= reg_dst_mac_high(15 downto 0) & reg_dst_mac_low;
      REG_ROCE_TX_DEST_UDP    <= reg_dst_udp(15 downto 0);
      -- roce
      REG_ROCE_TX_DEST_QP     <= reg_dst_qp(23 downto 0);
      REG_ROCE_TX_PSN         <= reg_first_psn(23 downto 0);
      REG_ROCE_TX_Q_KEY       <= reg_q_key;

      RX_DST_UDP <= g_udp_port;
      TX_SRC_UDP <= g_udp_port;
      M_AXIS_TVALID <= m_axis_tvalid_i;
      S_AXIS_TREADY <= s_axis_tready_i;

   reg_debug_gen: if g_debug = true generate
      debug_vio_inst: entity work.debug_vio
      port map(
         clk         => CLK,
         probe_in0   => reg_dst_ip,
         probe_in1   => reg_dst_mac_high,
         probe_in2   => reg_dst_mac_low,
         probe_in3   => reg_dst_udp,
         probe_in4   => reg_dst_qp,
         probe_in5   => reg_first_psn,
         probe_in6   => reg_q_key,
         probe_in7   => reg_control,
         probe_in8   => reg_status,
         probe_in9   => reg_stat_word_cnt_high,
         probe_in10  => reg_stat_word_cnt_low,
         probe_in11  => LC_REG_DEF_ID
      );      
   end generate;
      
end Behavioral;
