--=============================================================================
--! @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
--
-- 13/March/19 - added ability to shift out trigger number.

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;      --! By default 160MHz. Rising edge active
      logic_reset_i           : IN     std_logic ;     --! High for one 
      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
      not_clockout_trigger_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_busy, s_dut_clk_d1, s_trigger_out_d1, s_dut_reset_or_clk : std_logic := '0';
  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 => '0'); --! 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) := (others => '0'); --! Shift register loaded with trigger(event) number
  signal s_trigger_number_guard_sr : std_logic_vector( (g_NUM_TRIGNUM_BITS_TO_CLOCKOUT+g_NUM_TRIGNUM_GUARDBITS) -1 downto 0) := (others => '0'); --! Loaded with "ones" to indicate busy shifting trigger number
  signal s_busy_clocking_trigger_number : std_logic := '0'; --! High when shifting trigger number
  
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 ( s_trigger_out = '1' and s_trigger_number_guard_sr(0) = '0' and not_clockout_trigger_i = '0' ) then --! If we have a trigger, we are not busy with previous trigger and we want to shift out trigger number, 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);
        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
    --! 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 , logic_reset_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 logic_reset_i = '1' then
        s_stretch_trig_in <= '0';
        s_stretch_trig_in_sr <= ( others => '0' );
      elsif 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;

      --! Synchronize stretched trigger with strobe_4x_logic_i
      if logic_reset_i = '1' then
        s_trigger_out <= '0';
        s_trigger_out_sr <= ( others => '0' );
      elsif (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, not_clockout_trigger_i ) is -- , 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;

      s_busy <= dut_busy_i or s_busy_clocking_trigger_number;
      
      s_dut_clk_d1 <= s_dut_clk ;

      s_trigger_out_d1 <= s_trigger_out;
    
      if not_clockout_trigger_i = '0' then -- if flag set then clock out trigger number  
      	s_dut_reset_or_clk <= s_trigger_number_sr(0); -- Clock out trigger number LSB first.
      else
        -- Copy reset/clk signal straight through
        s_dut_reset_or_clk <= reset_or_clk_to_dut_i;        
      end if;

      -- Add registers to help timing closture
      busy_o <= s_busy;
      dut_clk_o  <= s_dut_clk_d1;
      dut_trigger_o <= s_trigger_out_d1;
      dut_reset_or_clk_o <= s_dut_reset_or_clk;
      
      
    end if;
  end process register_signals;

  
END ARCHITECTURE rtl;

