--------------------------------------------------------------------------------
-- CERN BE-CO-HT
--
-- https://www.ohwr.org/projects/wr2rf-vme
--------------------------------------------------------------------------------
--
-- unit name:   wr2rf_lemo
--
-- description: Provides iobs and lemo input/output selection
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------

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

library unisim;
use unisim.vcomponents.all;

entity wr2rf_lemo is

   port (
      clk_sys_i        : in  std_logic;
      rst_sys_n_i      : in  std_logic;
      clk125m_i        : in  std_logic;

      tmg_clk_i        : in  std_logic_vector(2 downto 1);
      tmg_io_i         : in  std_logic_vector(4 downto 1);
      tmg_clk_o        : out std_logic_vector(2 downto 1);
      tmg_io_o         : out std_logic_vector(4 downto 1);

      -- lemo mux selections
      rf1_t1_start_sel_i :  in std_logic_vector(5 downto 0);
      rf1_t2_start_sel_i :  in std_logic_vector(5 downto 0);
      rf2_t1_start_sel_i :  in std_logic_vector(5 downto 0);
      rf2_t2_start_sel_i :  in std_logic_vector(5 downto 0);
      rf1_t1_stop_sel_i  :  in std_logic_vector(5 downto 0);
      rf1_t2_stop_sel_i  :  in std_logic_vector(5 downto 0);
      rf2_t1_stop_sel_i  :  in std_logic_vector(5 downto 0);
      rf2_t2_stop_sel_i  :  in std_logic_vector(5 downto 0);

      -- outputs
      rf1_t1_start_o   : out std_logic;
      rf1_t2_start_o   : out std_logic;
      rf2_t1_start_o   : out std_logic;
      rf2_t2_start_o   : out std_logic;
      rf1_t1_stop_o    : out std_logic;
      rf1_t2_stop_o    : out std_logic;
      rf2_t1_stop_o    : out std_logic;
      rf2_t2_stop_o    : out std_logic;

      -- debug signal options
      wrs_rx_valid_i   : in  std_logic;
      wrs_tx_valid_i   : in  std_logic;
      nco_reset_i      : in  std_logic;
      rf1_nco_reset_cdelayed_i : in  std_logic;
      rf2_nco_reset_cdelayed_i : in  std_logic;
      rf1_frev_i       : in  std_logic;
      rf1_frev_main_i  : in  std_logic;
      rf2_frev_i       : in  std_logic;
      rf2_frev_main_i  : in  std_logic;
      rf_rst_i         : in  std_logic;
      lemo_gpio_i      : in  std_logic_vector(2 downto 1);

      softstart_sel_i     : in  std_logic_vector(3 downto 0);
      softstop_sel_i      : in  std_logic_vector(3 downto 0);
      rf1_t1_softstart_i  : in  std_logic;
      rf1_t2_softstart_i  : in  std_logic;
      rf2_t1_softstart_i  : in  std_logic;
      rf2_t2_softstart_i  : in  std_logic;
      rf1_t1_softstop_i   : in  std_logic;
      rf1_t2_softstop_i   : in  std_logic;
      rf2_t1_softstop_i   : in  std_logic;
      rf2_t2_softstop_i   : in  std_logic;
      lemo_ext_sel_i      : in  std_logic_vector(6 downto 1);

      -- debug signal selectors
      tmgio1_osel_i     : in  std_logic_vector(13 downto 0);
      tmgio2_osel_i     : in  std_logic_vector(13 downto 0);
      tmgio3_osel_i     : in  std_logic_vector(13 downto 0);
      tmgio4_osel_i     : in  std_logic_vector(13 downto 0);
      tmgclk1_osel_i    : in  std_logic_vector(13 downto 0);
      tmgclk2_osel_i    : in  std_logic_vector(13 downto 0) );

   attribute keep_hierarchy               : STRING;
   attribute keep_hierarchy of wr2rf_lemo : entity is "yes";

end entity;

