-------------------------------------------------------------------------------
--! @file       ps2pl_glue.vhd
--! @author     Adrian Byszuk <adrian.byszuk@cern.ch>
--! @company    CERN
--! @date       2021-06-28
--! @brief      PS to PL glue logic and hardcoded config
-------------------------------------------------------------------------------


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

--! @brief PS to PL glue logic and hardcoded config signals
entity ps2pl_glue is
  generic (
    g_phyaddr       : natural := 9; --! Ethernet PHY MDIO address
    g_config_vector : natural := 0; --! Ethernet IP configuration vector
    g_config_valid  : std_logic_vector := "0"; --! Configuration vector is valid
    g_signal_detect : std_logic_vector := "1" --! SFP signal detected status
  );
  Port (
    --! @name Fundamental clock and reset
    pl_clk_i    : in std_logic; --! PL clock
    pl_resetn_i : in std_logic; --! Reset of the module, coming from PS
    reset_o     : out std_logic; --! Reset to the PL

    --! @name Ethernet config and status
    --! MAC status. Check Xilinx docs for detailed info
    status_vector_i : in std_logic_vector(15 downto 0);
    mdc_i           : in std_logic; --! Management clock, (<= 2.5MHz)
    mdc_clk_led_o   : out std_logic; --! MDC activity LED
    an_config_o     : out std_logic;
    --! Config data for autonegotiation
    an_config_vec_o : out std_logic_vector(15 downto 0);
    tx_disable_o    : out std_logic;
    --! @}

    --! @name EMIO GPIOs
    p_pres_i          : in  std_logic_vector(1 downto 0);
    pwr_cycle_req_o   : out std_logic;
    bckpl_servmod_i   : in  std_logic_vector(7 downto 0);
    bckpl_servmod_o   : out std_logic_vector(7 downto 0);
    bckpl_servmod_t_o : out std_logic_vector(7 downto 0);
    bckpl_rst_n_o     : out std_logic;
    psu_alert_i       : in  std_logic;
    f_rst_i           : in  std_logic;
    f_rst_o           : out std_logic;
    f_rst_t_o         : out std_logic;
    ps_emio_o         : out std_logic_vector(15 downto 0);
    ps_emio_i         : in  std_logic_vector(94 downto 0);
    ps_emio_t_i       : in  std_logic_vector(15 downto 0);
    ps_irq_o          : out std_logic_vector(1 downto 0);

    --! @name I2C bus IRQs
    wrflash_i2c_irq_i : in std_logic;
    bckpl_i2c_irq_i   : in std_logic;

    --! @name Constants module
    --! Addr of the PHY device(slave). A constant number
    phyaddr_o              : out std_logic_vector(4 downto 0);
    --! PHY IP configuration vector (consult with Xilinx documentation)
    configuration_vector_o : out std_logic_vector(4 downto 0);
    --! Configuration vector is valid
    configuration_valid_o  : out std_logic_vector(0 downto 0);
    --! Optical signal detected ('1' if not connected to an optical module)
    signal_detect_o : out std_logic_vector(0 downto 0);

    --! @name Slices module
    link_status_led_o : out std_logic;
    link_sync_led_o   : out std_logic;
    clk_src_sel_o     : out std_logic_vector(1 downto 0)
  );
end ps2pl_glue;

