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

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

entity cmac_control is
   port (
      -- Core reset
      RESET                : in  std_logic := '0';
      
      -- Core clock
      CLK                  : in  std_logic := '0';

      -- Control signals
      RX_ENABLE            : out std_logic := '0';
      TX_ENABLE            : out std_logic := '0';      
      TX_SEND_RFI          : out std_logic := '0';
      TX_SEND_LFI          : out std_logic := '0';
      TX_SEND_IDLE         : out std_logic := '0';
      TX_PREAMBLE          : out std_logic_vector(55 downto 0) := (others => '0');
      
      -- Status signals
      RX_ALIGNED           : in  std_logic := '0';
      RX_RFI               : in  std_logic := '0';
      TX_OVERFLOW          : in  std_logic := '0';
      TX_UNDERFLOW         : in  std_logic := '0';
      
      -- User interface reset signals from CMAC
      USR_RX_RESET         : in  std_logic := '0';
      USR_TX_RESET         : in  std_logic := '0';
      
      -- Reset signals to CMAC
      RX_RESET             : out std_logic := '0';
      TX_RESET             : out std_logic := '0';
      CMAC_RESET           : out std_logic := '0';
      
      -- Reset signals to Core
      RX_CORE_RESET        : out std_logic := '0'; 
      TX_CORE_RESET        : out std_logic := '0';
      
      -- Initialization done
      INIT_DONE            : out std_logic := '0'
   );
end;

architecture Behavioral of cmac_control is

   constant LC_PREAMBLE          : std_logic_vector(55 downto 0) := x"55555555555555";

   type cmac_control_fsm_type is (CMAC_RST_ST, CMAC_RST_TXRX_ST, CMAC_WAIT_STABLE_ST, CMAC_WAIT_ALIGN_ST, CMAC_WAIT_RFI_ST, CMAC_WATCH_ST);
   signal cmac_ctrl_fsm          : cmac_control_fsm_type := CMAC_RST_ST;
   
   constant LC_CMAC_RST_CNT_MAX  : natural := 16;
   constant LC_TX_RST_CNT_MAX    : natural := 16;
   constant LC_RX_RST_CNT_MAX    : natural := 16;

   signal cmac_rst_cnt           : natural range 0 to LC_CMAC_RST_CNT_MAX := 0;
   signal tx_rst_cnt             : natural range 0 to LC_TX_RST_CNT_MAX := 0;
   signal rx_rst_cnt             : natural range 0 to LC_RX_RST_CNT_MAX := 0;   
   
   signal cmac_reset_i           : std_logic := '0';
   -- Enable Core datapath reset
   signal rst_core_tx_en         : std_logic := '0';
   signal rst_core_rx_en         : std_logic := '0';

   -- Enable CMAC datapath reset
   signal rst_tx_en              : std_logic := '0';
   signal rst_rx_en              : std_logic := '0';
   
   -- Internal reset signals
   signal rx_reset_i       : std_logic := '0';
   signal tx_reset_i       : std_logic := '0';
   
   signal rx_en            : std_logic := '0';
   signal tx_en            : std_logic := '0';
   signal tx_rfi_en        : std_logic := '0';

   signal reset_done       : std_logic := '0';
   
   signal init_done_i      : std_logic := '0';

