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

-- 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 debug_control is
   port ( 
      -- Reset and clock
      RESET                : in  std_logic := '0';
      INIT_CLK             : in  std_logic := '0';
      CORE_CLK             : in  std_logic := '0';
      -- Debug control enable
      DEBUG_ON             : in  std_logic := '0';
      -- Initialization done
      INIT_DONE            : in  std_logic := '0';
      -- Test pattern generating and checking 
      TEST_PATTERN         : in  std_logic := '0';
      -- RX test pattern checking enable
      RX_TEST_PATTERN      : out std_logic := '0';
      -- TX test pattern generation enable
      TX_TEST_PATTERN      : out std_logic := '0';
      -- Loopback mode 
      LOOPBACK_MODE_CTRL   : in std_logic_vector(2 downto 0) := (others => '0');
      LOOPBACK_MODE        : out std_logic_vector(11 downto 0) := (others => '0');
      -- CMAC reset after change of loopback mode
      DEBUG_RESET          : out std_logic := '0'
   );
end debug_control;

architecture Behavioral of debug_control is
   constant LC_LOOPBACK_NORMAL         : std_logic_vector(2 downto 0) := (others => '0');
   signal loopback_mode_sel            : std_logic_vector(2 downto 0) := (others => '0');
   signal loopback_mode_sel_d          : std_logic_vector(2 downto 0) := (others => '0');
   
   constant LC_DEBUG_CHANGED_CNT_MAX   : natural := 16;
   signal loopback_changed             : std_logic := '0';
   signal test_pattern_cdc_rst         : std_logic := '0';
   signal debug_on_d                   : std_logic := '0';
   signal debug_changed                : std_logic := '0';
   signal debug_changed_cnt            : natural range 0 to LC_DEBUG_CHANGED_CNT_MAX := 0;


begin

   with LOOPBACK_MODE_CTRL select loopback_mode_sel <= 
      "001" when "001",  -- NEAR_END_PCS
      "010" when "010",  -- NEAR_END_PMA
      "110" when "011",   -- FAR_END_PCS
      "100" when "100",   -- FAR_END_PMA
      "000" when others;

   loopback_loop: for i in 0 to LOOPBACK_MODE'length/LOOPBACK_MODE_CTRL'length - 1 generate
      LOOPBACK_MODE((i+1)*LOOPBACK_MODE_CTRL'length - 1 downto i*LOOPBACK_MODE_CTRL'length) <= loopback_mode_sel;
   end generate;

   delay_proc: process(INIT_CLK)
      begin 
         if rising_edge(INIT_CLK) then
            if RESET = '1' then
               DEBUG_RESET <= '0';
               debug_on_d <= '0';
               loopback_mode_sel_d <= "000";
               debug_changed_cnt <= 0;
               debug_changed <= '0';
            else
               DEBUG_RESET <= '0';
               -- One cycle delay on loopback mode and debug signals
               loopback_mode_sel_d <= loopback_mode_sel;
               debug_on_d <= DEBUG_ON;

               -- Looking for change on DEBUG_ON and loopback_mode_sel signals
               -- Reset if change was found
               if debug_on_d /= DEBUG_ON or (loopback_mode_sel /= loopback_mode_sel_d and DEBUG_ON = '1') then
                  debug_changed <= '1';
                  debug_changed_cnt <= 0;
               elsif debug_changed = '1' then
                  if debug_changed_cnt < LC_DEBUG_CHANGED_CNT_MAX then
                     debug_changed_cnt <= debug_changed_cnt + 1;
                     DEBUG_RESET <= '1';
                  else
                     debug_changed <= '0';
                  end if;
               end if;
            end if;
         end if;
      end process;

   test_pattern_cdc_rst <= INIT_DONE and DEBUG_ON;

   tx_test_pattern_cdc_inst: entity work.xcdc
   generic map(
      g_length       => 2,
      g_rst_val      => '0',
      g_data_width   => 1
   )
   port map(
      RSTN        => test_pattern_cdc_rst,
      DCLK        => CORE_CLK,
      DATA_IN(0)  => TEST_PATTERN,
      DATA_OUT(0) => TX_TEST_PATTERN
   );
   
   rx_test_pattern_cdc_inst: entity work.xcdc
   generic map(
      g_length       => 2,
      g_rst_val      => '0',
      g_data_width   => 1
   )
   port map(
      RSTN        => test_pattern_cdc_rst,
      DCLK        => CORE_CLK,
      DATA_IN(0)  => TEST_PATTERN,
      DATA_OUT(0) => RX_TEST_PATTERN
   );   


end Behavioral;
