--=============================================================================
--! @file GPP_rtl.vhd
--=============================================================================
--
-------------------------------------------------------------------------------
-- --
-- University of Santiago de Compostela, High Energy Physics Group.
-- --
------------------------------------------------------------------------------- --
-- VHDL Architecture fmc_mTLU_lib.GPP.rtl
--
--! @brief GPP - General purpose pulser. Generates a pulse with some conditions \n
--! synchronous with a global clock
--! IPBus address map:\n
--
--! @author Alvaro Dosil , alvaro.dosil@usc.es
--
--! @date 15:42:31 01/15/2013 
--
--! @version v0.1
--
--! @details
--!
--!
--! <b>Dependencies:</b>\n
--!
--! <b>References:</b>\n
--!
--! <b>Modified by: 
--! Author: 
-------------------------------------------------------------------------------
--! \n\n<b>Last changes:</b>\n
-------------------------------------------------------------------------------
--! @todo <next thing to do> \n
--! <another thing to do> \n
--
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity GPP is
   GENERIC( 
      g_IPBUS_WIDTH      : positive := 32;
		g_PULSEDELAY_WIDTH : positive := 7                 --! The maximum delay is 32 cicles
   );
	PORT( clk_logic_i       : IN     std_logic;                                          --! Rising edge active
			Enable_i          : IN     std_logic;                                          --
			Reset_i           : IN     std_logic;                                          --
			RstPulseCnt_i     : IN     std_logic;                                          -- Reset pulse counter
			Trigger_i         : IN     std_logic;                                          -- Trigger input signal
			NMaxPulses_i      : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Max number of pulses
			SuDTime_i         : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Startup dead time
			PulseTime4x_i     : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Pulse time 4x
			--PulseTime_i       : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);       -- Pulse time
		   IpDTime_i         : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Interpulse dead time
			RearmTime_i       : IN     std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Time before rearm after reach the max number of pulses
			Force_PullDown_i  : IN     std_logic;                                          -- Force pull down
			WU_i              : IN     std_logic;                                          -- Output trigger signal with update
			PulseDelay_i      : IN     std_logic_vector(g_PULSEDELAY_WIDTH-1 downto 0);    -- Pulse delay (0 to 31 clock cicles)
	      event_number_o    : OUT    std_logic_vector(g_IPBUS_WIDTH-1 downto 0);         -- Event number
			MaxPulses_o       : OUT    std_logic;                                          -- Maximun pulse reached output
			Pulse_o           : OUT    std_logic;                                          --! 40 MHz pulse Generated
			Pulse_d_o         : OUT    std_logic                                           --! 40 MHz pulse generated delayed n clock cicles
			);
end GPP;

architecture rtl of GPP is
   --! FSM state values
   type state_values is (st0, st1, st2, st3, st4, st5, st6);
	signal pres_state, next_state: state_values;
	
	signal s_pulse_counter_en  : std_logic := '0';                                             --! Pulse counter enable
	signal s_RstPulse_counter  : std_logic := '0';                                             --! Reset pulse counter
	signal s_RstPulseCnt       : std_logic := '0';                                             --! Reset pulse counter
	signal s_PulseTime4x       : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                   --! Pulse time
	signal s_PulseCounter      : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                   --! Pulse counter value
	signal s_MaxPulses         : std_logic := '0';                                             --! Max number of pulses reached
	--signal s_Pulse4x           : std_logic := '0';                                             --! Active 4x pulse signal
	--signal s_Pulse_tmp         : std_logic := '0';                                             --
	signal s_Pulse             : std_logic := '0';                                             --! Active pulse signal
	signal s_Pulse_d           : std_logic_vector(g_IPBUS_WIDTH-1 downto 0) := (others=>'0');  --! Active pulse signal delayed
   
	signal s_load_SuDTime      : std_logic := '1';                                             --! Counter load signal
	signal s_SuDTime_Counter   : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                   --! Startup dead time counter
	signal EOSDT               : std_logic := '0';                                             --! End of startup dead time signal
	
	signal s_load_PulseTime4x    : std_logic := '1';                                           --! Counter load 4x signal
	signal s_PulseTime4x_Counter : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                 --! Pulse time 4x counter
	signal EOP                 : std_logic := '0';                                             --! End of 4x pulse signal
	
	signal s_load_IpDTime      : std_logic := '1';                                             --! Counter load signal
	signal s_IpDTime_Counter   : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                   --! Interpulse dead time counter
	signal EOIDT               : std_logic := '0';                                             --! End of interpulse dead time signal

   signal s_load_PulseTime    : std_logic := '1';                                             --! Counter load signal
	
	signal s_load_RearmTime    : std_logic := '1';                                             --! Rearm counter load signal
	signal s_Rearm_Counter     : std_logic_vector(g_IPBUS_WIDTH-1 downto 0);                   --! Startup dead time counter
	signal EOREARM             : std_logic := '0';                                             --! End of startup dead time signal

