--=============================================================================
--! @file DUTInterface_AIDA_rtl.vhd
--=============================================================================
--
-------------------------------------------------------------------------------
-- --
-- University of Bristol, High Energy Physics Group.
-- --
------------------------------------------------------------------------------- --
-- VHDL Architecture fmc_mTLU_lib.DUTInterface_AIDA.rtl
--
--------------------------------------------------------------------------------
--
LIBRARY ieee;
USE ieee.std_logic_1164.all;

--! @brief "AIDA Style" Interface to a Device Under Test (DUT) connector.
--! factorized from original DUTInterfaces_rtl.vhd firmware.
--!
--! @author David Cussans , David.Cussans@bristol.ac.uk
--!
--! @date 1/Sept/2015
--!
--! @version v0.1
--!
--! @details
--

ENTITY DUTInterface_AIDA IS
   GENERIC( 
     g_IPBUS_WIDTH : positive := 32;
     g_GENERATE_CLOCK : boolean := FALSE; --! Set true to generate a clock to DUT in logic ( rather than using clock generated in clock generator chip )
     g_NUM_TRIGNUM_BITS_TO_CLOCKOUT : positive := 16;
     g_NUM_TRIGNUM_GUARDBITS : positive := 4
   );
   PORT( 
      clk_4x_logic_i          : IN     std_logic;
      strobe_4x_logic_i       : IN     std_logic;      --! goes high every 4th clock cycle
      trigger_counter_i       : IN     std_logic_vector (g_IPBUS_WIDTH-1 DOWNTO 0);  --! Number of trigger events since last reset
      clockout_trigger_flag_i : in     std_logic;      --! Set high to clock out trigger counter on rst/dut-clock line
      trigger_i               : IN     std_logic;      --! goes high when trigger logic issues a trigger
      reset_or_clk_to_dut_i   : IN     std_logic;      --! Synchronization signal. Passed to DUT pins
      shutter_to_dut_i        : IN     std_logic;      --! Goes high to indicate data-taking active. Gets passed to DUT pins
      busy_o                  : OUT    std_logic;      --! goes high when DUT is busy or vetoed by shutter
      
      -- Signals to/from DUT
      dut_busy_i       : IN     std_logic;     --! BUSY input from DUTs
      dut_clk_o        : OUT    std_logic;     --! clocks trigger data when in EUDET mode
      dut_reset_or_clk_o : OUT    std_logic;     --! Either reset line or trigger
      dut_shutter_o      : OUT    std_logic;     --! Shutter output. Output 0 (RJ45) has no shutter signal
      dut_trigger_o      : OUT    std_logic     --! Trigger output

   );

-- Declarations

END ENTITY DUTInterface_AIDA ;

--
ARCHITECTURE rtl OF DUTInterface_AIDA IS

  signal s_strobe_4x_logic_d1 : std_logic;
  signal s_dut_clk : std_logic := '0';  -- Clock to be sent to DUT connectors ( before final register )
  signal s_dut_clk_sr : std_logic_vector(2 downto 0) := "001"; --! Gets shifted out by clk_4x logic. Loaded by strobe_4x_logic
  signal s_stretch_trig_in : std_logic := '0';  -- ! stretched version of trigger_i 
  signal s_stretch_trig_in_sr : std_logic_vector(2 downto 0) := "111"; --! Gets shifted out by clk_4x logic. Loaded by trigger_i
  signal s_trigger_out : std_logic := '0';  -- ! trigger shifted to start on strobe_4x_logic

  -- Set length of output trigger here ( output length = length of this vector + 1 ) 
  signal s_trigger_out_sr : std_logic_vector(2 downto 0) := ( others => '1'); --! Gets shifted out by clk_4x logic. Loaded by strobe_4x_logic.
  
  signal s_trigger_number_sr : std_logic_vector(g_NUM_TRIGNUM_BITS_TO_CLOCKOUT-1 downto 0);
  signal s_trigger_number_guard_sr : std_logic_vector( (g_NUM_TRIGNUM_BITS_TO_CLOCKOUT+g_NUM_TRIGNUM_GUARDBITS) -1 downto 0);
  signal s_busy_clocking_trigger_number : std_logic;
  