architecture rtl of wr2rf_lemo is

   signal lemo_in         : std_logic_vector(6 downto 1);
   signal lemo_out        : std_logic_vector(6 downto 1);
   signal lemo_in_r       : std_logic_vector(6 downto 1);
   signal lemo_out_r      : std_logic_vector(6 downto 1);
   signal lemo_out_h      : std_logic_vector(6 downto 1);
   signal lemo_ext        : std_logic_vector(6 downto 1);      
   signal lemo_out_iob    : std_logic_vector(6 downto 1);  
   signal lemo_out_iob_r  : std_logic_vector(6 downto 1);   
   signal lemo_in_syncd   : std_logic_vector(6 downto 1);

   signal rf1_t1_start_sel_r : std_logic_vector(6 downto 1);
   signal rf1_t2_start_sel_r : std_logic_vector(6 downto 1);
   signal rf2_t1_start_sel_r : std_logic_vector(6 downto 1);
   signal rf2_t2_start_sel_r : std_logic_vector(6 downto 1);
   signal rf1_t1_stop_sel_r  : std_logic_vector(6 downto 1);
   signal rf1_t2_stop_sel_r  : std_logic_vector(6 downto 1);
   signal rf2_t1_stop_sel_r  : std_logic_vector(6 downto 1);
   signal rf2_t2_stop_sel_r  : std_logic_vector(6 downto 1);

   signal rf1_t1_start       : std_logic;
   signal rf1_t2_start       : std_logic;
   signal rf2_t1_start       : std_logic;
   signal rf2_t2_start       : std_logic;
   signal rf1_t1_stop        : std_logic;
   signal rf1_t2_stop        : std_logic;
   signal rf2_t1_stop        : std_logic;
   signal rf2_t2_stop        : std_logic;
   signal rf1_t1_start_r     : std_logic_vector(3 downto 0);
   signal rf1_t2_start_r     : std_logic_vector(3 downto 0);
   signal rf2_t1_start_r     : std_logic_vector(3 downto 0);
   signal rf2_t2_start_r     : std_logic_vector(3 downto 0);
   signal rf1_t1_stop_r      : std_logic;
   signal rf1_t2_stop_r      : std_logic;
   signal rf2_t1_stop_r      : std_logic;
   signal rf2_t2_stop_r      : std_logic;

   signal start              : std_logic;
   signal stop               : std_logic;
   signal softstart          : std_logic;
   signal softstop           : std_logic;

   attribute IOB  : string;
   attribute keep : string;
   attribute IOB  of lemo_in_r      : signal is "true";
   attribute IOB  of lemo_out_iob_r : signal is "true";
   attribute keep of lemo_in_r      : signal is "true";
   attribute keep of lemo_out_iob_r : signal is "true";

