--=============================================================================
--! @file triggerLogic_rtl.vhd
--=============================================================================
--
-------------------------------------------------------------------------------
-- --
-- University of Bristol, High Energy Physics Group.
-- --
------------------------------------------------------------------------------- --
-- VHDL Architecture fmc_mTLU_lib.triggerLogic.rtl
--
--! @brief Produces triggers from either trigger inputs or internal generator\n
--! IPBus address map:\n
--! 0x00000000 RO - Number of triggers issued since last reset.\n
--! 0x00000001 RO - Number of possible triggers since last reset (i.e. pre-veto triggers)\n
--! 0x00000010 RW - Interval between internal triggers in ticks of logic_strobe_i\n
--! 0x00000011 RW - trigger mask ( 1 bit per input )\n
--! 0x00000100 RW - bit-0 - internal trigger veto. Set to halt vetos.\n
--! 0x00000101 RO - state of external veto
--
--! @author David Cussans , David.Cussans@bristol.ac.uk
--
--! @date 16:06:19 11/09/12
--
--! @version v0.1
--
--! @details
--!
--!
--! <b>Dependencies:</b>\n
--!
--! <b>References:</b>\n
--!
--! <b>Modified by: Alvaro Dosil , alvaro.dosil@usc.es </b>\n
--! Author: 
-------------------------------------------------------------------------------
--! \n\n<b>Last changes:</b>\n
--! Move all IPBus stuff into ipbus_syncreg_v , which also handles clock domain
--! crossing. 20/Feb/2014 , David Cussans
-------------------------------------------------------------------------------
--! @todo <next thing to do> \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;

USE work.fmcTLU.all;

ENTITY triggerLogic IS
   GENERIC( 
      g_NUM_INPUTS  : positive := 4;
      g_IPBUS_WIDTH : positive := 32
   );
   PORT( 
      clk_4x_logic_i      : IN     std_logic;                                    -- ! Rising edge active
      ipbus_clk_i         : IN     std_logic;
      ipbus_i             : IN     ipb_wbus;                                     -- Signals from IPBus core to slave
      ipbus_reset_i       : IN     std_logic;
      logic_reset_i       : IN     std_logic;                                    -- active high. Synchronous with clk_4x_logic
      logic_strobe_i      : IN     std_logic;                                    -- ! Pulses high once every 4 cycles of clk_4x_logic
      trigger_i           : IN     std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);   -- ! High when trigger from input connector active
		trigger_times_i     : IN     t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);  --! trigger arrival time 
      veto_i              : IN     std_logic;                                    -- ! Halts triggers when high
		trigger_o           : OUT     std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);   -- ! High when trigger from input connector active and enabled
		trigger_times_o     : OUT    t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);  --! trigger arrival time 
      event_number_o      : OUT    std_logic_vector (g_IPBUS_WIDTH-1 DOWNTO 0);  -- starts at one. Increments for each post_veto_trigger
      ipbus_o             : OUT    ipb_rbus;                                     -- signals from slave to IPBus core
      post_veto_trigger_o : OUT    std_logic;                                    -- ! goes high when trigger passes
      pre_veto_trigger_o  : OUT    std_logic;
      trigger_active_o    : OUT    std_logic                                     --! Goes high when triggers are active ( ie. not veoted)
   );

-- Declarations

END ENTITY triggerLogic ;

--
ARCHITECTURE rtl OF triggerLogic IS

  --! vector that stores trigger output for each combination of trigger inputs.
  signal s_trigger_inputs_enabled , s_trigger_inputs_enabled_ipb : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := x"00000001";--(others=>'1');  
  signal s_external_trigger , s_internal_veto , s_internal_veto_ipb : std_logic := '0';
  signal s_internal_trigger_interval: std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  -- setting s_internal_trigger_interval to zero means no internal triggers
  signal s_pre_veto_trigger_counter , s_post_veto_trigger_counter : unsigned(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  -- ! counters for triggers before and after veto
  signal s_pre_veto_trigger_counter_ipb , s_post_veto_trigger_counter_ipb : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  -- ! counters for triggers before and after veto, on ipbus clock domain
  
  signal s_triggers : std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0) := (others=>'0');
  signal s_trigger_times : t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0) := (others=>(others=>'0'));
  signal s_internal_trigger, s_internal_trigger_d : std_logic := '0';  -- ! Strobes high for one clock cycle at intervals of s_internal_trigger_interval cycles
