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 triggerInputs_newTLU_tb is
  generic (
    g_NUM_INPUTS  : natural  := 6;
    g_IPBUS_WIDTH : positive := 32
    );
end;

architecture bench of triggerInputs_newTLU_tb is

  component triggerInputs_newTLU
     GENERIC( 
        g_NUM_INPUTS  : natural  := g_NUM_INPUTS;
        g_IPBUS_WIDTH : positive := g_IPBUS_WIDTH
        );
     PORT(
        clk_4x_logic         : IN     std_logic;
        clk_200_i : IN     std_logic;
        strobe_4x_logic_i    : IN     std_logic;
        threshold_discr_p_i  : IN     std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
        threshold_discr_n_i  : IN     std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
        reset_i              : IN     std_logic;
        trigger_times_o      : OUT    t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
        trigger_o            : OUT    std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
        edge_rising_times_o  : OUT    t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
        edge_falling_times_o : OUT    t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
        edge_rising_o        : OUT    std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
        edge_falling_o       : OUT    std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
        ipbus_clk_i          : IN     std_logic;
        ipbus_reset_i        : IN     std_logic;
        ipbus_i              : IN     ipb_wbus;
        ipbus_o              : OUT    ipb_rbus;
        clk_8x_logic_i      : IN     std_logic;
        strobe_8x_logic_i   : IN     std_logic
     );
  end component;

  signal clk_4x_logic: std_logic := '0';
  signal clk_200_i: std_logic;
  signal strobe_4x_logic_i: std_logic;
  signal threshold_discr_p_i: std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
  signal threshold_discr_n_i: std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
  signal reset_i: std_logic;
  signal trigger_times_o: t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
  signal trigger_o: std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
  signal edge_rising_times_o: t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
  signal edge_falling_times_o: t_triggerTimeArray (g_NUM_INPUTS-1 DOWNTO 0);
  signal edge_rising_o: std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
  signal edge_falling_o: std_logic_vector (g_NUM_INPUTS-1 DOWNTO 0);
  signal ipbus_clk_i: std_logic;
  signal ipbus_reset_i: std_logic;
  signal ipbus_i: ipb_wbus;
  signal ipbus_o: ipb_rbus;
  signal clk_8x_logic_i: std_logic;
  signal clk_logic : std_logic;
  signal strobe_8x_logic_i: std_logic ;
  constant C_NUM_STROBE_TAPS : positive := 2;  --! Adjust to shift strobes relative to 40MHz clock edge
  signal s_clk40_delayed_160 : std_logic_vector(C_NUM_STROBE_TAPS downto 0); --! Shift register used to generate clock_4x strobe. Adjust length for correct alignment with incoming clock
  signal s_clk40_delayed_320 : std_logic_vector((2*C_NUM_STROBE_TAPS)+1 downto 0);  --! Shift register used to generate clock_8x strobe. Adjust length for correct alignment with incoming clock

  constant clock320_period: time := 3.125 ns; -- 320 MHz clock
  constant clock200_period: time := 5 ns; -- 200 MHz clock
  constant delta : time := 0.02 ns; -- make sure IPBus clock drifts w.r.t.
                                    -- logic clocks
  constant clockipbus_period: time := 31.25 ns + delta; -- 320 MHz clock

  signal stop_the_clock: boolean;

begin

  -- Insert values for generic parameters !!
  uut: triggerInputs_newTLU generic map ( g_NUM_INPUTS         => g_NUM_INPUTS,
                                          g_IPBUS_WIDTH        => g_IPBUS_WIDTH )
                               port map ( clk_4x_logic         => clk_4x_logic,
                                          clk_200_i            => clk_200_i,
                                          strobe_4x_logic_i    => strobe_4x_logic_i,
                                          threshold_discr_p_i  => threshold_discr_p_i,
                                          threshold_discr_n_i  => threshold_discr_n_i,
                                          reset_i              => reset_i,
                                          trigger_times_o      => trigger_times_o,
                                          trigger_o            => trigger_o,
                                          edge_rising_times_o  => edge_rising_times_o,
                                          edge_falling_times_o => edge_falling_times_o,
                                          edge_rising_o        => edge_rising_o,
                                          edge_falling_o       => edge_falling_o,
                                          ipbus_clk_i          => ipbus_clk_i,
                                          ipbus_reset_i        => ipbus_reset_i,
                                          ipbus_i              => ipbus_i,
                                          ipbus_o              => ipbus_o,
                                          clk_8x_logic_i       => clk_8x_logic_i,
                                          strobe_8x_logic_i    => strobe_8x_logic_i );

  stimulus: process
  begin
      stop_the_clock <= false;
    -- Put initialisation code here
      reset_i <= '1';
      wait for clock320_period * 16;
      reset_i <= '0';

      wait for clock320_period * 200;

    -- Put test bench stimulus code here

    stop_the_clock <= true;
    wait;
  end process;

  clock320: process
  begin
    while not stop_the_clock loop
      clk_8x_logic_i <= '0', '1' after clock320_period / 2;
      wait for clock320_period;
    
    end loop;
    wait;
  end process;

  clock4x_1x: process( clk_8x_logic_i )
        variable ctr : unsigned(2 downto 0):= (others => '0');    
  begin
    if rising_edge( clk_8x_logic_i ) then
      clk_4x_logic <= not clk_4x_logic;

      ctr := ctr + 1;

      clk_logic <= ctr(2);
    end if;
    
    end process;

       
  -- Generate a strobe signal for 160MHz clock
    generate_4x_strobe: process (clk_4x_logic, clk_logic)
    begin  -- process generate_4x_strobe
      if rising_edge(clk_4x_logic) then
        s_clk40_delayed_160 <= s_clk40_delayed_160(s_clk40_delayed_160'left-1 downto 0) & clk_logic;
        strobe_4x_logic_i   <=  s_clk40_delayed_160(s_clk40_delayed_160'left-1) and not s_clk40_delayed_160(s_clk40_delayed_160'left);
      end if;
    end process generate_4x_strobe;

  -- Generate a strobe signal for 320MHz clock
    generate_8x_strobe: process (clk_8x_logic_i, clk_logic)
    begin  -- process generate_4x_strobe
      if rising_edge(clk_8x_logic_i) then
        s_clk40_delayed_320 <= s_clk40_delayed_320(s_clk40_delayed_320'left-1 downto 0) & clk_logic;
        strobe_8x_logic_i   <=  s_clk40_delayed_320(s_clk40_delayed_320'left-1) and not s_clk40_delayed_320(s_clk40_delayed_320'left);
      end if;
    end process generate_8x_strobe;
      
  clockIpbus: process
  begin
    while not stop_the_clock loop
      ipbus_clk_i <= '0', '1' after clockipbus_period / 2;
      wait for clockipbus_period;
    end loop;
    wait;
  end process;

  clock200: process
  begin
    while not stop_the_clock loop
      clk_200_i <= '0', '1' after clock200_period / 2;
      wait for clock200_period;
    end loop;
    wait;
  end process;

end;
  