begin

   -- lemo inputs to the fpga
   lemo_in(6) <= tmg_clk_i(2);
   lemo_in(5) <= tmg_clk_i(1);
   lemo_in(4) <= tmg_io_i(4);
   lemo_in(3) <= tmg_io_i(3);
   lemo_in(2) <= tmg_io_i(2);
   lemo_in(1) <= tmg_io_i(1);

   process (clk_sys_i) is
   begin
      if rising_edge(clk_sys_i) then
         lemo_in_r          <= lemo_in;
         rf1_t1_start_sel_r <= rf1_t1_start_sel_i;
         rf1_t2_start_sel_r <= rf1_t2_start_sel_i;
         rf2_t1_start_sel_r <= rf2_t1_start_sel_i;
         rf2_t2_start_sel_r <= rf2_t2_start_sel_i;
         rf1_t1_stop_sel_r  <= rf1_t1_stop_sel_i;
         rf1_t2_stop_sel_r  <= rf1_t2_stop_sel_i;
         rf2_t1_stop_sel_r  <= rf2_t1_stop_sel_i;
         rf2_t2_stop_sel_r  <= rf2_t2_stop_sel_i;
      end if;
   end process;

   -- input wiring
   rf1_t1_start <= lemo_in_r(1) when rf1_t1_start_sel_r(1) = '1' else
                   lemo_in_r(2) when rf1_t1_start_sel_r(2) = '1' else
                   lemo_in_r(3) when rf1_t1_start_sel_r(3) = '1' else
                   lemo_in_r(4) when rf1_t1_start_sel_r(4) = '1' else
                   lemo_in_r(5) when rf1_t1_start_sel_r(5) = '1' else
                   lemo_in_r(6) when rf1_t1_start_sel_r(6) = '1' else '0';
   rf1_t2_start <= lemo_in_r(1) when rf1_t2_start_sel_r(1) = '1' else
                   lemo_in_r(2) when rf1_t2_start_sel_r(2) = '1' else
                   lemo_in_r(3) when rf1_t2_start_sel_r(3) = '1' else
                   lemo_in_r(4) when rf1_t2_start_sel_r(4) = '1' else
                   lemo_in_r(5) when rf1_t2_start_sel_r(5) = '1' else
                   lemo_in_r(6) when rf1_t2_start_sel_r(6) = '1' else '0';
   rf2_t1_start <= lemo_in_r(1) when rf2_t1_start_sel_r(1) = '1' else
                   lemo_in_r(2) when rf2_t1_start_sel_r(2) = '1' else
                   lemo_in_r(3) when rf2_t1_start_sel_r(3) = '1' else
                   lemo_in_r(4) when rf2_t1_start_sel_r(4) = '1' else
                   lemo_in_r(5) when rf2_t1_start_sel_r(5) = '1' else
                   lemo_in_r(6) when rf2_t1_start_sel_r(6) = '1' else '0';
   rf2_t2_start <= lemo_in_r(1) when rf2_t2_start_sel_r(1) = '1' else
                   lemo_in_r(2) when rf2_t2_start_sel_r(2) = '1' else
                   lemo_in_r(3) when rf2_t2_start_sel_r(3) = '1' else
                   lemo_in_r(4) when rf2_t2_start_sel_r(4) = '1' else
                   lemo_in_r(5) when rf2_t2_start_sel_r(5) = '1' else
                   lemo_in_r(6) when rf2_t2_start_sel_r(6) = '1' else '0';

   rf1_t1_stop  <= lemo_in_r(1) when rf1_t1_stop_sel_r(1) = '1' else
                   lemo_in_r(2) when rf1_t1_stop_sel_r(2) = '1' else
                   lemo_in_r(3) when rf1_t1_stop_sel_r(3) = '1' else
                   lemo_in_r(4) when rf1_t1_stop_sel_r(4) = '1' else
                   lemo_in_r(5) when rf1_t1_stop_sel_r(5) = '1' else
                   lemo_in_r(6) when rf1_t1_stop_sel_r(6) = '1' else '0';
   rf1_t2_stop  <= lemo_in_r(1) when rf1_t2_stop_sel_r(1) = '1' else
                   lemo_in_r(2) when rf1_t2_stop_sel_r(2) = '1' else
                   lemo_in_r(3) when rf1_t2_stop_sel_r(3) = '1' else
                   lemo_in_r(4) when rf1_t2_stop_sel_r(4) = '1' else
                   lemo_in_r(5) when rf1_t2_stop_sel_r(5) = '1' else
                   lemo_in_r(6) when rf1_t2_stop_sel_r(6) = '1' else '0';
   rf2_t1_stop  <= lemo_in_r(1) when rf2_t1_stop_sel_r(1) = '1' else
                   lemo_in_r(2) when rf2_t1_stop_sel_r(2) = '1' else
                   lemo_in_r(3) when rf2_t1_stop_sel_r(3) = '1' else
                   lemo_in_r(4) when rf2_t1_stop_sel_r(4) = '1' else
                   lemo_in_r(5) when rf2_t1_stop_sel_r(5) = '1' else
                   lemo_in_r(6) when rf2_t1_stop_sel_r(6) = '1' else '0';
   rf2_t2_stop  <= lemo_in_r(1) when rf2_t2_stop_sel_r(1) = '1' else
                   lemo_in_r(2) when rf2_t2_stop_sel_r(2) = '1' else
                   lemo_in_r(3) when rf2_t2_stop_sel_r(3) = '1' else
                   lemo_in_r(4) when rf2_t2_stop_sel_r(4) = '1' else
                   lemo_in_r(5) when rf2_t2_stop_sel_r(5) = '1' else
                   lemo_in_r(6) when rf2_t2_stop_sel_r(6) = '1' else '0';

   process (clk_sys_i) is
   begin
      if rising_edge(clk_sys_i) then
         -- to support using the same lemo for stop + start, we delay the start wrt stop
         rf1_t1_start_r <= rf1_t1_start & rf1_t1_start_r(3 downto 1);
         rf1_t2_start_r <= rf1_t2_start & rf1_t2_start_r(3 downto 1);
         rf2_t1_start_r <= rf2_t1_start & rf2_t1_start_r(3 downto 1);
         rf2_t2_start_r <= rf2_t2_start & rf2_t2_start_r(3 downto 1);
         rf1_t1_stop_r  <= rf1_t1_stop;
         rf1_t2_stop_r  <= rf1_t2_stop;
         rf2_t1_stop_r  <= rf2_t1_stop;
         rf2_t2_stop_r  <= rf2_t2_stop;
      end if;
   end process;

   rf1_t1_start_o <= rf1_t1_start_r(0);
   rf1_t2_start_o <= rf1_t2_start_r(0);
   rf2_t1_start_o <= rf2_t1_start_r(0);
   rf2_t2_start_o <= rf2_t2_start_r(0);
   rf1_t1_stop_o  <= rf1_t1_stop_r;
   rf1_t2_stop_o  <= rf1_t2_stop_r;
   rf2_t1_stop_o  <= rf2_t1_stop_r;
   rf2_t2_stop_o  <= rf2_t2_stop_r;

   ------------------------------------------------------------------------
   -- OUTPUTS
   ------------------------------------------------------------------------
   softstart <= rf1_t1_softstart_i when softstart_sel_i(0) = '1' else
                rf1_t2_softstart_i when softstart_sel_i(1) = '1' else
                rf2_t1_softstart_i when softstart_sel_i(2) = '1' else
                rf2_t2_softstart_i when softstart_sel_i(3) = '1' else '0';
   softstop  <= rf1_t1_softstop_i  when softstop_sel_i(0)  = '1' else
                rf1_t2_softstop_i  when softstop_sel_i(1)  = '1' else
                rf2_t1_softstop_i  when softstop_sel_i(2)  = '1' else
                rf2_t2_softstop_i  when softstop_sel_i(3)  = '1' else '0';   

   lemo_out(1) <= nco_reset_i              when tmgio1_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgio1_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgio1_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgio1_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgio1_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgio1_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgio1_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgio1_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgio1_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgio1_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgio1_osel_i(10) = '1' else
                  rf_rst_i                 when tmgio1_osel_i(11) = '1' else
                  softstop                 when tmgio1_osel_i(12) = '1' else
                  softstart                when tmgio1_osel_i(13) = '1' else '0';
   lemo_out(2) <= nco_reset_i              when tmgio2_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgio2_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgio2_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgio2_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgio2_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgio2_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgio2_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgio2_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgio2_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgio2_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgio2_osel_i(10) = '1' else
                  rf_rst_i                 when tmgio2_osel_i(11) = '1' else
                  softstop                 when tmgio2_osel_i(12) = '1' else
                  softstart                when tmgio2_osel_i(13) = '1' else '0';
   lemo_out(3) <= nco_reset_i              when tmgio3_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgio3_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgio3_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgio3_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgio3_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgio3_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgio3_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgio3_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgio3_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgio3_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgio3_osel_i(10) = '1' else
                  rf_rst_i                 when tmgio3_osel_i(11) = '1' else
                  softstop                 when tmgio3_osel_i(12) = '1' else
                  softstart                when tmgio3_osel_i(13) = '1' else '0';
   lemo_out(4) <= nco_reset_i              when tmgio4_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgio4_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgio4_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgio4_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgio4_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgio4_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgio4_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgio4_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgio4_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgio4_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgio4_osel_i(10) = '1' else
                  rf_rst_i                 when tmgio4_osel_i(11) = '1' else
                  softstop                 when tmgio4_osel_i(12) = '1' else
                  softstart                when tmgio4_osel_i(13) = '1' else '0';

   lemo_out(5) <= nco_reset_i              when tmgclk1_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgclk1_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgclk1_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgclk1_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgclk1_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgclk1_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgclk1_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgclk1_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgclk1_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgclk1_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgclk1_osel_i(10) = '1' else
                  rf_rst_i                 when tmgclk1_osel_i(11) = '1' else
                  softstop                 when tmgclk1_osel_i(12) = '1' else
                  softstart                when tmgclk1_osel_i(13) = '1' else '0';
   lemo_out(6) <= nco_reset_i              when tmgclk2_osel_i(0)  = '1' else
                  rf1_nco_reset_cdelayed_i when tmgclk2_osel_i(1)  = '1' else
                  rf2_nco_reset_cdelayed_i when tmgclk2_osel_i(2)  = '1' else
                  rf1_frev_i               when tmgclk2_osel_i(3)  = '1' else
                  rf1_frev_main_i          when tmgclk2_osel_i(4)  = '1' else
                  rf2_frev_i               when tmgclk2_osel_i(5)  = '1' else
                  rf2_frev_main_i          when tmgclk2_osel_i(6)  = '1' else
                  wrs_rx_valid_i           when tmgclk2_osel_i(7)  = '1' else
                  wrs_tx_valid_i           when tmgclk2_osel_i(8)  = '1' else
                  lemo_gpio_i(1)           when tmgclk2_osel_i(9)  = '1' else
                  lemo_gpio_i(2)           when tmgclk2_osel_i(10) = '1' else
                  rf_rst_i                 when tmgclk2_osel_i(11) = '1' else
                  softstop                 when tmgclk2_osel_i(12) = '1' else
                  softstart                when tmgclk2_osel_i(13) = '1' else '0';

   -- We register all the clock signals in the 125 MHz domain
   -- whilst this is a CDC for signals in the sys domain. The clocks are in
   -- phase _and_ the path _is_ timed. There isn't a blanket async clk group
   -- constraint in force.
   process(clk125m_i) is
   begin
      if rising_edge(clk125m_i) then
         lemo_out_r     <= lemo_out;
         lemo_out_h     <= lemo_out_r;
         lemo_out_iob_r <= lemo_out_iob;
      end if;
   end process;

   g_pulse_ext : for i in 1 to 6 generate
   begin
      pulse_ext_i : entity work.gc_extend_pulse
         generic map (
            g_width    => 128 )
         port map (
            clk_i      => clk125m_i,
            rst_n_i    => rst_sys_n_i, -- <o>
            pulse_i    => lemo_out_h(i),
            extended_o => lemo_ext(i) );
   end generate;

   lemo_out_iob(6) <= lemo_out_h(6) when lemo_ext_sel_i(6) = '0' else lemo_ext(6);
   lemo_out_iob(5) <= lemo_out_h(5) when lemo_ext_sel_i(5) = '0' else lemo_ext(5);
   lemo_out_iob(4) <= lemo_out_h(4) when lemo_ext_sel_i(4) = '0' else lemo_ext(4);
   lemo_out_iob(3) <= lemo_out_h(3) when lemo_ext_sel_i(3) = '0' else lemo_ext(3);
   lemo_out_iob(2) <= lemo_out_h(2) when lemo_ext_sel_i(2) = '0' else lemo_ext(2);
   lemo_out_iob(1) <= lemo_out_h(1) when lemo_ext_sel_i(1) = '0' else lemo_ext(1);   

   -- Current hardwiring
   tmg_clk_o(2) <= lemo_out_iob_r(6);
   tmg_clk_o(1) <= lemo_out_iob_r(5);
   tmg_io_o(4)  <= lemo_out_iob_r(4);
   tmg_io_o(3)  <= lemo_out_iob_r(3);
   tmg_io_o(2)  <= lemo_out_iob_r(2);
   tmg_io_o(1)  <= lemo_out_iob_r(1);

end architecture;