begin
   
   reset_done <= not (USR_TX_RESET or USR_RX_RESET);

   cmac_ctrl_main_fsm_proc: process(CLK)
      begin
         if rising_edge(CLK) then
            if RESET = '1' then
               cmac_reset_i <= '1';
               rx_en <= '0';
               tx_en <= '0';
               tx_rfi_en <= '0';
               rst_tx_en <= '1';
               rst_rx_en <= '1';
               rst_core_tx_en <= '1';
               rst_core_rx_en <= '1';
               cmac_ctrl_fsm <= CMAC_RST_ST;
            else
               cmac_reset_i <= '0';
               init_done_i <= '0';
               rx_en <= '0';
               tx_en <= '0';
               tx_rfi_en <= '0';
               rst_tx_en <= '0';
               rst_rx_en <= '0';
               rst_core_tx_en <= '0';
               rst_core_rx_en <= '0';
               
               case cmac_ctrl_fsm is
                  -- Hold CMAC in reset for LC_CMAC_RST_CNT_MAX cycles
                  when CMAC_RST_ST =>
                     cmac_reset_i <= '1';
                     rst_tx_en <= '1';
                     rst_rx_en <= '1';
                     rst_core_tx_en <= '1';
                     rst_core_rx_en <= '1';
                     if cmac_rst_cnt < LC_CMAC_RST_CNT_MAX then
                        cmac_rst_cnt <= cmac_rst_cnt + 1;
                     else
                        cmac_rst_cnt <= 0;
                        cmac_ctrl_fsm <= CMAC_WAIT_STABLE_ST;
                     end if;
                  -- Hold CMAC TX and RX paths in reset for LC_CMAC_RST_CNT_MAX cycles
                  when CMAC_RST_TXRX_ST =>
                     rst_tx_en <= '1';
                     rst_rx_en <= '1';
                     rst_core_tx_en <= '1';
                     rst_core_rx_en <= '1';
                     if cmac_rst_cnt < LC_CMAC_RST_CNT_MAX then
                        cmac_rst_cnt <= cmac_rst_cnt + 1;
                     else
                        cmac_rst_cnt <= 0;
                        cmac_ctrl_fsm <= CMAC_WAIT_STABLE_ST;
                     end if;                     
                  -- Wait untial clock are stable (USER_TX ans RX reset is low)                                     
                  when CMAC_WAIT_STABLE_ST =>
                     rst_core_tx_en <= '1';
                     rst_core_rx_en <= '1';
                     if reset_done = '1' then
                        cmac_ctrl_fsm <= CMAC_WAIT_ALIGN_ST;
                     end if;
                     
                  -- Wait for CMAC alignment
                  when CMAC_WAIT_ALIGN_ST => 
                     rx_en <= '1';
                     tx_rfi_en <= '1';
                     rst_core_tx_en <= '1';
                     rst_core_rx_en <= '1';
                     if RX_ALIGNED = '1' then
                        cmac_ctrl_fsm <= CMAC_WAIT_RFI_ST;
                     end if;    
                                      
                  -- Wait for receiver not sending RFI
                  when CMAC_WAIT_RFI_ST => 
                     rx_en <= '1';
                     rst_core_tx_en <= '1';
                     rst_core_rx_en <= '1';
                     if RX_RFI = '0' then
                        cmac_ctrl_fsm <= CMAC_WATCH_ST;
                     end if;
                     
                  -- Watch CMAC status
                  when CMAC_WATCH_ST =>
                     init_done_i <= '1';
                     tx_en <= '1';
                     rx_en <= '1';
                     if TX_UNDERFLOW = '1' or TX_OVERFLOW = '1' then
                        cmac_ctrl_fsm <= CMAC_RST_TXRX_ST;
                     end if;
                     
                     if RX_ALIGNED = '0' or reset_done = '0' then
                        cmac_ctrl_fsm <= CMAC_RST_ST;
                     end if;
                     
               end case;
            end if;
         end if;
      end process;
  
   -- CMAC core reset
   CMAC_RESET <= cmac_reset_i;
   -- Datapaths of CMAC core resets
   TX_RESET <= rst_tx_en;
   RX_RESET <= rst_rx_en;
   -- This core resets
   TX_CORE_RESET <= rst_core_tx_en or USR_TX_RESET;
   RX_CORE_RESET <= rst_core_rx_en or USR_RX_RESET;
   -- Enable RX and TX (To CMAC)
   RX_ENABLE <= rx_en;
   TX_ENABLE <= tx_en;
   -- Send RFI, LFI or IDLE (To CMAC)
   TX_SEND_RFI <= tx_rfi_en; 
   TX_SEND_LFI <= '0';
   TX_SEND_IDLE <= '0';
   -- TX preamble
   TX_PREAMBLE <= LC_PREAMBLE;
   -- Initialization done and successful
   INIT_DONE <= init_done_i;

end;