begin

   --! Generated pulse counter
	c_pulse_counter : entity work.CounterUp
   PORT MAP (
     clk   => clk_logic_i,
     ce    => s_pulse_counter_en,
     sinit => s_RstPulse_counter,
     q     => s_PulseCounter
	);
	s_RstPulse_counter <= Reset_i or RstPulseCnt_i or s_RstPulseCnt;
	s_pulse_counter_en <= '1' when (s_Pulse = '1') and (s_Pulse_d(0) = '0') and (s_MaxPulses = '0')
	                      else '0'; -- The counter will always be one cicle delayed wrt to the pulse
	s_MaxPulses <= '1' when (s_PulseCounter = NMaxPulses_i) and (NMaxPulses_i /= x"00000000")
                  else '0';


   --! Startup dead time counter
   c_startup_dtime : entity work.CounterDown
   PORT MAP (
     clk  => clk_logic_i,
     load => s_load_SuDTime,
     l    => std_logic_vector(unsigned(SuDTime_i)-1),
     q    => s_SuDTime_Counter
   );
	EOSDT <= '1' when (s_SuDTime_Counter = x"00000000")
          else '0';
			 
	--! Pulse time counter
   c_pulse_time : entity work.CounterDown
   PORT MAP (
     clk  => clk_logic_i,
     load => s_load_PulseTime4x,
     l    => std_logic_vector(unsigned(s_PulseTime4x)-1),
     q    => s_PulseTime4x_Counter
   );
	s_PulseTime4x <= x"00000001" when PulseTime4x_i = x"00000000"    -- At least one cicle pulse is generated
	               else PulseTime4x_i;
	EOP <= '1' when (s_PulseTime4x_Counter = x"00000000")
          else '0';
			 
   --! Interpulse dead time counter
   c_interpulse_dtime : entity work.CounterDown
   PORT MAP (
     clk  => clk_logic_i,
     load => s_load_IpDTime,
     l    => std_logic_vector(unsigned(IpDTime_i)-1),
     q    => s_IpDTime_Counter
   );
	EOIDT <= '1' when (s_IpDTime_Counter = x"00000000")
          else '0';
			 
	--! Rearm time after the max pulses reached
   c_rearm_dtime : entity work.CounterDown
   PORT MAP (
     clk  => clk_logic_i,
     load => s_load_RearmTime,
     l    => std_logic_vector(unsigned(RearmTime_i)-1),
     q    => s_Rearm_Counter
   );
	EOREARM <= '1' when (s_Rearm_Counter = x"00000000")
              else '0';

	--! FSM register
	statereg: process(clk_logic_i, Enable_i, Reset_i)
	begin
		if Enable_i = '0'  then 
			pres_state <= st0;            -- Move to st0 - INITIAL STATE
      
		elsif Reset_i = '1' then
			pres_state <= st0;            -- Move to st0 - INITIAL STATE
        
		elsif rising_edge(clk_logic_i) then
			pres_state <= next_state;     -- Move to next state
        
		end if;
	end process statereg;


   --! FSM combinational block
	fsm: process(pres_state, Enable_i, Reset_i, Trigger_i, s_MaxPulses, EOP, EOSDT, EOIDT, Force_PullDown_i)
	begin
	  next_state <= pres_state;
	  -- Default values
	  s_Pulse          <= '0';
	  s_load_SuDTime     <= '1';
	  s_load_PulseTime4x <= '1';
	  s_load_IpDTime     <= '1';
	  s_load_RearmTime   <= '1';
	  
	  s_RstPulseCnt      <= '0';
  
     case pres_state is
	  
	    -- st0 - INITIAL STATE
		 when st0=>
         if (Enable_i = '1') and (Reset_i = '0') then 
           next_state <= st1;            -- Next state is "st1 - IDLE"
         end if;
       
		 
		 -- st1 - IDLE STATE
       when st1=>
         if s_MaxPulses = '1' then
           next_state <= st5;            -- Next state is "st5 - NMAX PULSES REACHED"
         else
           if Trigger_i = '1' and Force_PullDown_i = '0' then 
             if (to_integer(unsigned(SuDTime_i)) = 0) then
               next_state <= st3;        -- Next state is "st3 - PULSE"
             else
               next_state <= st2;        -- Next state is "st2 - STARTUP DEAD-TIME"
             end if; 
           end if;
         end if;
		 
		 
		 -- st2 - STARTUP DEAD-TIME
       when st2=>
         s_load_SuDTime <= '0';
           if EOSDT = '1' then
             next_state <= st3;          -- Next state is "st3 - PULSE"
           end if;
			  
			 
		 -- st3 - PULSE
       when st3=>
         s_Pulse <= '1';
         s_load_PulseTime4x <= '0';
				
         if (EOP = '1') or (Force_PullDown_i = '1')then
           if (to_integer(unsigned(IpDTime_i)) = 0) then
             next_state <= st1;         -- Next state is "st1 - IDLE"
           else
             next_state <= st4;         -- Next state is "st4 - INTERPULSE DEAD-TIME"
           end if;
         end if;
				
         if Trigger_i = '1' then
           if (WU_i = '1') then
             next_state <= st6;         -- Next state is "st6 - RELOAD PULSE TIMER"
           end if;	
         end if;
       
		 
		 -- st4 - INTERPULSE DEAD-TIME
       when st4=>
         s_load_IpDTime <= '0';
         if EOIDT = '1' then
           next_state <= st1;            -- Next state is "st1 - IDLE"
         end if;
				
		 -- st5 - NMAX PULSES REACHED
       when st5=>
		   s_load_RearmTime <= '0';
			if EOREARM = '1' then
			  next_state <= st1;            -- Next state is "st1 - IDLE"
			  s_RstPulseCnt <= '1';
			end if;
			
		 -- st6 - RELOAD PULSE TIMER
       when st6=>
         s_Pulse <= '1';
         next_state <= st3;              -- Next state is "st3 - PULSE"
			
--       when others=>
--         next_state<=st0;                -- Next state is "st0 - INITIAL STATE"
     
	  end case;
	
	end process fsm;    
	
	-- Pulse reg
	p_reg_pulse : process ( clk_logic_i , Reset_i )
   begin  
	  if Reset_i = '1' then
	    s_Pulse_d <= (others => '0');
	  
	  elsif rising_edge(clk_logic_i) then
       for i in 0 to g_IPBUS_WIDTH-2 loop
         s_Pulse_d(i+1) <= s_Pulse_d(i);
       end loop;
	    s_Pulse_d(0) <= s_Pulse;
	  end if;
	end process p_reg_pulse;
	
	event_number_o <= s_PulseCounter;
	MaxPulses_o <= s_MaxPulses;
	Pulse_o <= s_Pulse;
	Pulse_d_o <= s_Pulse when PulseDelay_i = "0000000" else
	             s_Pulse_d(to_integer(unsigned(PulseDelay_i))-1);
	
end rtl;