--  signal s_internal_trigger_timer : unsigned(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  -- counter for internal trigger generation
  signal s_internal_trigger_timer , s_internal_trigger_timer_d : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  -- counter for internal trigger generation and counter delay
  signal s_internal_trigger_active , s_internal_trigger_active_d, s_internal_trigger_active_ipb : std_logic := '0';  -- ! Goes high when internal trigger is running.
  signal s_ipbus_ack : std_logic := '0';  -- used to produce a delayed IPBus ack signal

--  signal s_logic_reset ,  s_logic_reset_ipb : std_logic := '0';  -- ! Take high to reset counters etc.
  signal s_pre_veto_trigger ,s_post_veto_trigger : std_logic := '0';  -- ! Can't read from an output port so keep internal copy

	signal s_AND_Window : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  --! Time window for the trigger coincidence
	signal s_AND_Mask, s_AND_Mask_ipb : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');  --! Mask to select the trigger inputs in coincidence
	signal s_OR_Mask : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0'); 
	signal s_AND_trigger: std_logic := '0';
	signal s_OR_trigger: std_logic := '0';
	
  constant c_N_CTRL : positive := 8;
  constant c_N_STAT : positive := 8;
  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);
  signal s_veto_word : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');
  signal s_external_veto_word : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others => '0');