BEGIN


  -- purpose: drives dut_reset_or_clk_o
  -- type   : combinational
  -- inputs : clk_4x_logic_i 
  p_dutClkRst: process (clk_4x_logic_i ) is
  begin  -- process p_dutClkRst

    if ( rising_edge(clk_4x_logic_i) and  strobe_4x_logic_i = '1' ) then
      
      if clockout_trigger_flag_i = '1' then -- if flag set then clock out trigger number

        if (s_trigger_out_sr(0) = '1' and s_trigger_number_guard_sr(0) = '0') then -- If we have a trigger then load shift register with trigger number
          s_trigger_number_sr <= trigger_counter_i(s_trigger_number_sr'range);
          s_trigger_number_guard_sr <= ( others =>'1');
        else
          s_trigger_number_sr <= '0' & s_trigger_number_sr(s_trigger_number_sr'left downto 1);
          s_trigger_number_guard_sr <= '0' & s_trigger_number_guard_sr(s_trigger_number_guard_sr'left downto 1);
          dut_reset_or_clk_o <= s_trigger_number_sr(0); -- Clock out trigger number LSB first.
        end if;

      else
        -- Copy reset/clk signal straight through
        dut_reset_or_clk_o <= reset_or_clk_to_dut_i;        
      end if;
    end if;
  end process p_dutClkRst;

  s_busy_clocking_trigger_number <= s_trigger_number_guard_sr(0);
  
  dut_shutter_o <= shutter_to_dut_i;

  -- Optional generation of clock to DUT
  gen_GenerateClock: if g_generate_clock generate 

    -- purpose: generates a clock from 4x clock and strobe ( high once every 4 cycles )
    -- should produce 11001100... etc. ie. 40MHz clock from 160MHz clock
    -- type   : combinational
    -- inputs : clk_4x_logic_i , strobe_4x_i
    -- outputs: s_dut_clk
    p_dut_clk_gen: process (clk_4x_logic_i , strobe_4x_logic_i) is
    begin  -- process p_dut_clk_gen
      if rising_edge(clk_4x_logic_i) then
        if (strobe_4x_logic_i = '1') then
          s_dut_clk <= '1';
          s_dut_clk_sr <= "001";
        else
          s_dut_clk <= s_dut_clk_sr(0);
          s_dut_clk_sr <= '0' & s_dut_clk_sr(s_dut_clk_sr'left downto 1);          
        end if;
      end if;
    end process p_dut_clk_gen;
  else generate
    
    s_dut_clk <= '0'; -- Set clock to zero and rely on external clock generator chip

  end generate;  
  

  -- purpose: re-times a single cycle pulse on trigger on clk_4x_logic onto clk_logic 
  -- type   : combinational
  -- inputs : clk_4x_logic_i , strobe_4x_logic_i , trigger_i
  -- outputs: s_premask_trigger_to_dut
  p_dut_trig_retime: process (clk_4x_logic_i , strobe_4x_logic_i , trigger_i) is
  begin  -- process p_dut_trig_retime
    if rising_edge(clk_4x_logic_i)  then

      -- Stretch trigger_i pulse to 4 clock cycles on clk4x
      if trigger_i = '1' then
        s_stretch_trig_in <= '1';
        s_stretch_trig_in_sr <= ( others => '1' );
      else
        s_stretch_trig_in <= s_stretch_trig_in_sr(0);
        s_stretch_trig_in_sr <= '0' & s_stretch_trig_in_sr(s_stretch_trig_in_sr'left downto 1);
      end if;

      -- 
      if (strobe_4x_logic_i  = '1') and ( s_stretch_trig_in = '1' ) then
        s_trigger_out <= '1';
        s_trigger_out_sr <= ( others => '1' );
      else
        s_trigger_out <= s_trigger_out_sr(0);
        s_trigger_out_sr <= '0' & s_trigger_out_sr(s_trigger_out_sr'left downto 1);
      end if;
      
    end if;
  end process p_dut_trig_retime;

    
  -- purpose: register for internal signals and output signals
  -- type   : combinational
  -- inputs : clk_4x_logic_i , strobe_4x_logic_i , s_veto
  -- outputs: busy_o
  register_signals: process (clk_4x_logic_i)-- , strobe_4x_logic_i , s_veto)
  begin  -- process register_signals
    if rising_edge(clk_4x_logic_i) then

      s_strobe_4x_logic_d1 <= strobe_4x_logic_i;

      busy_o <= dut_busy_i or s_busy_clocking_trigger_number;
      
      dut_clk_o <= s_dut_clk ;

      dut_trigger_o <= s_trigger_out;
      
    end if;
  end process register_signals;

  
END ARCHITECTURE rtl;

