--=============================================================================
--! @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
-------------------------------------------------------------------------------
--! @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;

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 conector active
      veto_i              : IN     std_logic;                                    -- ! Halts triggers when high
      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_pattern , s_trigger_pattern_ipb : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (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_internal_trigger : 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_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
BEGIN

  -----------------------------------------------------------------------------
  -- IPBus write
  -----------------------------------------------------------------------------
  ipbus_write: process (ipbus_clk_i)
  begin  -- process ipb_clk_i
    if rising_edge(ipbus_clk_i) then

       if (ipbus_i.ipb_strobe = '1' and ipbus_i.ipb_write = '1') then

         case ipbus_i.ipb_addr(2 downto 0) is
           when "010" => s_internal_trigger_interval <= ipbus_i.ipb_wdata ; -- Interval between internal triggers in ticks of logic_strobe_i
           when "011" => s_trigger_pattern_ipb <= ipbus_i.ipb_wdata ; -- trigger mask ( 16 bits for 4 inputs )
           when "100" => s_internal_veto_ipb <= ipbus_i.ipb_wdata(0) ; -- bit-0 - internal trigger veto. Set to halt vetos.
           when others => null;
         end case;
          
       end if;

       s_ipbus_ack <= ipbus_i.ipb_strobe and not s_ipbus_ack;

       if (s_internal_trigger_interval = x"00000000") then
         s_internal_trigger_active_ipb <= '0';
       else
         s_internal_trigger_active_ipb <= '1';
       end if;
       
    end if;
  end process ipbus_write;

  ipbus_o.ipb_ack <= s_ipbus_ack;
  ipbus_o.ipb_err <= '0';

  -----------------------------------------------------------------------------
  -- IPBUS read
  -----------------------------------------------------------------------------
  with ipbus_i.ipb_addr(2 downto 0) select
    ipbus_o.ipb_rdata <=
    s_post_veto_trigger_counter_ipb                     when "000",
    s_pre_veto_trigger_counter_ipb                      when "001",
    s_internal_trigger_interval                         when "010",
    s_trigger_pattern_ipb                               when "011",
    x"0000000" & "000" & s_internal_veto_ipb            when "100",
    (others => '1')                                     when others;

  
  register_trigger_pattern: entity work.sync_reg
	 generic map(g_Data_width => s_trigger_pattern_ipb'length)
    port map(
      clk_i => clk_4x_logic_i,
		async_i => s_trigger_pattern_ipb,
		sync_o => s_trigger_pattern
	   );
		
  -- Move the trigger pattern onto the logic clock domain to allow timing
  -- constraints to be met.
--  register_trigger_pattern : entity work.registerCounter
--    generic map (
--      g_DATA_WIDTH => s_trigger_pattern_ipb'length )
--    port map (
--    clk_input_i => ipbus_clk_i,
--    data_i      => s_trigger_pattern_ipb,
--    data_o      => s_trigger_pattern,
--    clk_output_i  => clk_4x_logic_i
--    );
  
  
  -----------------------------------------------------------------------------
  -- Generate triggers
  -----------------------------------------------------------------------------
  --s_external_trigger <= s_trigger_pattern(to_integer(unsigned(trigger_i)));
  s_external_trigger <= '0' when (s_trigger_pattern(trigger_i'range) and trigger_i) = std_logic_vector(to_unsigned(0,trigger_i'length)) else
                        '1';
  
  --! 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);
		 --s_post_veto_trigger <= (s_internal_trigger) and (not ( s_internal_veto or veto_i) );
       --s_pre_veto_trigger <= (s_internal_trigger);
     end if;
   end process;

  pre_veto_trigger_o <= s_pre_veto_trigger ;
  post_veto_trigger_o <= s_post_veto_trigger;

-- purpose: Generates internal triggers a regular intervals set by s_internal_trigger_interval. Zero means no triggers
  -- type   : combinational
  -- inputs : clk_4x_logic_i , logic_strobe_i
  -- outputs: s_internal_trigger , s_internal_trigger_timer
--  p_internal_triggers: process (clk_4x_logic_i )
--  begin  -- process p_internal_triggers
--    if rising_edge(clk_4x_logic_i) then
--
--      s_internal_trigger <= '0';
--      s_internal_trigger_active <= s_internal_trigger_active_ipb;  -- move from
--                                                             --IPBus clock to trigger
--      if s_internal_trigger_active = '1' then
--
--        if (s_internal_trigger_timer = unsigned(s_internal_trigger_interval)) then        
--          s_internal_trigger <= '1';
--          s_internal_trigger_timer <= (others => '0');
--        else
--          s_internal_trigger <= '0';
--          s_internal_trigger_timer <= s_internal_trigger_timer + 1;
--        end if;
--        
--      end if;
--      
--      
--    end if;
--  end process p_internal_triggers;

  p_internal_triggers: process (clk_4x_logic_i )
  begin  -- process p_internal_triggers
    if rising_edge(clk_4x_logic_i) then

      s_internal_trigger_active <= s_internal_trigger_active_ipb;  -- move from
                                                                   -- IPBus clock to trigger
      s_internal_veto <= s_internal_veto_ipb;
		
		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,
    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;


  register_pre_veto_trigs: entity work.sync_reg
	 generic map(g_Data_width => g_IPBUS_WIDTH)
    port map(
      clk_i => ipbus_clk_i,
		async_i => std_logic_vector(s_pre_veto_trigger_counter),
		sync_o => s_pre_veto_trigger_counter_ipb
	   );
		
  register_post_veto_trigs: entity work.sync_reg
	 generic map(g_Data_width => g_IPBUS_WIDTH)
    port map(
      clk_i => ipbus_clk_i,
		async_i => std_logic_vector(s_post_veto_trigger_counter),
		sync_o => s_post_veto_trigger_counter_ipb
	   );
		
--  -- Transfer  trigger counters to IPBUS clock domain
--  register_pre_veto_trigs : entity work.registerCounter
--    generic map (
--      g_DATA_WIDTH => g_IPBUS_WIDTH)
--    port map (
--    clk_input_i => clk_4x_logic_i,
--    data_i      => std_logic_vector(s_pre_veto_trigger_counter),
--    data_o      => s_pre_veto_trigger_counter_ipb,
--    clk_output_i  => ipbus_clk_i
--    );

--  -- Transfer  trigger counters to IPBUS clock domain
--  register_post_veto_trigs : entity work.registerCounter
--    generic map (
--      g_DATA_WIDTH => g_IPBUS_WIDTH)
--    port map (
--    clk_input_i => clk_4x_logic_i,
--    data_i      => std_logic_vector(s_post_veto_trigger_counter),
--    data_o      => s_post_veto_trigger_counter_ipb,
--    clk_output_i  => ipbus_clk_i
--    );        

  
  event_number_o <= std_logic_vector(s_post_veto_trigger_counter);
  
END ARCHITECTURE rtl;