BEGIN
  -----------------------------------------------------------------------------
  -- 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
  -- workaround to match the number of clock cycles with the configured interval
  s_internal_trigger_interval <= x"00000000" when s_sync_control_from_ipbus(2)<x"00000005" else
										std_logic_vector(unsigned(s_sync_control_from_ipbus(2))-2);
										
  s_trigger_inputs_enabled <= s_sync_control_from_ipbus(3);
  s_veto_word <= s_sync_control_from_ipbus(4);
  s_internal_veto <= s_veto_word(0);
  s_AND_Mask_ipb <= s_sync_control_from_ipbus(6);
  s_AND_Window <= s_sync_control_from_ipbus(7);

  -- Map the status registers
  s_status_to_ipbus(0) <= std_logic_vector(s_post_veto_trigger_counter);
  s_status_to_ipbus(1) <= std_logic_vector(s_pre_veto_trigger_counter);
  s_status_to_ipbus(2) <= s_internal_trigger_interval;
  s_status_to_ipbus(3) <= s_trigger_inputs_enabled;
  s_status_to_ipbus(4) <= s_veto_word;
  s_status_to_ipbus(5) <= s_external_veto_word;
  s_external_veto_word(0) <= veto_i;
  s_external_veto_word(g_IPBUS_WIDTH-1 downto 1) <= (others=>'0');
  s_status_to_ipbus(6) <= s_AND_Mask;
  s_status_to_ipbus(7) <= s_AND_Window;
  
  -----------------------------------------------------------------------------
  -- Generate triggers
  -----------------------------------------------------------------------------
  s_AND_Mask <= s_AND_Mask_ipb and s_trigger_inputs_enabled;
  s_OR_Mask <= (not s_AND_Mask) and s_trigger_inputs_enabled; -- Inputs not selected for coincidence will work as OR triggers.
  --s_OR_trigger <= '0' when (s_OR_Mask(trigger_i'range) and trigger_i) = std_logic_vector(to_unsigned(0,trigger_i'length)) else
	--					'1';
  
  -- Trigger Filtering
  s_external_trigger <= '0' when (s_trigger_inputs_enabled(trigger_i'range) and trigger_i) = std_logic_vector(to_unsigned(0,trigger_i'length)) else
                        '1';
	
	s_triggers <= trigger_i and s_trigger_inputs_enabled(trigger_i'range);
	
	trig_masks : process(trigger_times_i, s_trigger_inputs_enabled)
	begin
		for i in 0 to g_NUM_INPUTS-1 loop
			s_trigger_times(i)(4 downto 3) <= trigger_times_i(i)(4 downto 3);
			if s_trigger_inputs_enabled(i)='1' then
				s_trigger_times(i)(2 downto 0) <= trigger_times_i(i)(2 downto 0);
			else
				s_trigger_times(i)(2 downto 0) <= "000";
			end if;
		end loop;
	end process;
	
	trigger_o <= s_triggers;
	trigger_times_o <= s_trigger_times;
  
  
  
--  	--! Trigger coincidence logic 
--	coincicence_logic : entity work.coincidences
--	generic map(
--		g_nInputs	=> g_NUM_INPUTS,
--		g_IPBUS_WIDTH => g_IPBUS_WIDTH
--		)
--    Port map( 
--		trigger_i 	=> trigger_i,
--		data_i 		=> trigger_times_i,
--      clk_i 		=> clk_4x_logic_i,
--		rst_i			=> logic_reset_i,
--		Window_i		=> s_AND_Window,
--		mask_i		=> s_AND_Mask,
--      trigger_o 	=> s_AND_trigger,
--		data_first_trig_o => open,
--      data_o 		=> trigger_times_o
--		);
  
  --s_external_trigger <= s_OR_trigger; -- or s_AND_trigger;
  
  
  --! Produce triggers....
   trigGen : process  ( clk_4x_logic_i ) 
   begin 
     if rising_edge(clk_4x_logic_i)  then 
       s_post_veto_trigger <= (s_external_trigger or s_internal_trigger) and (not ( s_internal_veto or veto_i) );
       s_pre_veto_trigger <= (s_external_trigger or s_internal_trigger);
     end if;
   end process;
	

  pre_veto_trigger_o <= s_pre_veto_trigger ;
  post_veto_trigger_o <= s_post_veto_trigger;
  trigger_active_o <= s_post_veto_trigger;

	
	--! Internal trigger generator
  p_internal_triggers: process (clk_4x_logic_i )
  begin  -- process p_internal_triggers
    if rising_edge(clk_4x_logic_i) then
      if (s_internal_trigger_interval = x"00000000") then
        s_internal_trigger_active <= '0';
      else
        s_internal_trigger_active <= '1';
      end if;
		
		s_internal_trigger_active_d <= s_internal_trigger_active;    -- signal delayed
      s_internal_trigger_timer_d <= s_internal_trigger_timer;      -- Signal delayed
    end if;
  end process p_internal_triggers;
  
  s_internal_trigger <= '1' when (s_internal_trigger_timer = ( x"00000000" )) and (s_internal_trigger_timer_d = ( x"00000001" ))
                        else '0';
				
				
				
  -- Use a coregen counter to allow timing constraints to be met.
  c_internal_triggers: entity work.internalTriggerGenerator
  PORT MAP (
    clk => clk_4x_logic_i,
    ce => s_internal_trigger_active,
    load => s_internal_trigger or (s_internal_trigger_active and not s_internal_trigger_active_d),
    l => s_internal_trigger_interval,
    q => s_internal_trigger_timer
  );
  
  -----------------------------------------------------------------------------
  -- Count triggers
  -----------------------------------------------------------------------------
  p_trigger_counter: process (clk_4x_logic_i )
  begin  -- process p_trigger_counter

    if rising_edge(clk_4x_logic_i) then

      if logic_reset_i = '1' then
        s_post_veto_trigger_counter <= ( others => '0');
      elsif s_post_veto_trigger = '1' then
        s_post_veto_trigger_counter <= s_post_veto_trigger_counter + 1;
      end if;

      if logic_reset_i = '1' then
        s_pre_veto_trigger_counter <= ( others => '0');
      elsif s_pre_veto_trigger = '1' then
        s_pre_veto_trigger_counter <= s_pre_veto_trigger_counter + 1;
      end if;
      
    end if;
  end process p_trigger_counter;
 
  event_number_o <= std_logic_vector(s_post_veto_trigger_counter);
  
END ARCHITECTURE rtl;

