--=============================================================================
--! @file DUTInterfaces_rtl.vhd
--=============================================================================
--
-------------------------------------------------------------------------------
-- --
-- University of Bristol, High Energy Physics Group.
-- --
------------------------------------------------------------------------------- --
-- VHDL Architecture fmc_mTLU_lib.DUTInterfaces.rtl
--
--! @brief \n
--! \n
--
--! @author David Cussans , David.Cussans@bristol.ac.uk
--
--! @date 15:09:50 11/09/12
--
--! @version v0.1
--
--! @details
--! Address map:\n
--! 5-bit decoded
--! 0x00000000 - DUT interface mode, two bits per DUT. Up to 12 inputs XXXXXXXXBBAA99887766554433221100\n
--!            - mode: 0 = EUDET mode , 1 = synchronous ( LHC / Timepix ) , 2,3=reserved
--! 
--
--!
--! <b>Dependencies:</b>\n
--!
--! <b>References:</b>\n
--!
--! <b>Modified by:</b>\n
--! Author: 
-------------------------------------------------------------------------------
--! \n\n<b>Last changes:</b>\n
-------------------------------------------------------------------------------
--! @todo <next thing to do> Indicate if the DUT works under AIDA/EUDET style\n
--! <another thing to do> \n
--
--------------------------------------------------------------------------------
-- 
-- Created using using Mentor Graphics HDL Designer(TM) 2010.3 (Build 21)
--
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

USE work.ipbus.all;
use work.ipbus_reg_types.all;

library unisim;
use unisim.VComponents.all;


ENTITY DUTInterfaces IS
   GENERIC( 
      g_NUM_DUTS    : positive := 3;
      g_IPBUS_WIDTH : positive := 32
   );
   PORT( 
      busy_from_dut_n_i       : IN     std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- BUSY input from DUTs
      busy_from_dut_p_i       : IN     std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- BUSY input from DUTs
      clk_4x_logic_i          : IN     std_logic;
      ipbus_clk_i             : IN     std_logic;
      ipbus_i                 : IN     ipb_wbus;                                     -- Signals from IPBus core to slave
      ipbus_reset_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);
      trigger_i               : IN     std_logic;                                    -- goes high when trigger logic issues a trigger
      clk_to_dut_i				: IN     std_logic ;											-- ! clock to DUT
		reset_or_clk_to_dut_i	: IN		std_logic;
		AIDAhandshake_i			: IN 		std_logic;												-- AIDA/EUDET handshake
		ipbus_o                 : OUT    ipb_rbus;                                     -- signals from slave to IPBus core
      clk_to_dut_n_o        	: INOUT  std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- clocks trigger data when in EUDET mode
      clk_to_dut_p_o        	: INOUT  std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- clocks trigger data when in EUDET mode
		reset_or_clk_to_dut_n_o : OUT    std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- ! Either reset line or trigger
      reset_or_clk_to_dut_p_o : OUT    std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- ! Either reset line or trigger
      trigger_to_dut_n_o      : OUT    std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- ! Trigger output
      trigger_to_dut_p_o      : OUT    std_logic_vector (g_NUM_DUTS-1 DOWNTO 0);     -- ! Trigger output
      veto_o                  : OUT    std_logic                                     -- goes high when one or more DUT are busy
   );

-- Declarations

END ENTITY DUTInterfaces ;

--
ARCHITECTURE rtl OF DUTInterfaces IS

  signal s_intermediate_busy_or : std_logic_vector(g_NUM_DUTS downto 0);  -- OR tree
  signal s_veto : std_logic;
  signal s_strobe_4x_logic_d1 : std_logic;
  signal s_clk_to_DUT , s_busy_from_dut , s_reset_or_clk_to_dut , s_trigger_to_dut : std_logic_vector(g_NUM_DUTS-1 downto 0);
  signal s_DUT_mask : std_logic_vector(g_NUM_DUTS-1 downto 0) := (others => '0');   	--! Mask for the DUTs not used
  signal s_clk_is_input, s_clk_is_input_b : std_logic := '0'; 					--! Indicates the direction of the clock in the RJ45 DUT
  signal s_clk_to_tlu : std_logic := '0';
  
  constant c_N_CTRL : positive := 1;
  constant c_N_STAT : positive := 1;
  signal s_status_to_ipbus, s_sync_status_to_ipbus : ipb_reg_v(c_N_STAT-1 downto 0);
  signal s_control_from_ipbus,s_sync_control_from_ipbus : ipb_reg_v(c_N_CTRL-1 downto 0);