architecture Behavioral of ps2pl_glue is

  --! @name Port attributes for Xilinx IP Integrator
  ATTRIBUTE X_INTERFACE_INFO : STRING;
  ATTRIBUTE X_INTERFACE_PARAMETER : STRING;

  ATTRIBUTE X_INTERFACE_INFO OF pl_clk_i: signal IS "xilinx.com:signal:clock:1.0 pl_clk_i CLK";
  ATTRIBUTE X_INTERFACE_PARAMETER of pl_clk_i: SIGNAL is "ASSOCIATED_RESET pl_resetn_i";

  ATTRIBUTE X_INTERFACE_INFO OF pl_resetn_i: SIGNAL IS "xilinx.com:signal:reset:1.0 pl_resetn_i RST";
  ATTRIBUTE X_INTERFACE_PARAMETER of pl_resetn_i: SIGNAL is "POLARITY ACTIVE_LOW";

  ATTRIBUTE X_INTERFACE_INFO OF reset_o: SIGNAL IS "xilinx.com:signal:reset:1.0 reset_o RST";
  ATTRIBUTE X_INTERFACE_PARAMETER of reset_o: SIGNAL is "POLARITY ACTIVE_HIGH";

  ATTRIBUTE X_INTERFACE_INFO OF mdc_i: signal IS "xilinx.com:signal:clock:1.0 mdc_i CLK";

  ATTRIBUTE X_INTERFACE_INFO of ps_irq_o: SIGNAL is "xilinx.com:signal:interrupt:1.0 ps_irq_o INTERRUPT";
  ATTRIBUTE X_INTERFACE_INFO of wrflash_i2c_irq_i: SIGNAL is "xilinx.com:signal:interrupt:1.0 wrflash_i2c_irq_i INTERRUPT";
  ATTRIBUTE X_INTERFACE_INFO of bckpl_i2c_irq_i: SIGNAL is "xilinx.com:signal:interrupt:1.0 bckpl_i2c_irq_i INTERRUPT";
  --! @}

  -- Because we want to check if mdc_i<=2.5MHz, cnt must be up to 4096
  signal s_cnt     : unsigned(11 downto 0);
  signal s_mdc_led : std_logic;

  type t_pwr_state is (IDLE, RST);
  signal pwr_state : t_pwr_state;
  signal ps_emio2_d0 : std_logic;
  signal ps_emio2_p  : std_logic;

begin

  an_config_o     <= '0';
  an_config_vec_o <= x"D801";
  tx_disable_o    <= '0';

  phyaddr_o              <= std_logic_vector(to_unsigned(g_phyaddr, phyaddr_o'length));
  configuration_vector_o <= std_logic_vector(to_unsigned(g_config_vector, configuration_vector_o'length));
  configuration_valid_o  <= g_config_valid;
  signal_detect_o        <= g_signal_detect;

  reset_o <= '1' when (pl_resetn_i = '0') else
             '0';

  link_status_led_o <= status_vector_i(0);
  link_sync_led_o   <= status_vector_i(1);
  clk_src_sel_o     <= "11";

  calc_mdc: process(mdc_i, pl_resetn_i)
  begin

    if (pl_resetn_i = '0') then
      s_cnt <= (others => '0');
      s_mdc_led <= '0';

    elsif (rising_edge(mdc_i)) then

      if (s_cnt = 4096-2) then
        s_cnt 	<= (others=>'0');
        s_mdc_led <= '1';
      else
        s_mdc_led <= '0';
        s_cnt <= s_cnt + 1;
      end if;
    end if;

  end process;

  mdc_clk_led_o <= s_mdc_led;

  -- EMIO -> PL GPIO
  ps_emio_o(4 downto 0) <= p_pres_i(1 downto 0) & "000";

  ps_emio2_p <= '1' when (ps_emio2_d0 = '0' and ps_emio_i(2) = '1') else
                '0';
  process(pl_clk_i)
  begin
    if rising_edge(pl_clk_i) then
      ps_emio2_d0 <= ps_emio_i(2);
      if pl_resetn_i = '0' or (ps_emio2_p = '1' and ps_emio_i(1) = '1') then
        pwr_state <= IDLE;
        pwr_cycle_req_o <= '0';
      else

        if pwr_state = IDLE then
          pwr_cycle_req_o <= '0';
          if ps_emio2_p = '1' and ps_emio_i(1) = '0' then
            pwr_state <= RST;
          end if;

        elsif pwr_state = RST then
          pwr_cycle_req_o <= '1';
        end if;
      end if;
    end if;
  end process;

  -- EMIO 83..90
  ps_emio_o(12 downto 5)        <= bckpl_servmod_i(7 downto 0);
  bckpl_servmod_o(7 downto 0)   <= ps_emio_i(12 downto 5);
  bckpl_servmod_t_o(7 downto 0) <= ps_emio_t_i(12 downto 5);

  -- EMIO 91
  ps_emio_o(13) <= ps_emio_i(13);
  bckpl_rst_n_o <= ps_emio_i(13);
  -- EMIO 92
  ps_emio_o(14) <= psu_alert_i;
  -- EMIO 93
  ps_emio_o(15) <= f_rst_i;
  f_rst_o       <= ps_emio_i(15);
  f_rst_t_o     <= ps_emio_t_i(15);

  -- I2C irqs
  ps_irq_o(1 downto 0) <= bckpl_i2c_irq_i & wrflash_i2c_irq_i;

end Behavioral;