BEGIN


  -- Dummy code.
  s_intermediate_busy_or(0) <= '0';
   --s_busy_from_dut(g_NUM_DUTS-1 downto 0) <= (others=>'0');
  
  -----------------------------------------------------------------------------
  -- IPBus interface 
  -----------------------------------------------------------------------------
  ipbus_registers: entity work.ipbus_ctrlreg_v
    generic map(
      N_CTRL => c_N_CTRL,
      N_STAT => c_N_STAT
      )
    port map(
      clk => ipbus_clk_i,
      reset=> '0',--ipbus_reset_i ,
      ipbus_in=>  ipbus_i,
      ipbus_out=> ipbus_o,
      d=>  s_sync_status_to_ipbus,
      q=>  s_control_from_ipbus,
      stb=>  open
      );

  -- Synchronize registers from logic clock to ipbus.
    sync_status: entity work.synchronizeRegisters
    generic map (
      g_NUM_REGISTERS => c_N_STAT )
    port map (
      clk_input_i => clk_4x_logic_i,
      data_i      =>  s_status_to_ipbus,
      data_o      => s_sync_status_to_ipbus,
      clk_output_i => ipbus_clk_i);

    -- Synchronize registers from logic clock to ipbus.
    sync_ctrl: entity work.synchronizeRegisters
    generic map (
      g_NUM_REGISTERS => c_N_CTRL )
    port map (
      clk_input_i => ipbus_clk_i,
      data_i      =>  s_control_from_ipbus,
      data_o      => s_sync_control_from_ipbus,
      clk_output_i => clk_4x_logic_i);

  -- Map the control registers
  s_DUT_mask <= s_sync_control_from_ipbus(0)(g_NUM_DUTS-1 downto 0);

  -- Map the status registers
  s_status_to_ipbus(0) <= std_logic_vector(to_unsigned(0,g_IPBUS_WIDTH-g_NUM_DUTS)) & s_DUT_mask;
  
  
  -- These instances need to be out of the loop because the RJ45 permits a bidirectional clock
  clk_to_DUT_OBUFDS_inst_0 : OBUFDS
      generic map (
        IOSTANDARD => "LVDS_25")
      port map (
        O =>  clk_to_dut_p_o(0),    	-- Diff_p output (connect directly to top-level port)
        OB => clk_to_dut_n_o(0),   	-- Diff_n output (connect directly to top-level port)
        I =>  s_clk_to_dut(0)					-- Buffer output
      );
	clk_to_DUT_OBUFDS_inst_1 : OBUFDS
      generic map (
        IOSTANDARD => "LVDS_25")
      port map (
        O =>  clk_to_dut_p_o(1),    	-- Diff_p output (connect directly to top-level port)
        OB => clk_to_dut_n_o(1),   	-- Diff_n output (connect directly to top-level port)
        I =>  s_clk_to_dut(1)					-- Buffer output
      );
  clk_to_DUT_OBUFDS_inst_2 : IOBUFDS
		generic map (
			IOSTANDARD => "BLVDS_25")
		port map (
			O => s_clk_to_tlu,     -- Buffer output
			IO => clk_to_dut_p_o(2),   -- Diff_p inout (connect directly to top-level port)
			IOB => clk_to_dut_n_o(2), -- Diff_n inout (connect directly to top-level port)
			I => s_clk_to_dut(2),     -- Buffer input
			T => s_clk_is_input_b      -- 3-state enable input, high=input, low=output
			);
	s_clk_is_input <= not AIDAhandshake_i;
	
								
	--When an ODDR2 primitive is used in conjunction with a 3-state output, the T control pin must
	--also use an ODDR2 primitive configured in the same mode as the ODDR2 primitive used for data
	--output.
	ddr_for_clk_to_DUT_tristate : ODDR2
   generic map(
      DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
      INIT => '0', -- Sets initial state of the Q output to '0' or '1'
      SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
   port map (
      Q => s_clk_is_input_b, -- 1-bit output data
      C0 => clk_to_dut_i, -- 1-bit clock input
      C1 => not clk_to_dut_i, --not s_clk160_internal, -- 1-bit clock input
      CE => '1',  -- 1-bit clock enable input
      D0 => s_clk_is_input,   -- 1-bit data input (associated with C0)
      D1 => s_clk_is_input,   -- 1-bit data input (associated with C1)
      R => '0',    -- 1-bit reset input
      S => '0'     -- 1-bit set input
   );
				
  duts: for dut in 1 to g_NUM_DUTS generate
    
    busy_IBUFDS_inst : IBUFDS
      generic map (
        DIFF_TERM => TRUE, -- Differential Termination 
        IBUF_LOW_PWR => TRUE, -- Low power (TRUE) vs. performance (FALSE) setting for referenced I/O standards
        IOSTANDARD => "LVDS_25")
      port map (
        O => s_busy_from_dut(dut-1),  -- Buffer output
        I => busy_from_dut_p_i(dut-1),  -- Diff_p buffer input (connect directly to top-level port)
        IB => busy_from_dut_n_i(dut-1) -- Diff_n buffer input (connect directly to top-level port)
      );
		
   
    trig_OBUFDS_inst : OBUFDS
      generic map (
        IOSTANDARD => "LVDS_25")
      port map (
        O =>  trigger_to_dut_p_o(dut-1),     						-- Diff_p output (connect directly to top-level port)
        OB => trigger_to_dut_n_o(dut-1),   							-- Diff_n output (connect directly to top-level port)
        I =>  s_trigger_to_dut(dut-1) and s_DUT_mask(dut-1)    -- Buffer input 
      );
     
    clk_rst_OBUFDS_inst : OBUFDS
      generic map (
        IOSTANDARD => "LVDS_25")
      port map (
        O =>  reset_or_clk_to_dut_p_o(dut-1),    							-- Diff_p output (connect directly to top-level port)
        OB => reset_or_clk_to_dut_n_o(dut-1),   							-- Diff_n output (connect directly to top-level port)
        I =>  reset_or_clk_to_dut_i 	--s_reset_or_clk_to_dut(dut-1) and s_DUT_mask(dut-1)     -- Buffer input 
      );
     
    s_intermediate_busy_or(dut) <= s_intermediate_busy_or(dut-1) or
                                   (s_busy_from_dut(dut-1) and
											  s_DUT_mask(dut-1));
   
	
	ddr_for_clk_output : ODDR2
   generic map(
      DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
      INIT => '0', -- Sets initial state of the Q output to '0' or '1'
      SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
   port map (
      Q => s_clk_to_dut(dut-1), -- 1-bit output data
      C0 => clk_to_dut_i, -- 1-bit clock input
      C1 => not clk_to_dut_i, -- 1-bit clock input
      CE => '1',  -- 1-bit clock enable input
      D0 => '1',   -- 1-bit data input (associated with C0)
      D1 => '0',   -- 1-bit data input (associated with C1)
      R => '0',    -- 1-bit reset input
      S => '0'     -- 1-bit set input
   );
	 
  end generate duts;

    s_veto <=  s_intermediate_busy_or(g_NUM_DUTS);
  
  -- purpose: register for internal signals and output signals
  -- type   : combinational
  -- inputs : clk_4x_logic_i , strobe_4x_logic_i , s_veto
  -- outputs: veto_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
      veto_o <= s_veto;
      s_strobe_4x_logic_d1 <= strobe_4x_logic_i;
      --s_reset_or_clk_to_dut <= ( others => (s_strobe_4x_logic_d1 or strobe_4x_logic_i));      
      s_trigger_to_dut <= ( others => trigger_i );
		  --shutter_to_dut <= ( others => shutter_i );
    end if;
  end process register_signals;
  
END ARCHITECTURE rtl;

