-- Testbench created online at:
--   www.doulos.com/knowhow/perl/testbench_creation/
-- Copyright Doulos Ltd
-- SD, 03 November 2002

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;

library ieee;
use ieee.math_real.all;

entity DUTInterface_AIDA_tb is
  generic ( g_IPBUS_WIDTH : positive := 32 );
end;

architecture bench of DUTInterface_AIDA_tb is

  component DUTInterface_AIDA
     GENERIC( 
       g_IPBUS_WIDTH : positive := 32;
       g_GENERATE_CLOCK : boolean := FALSE;
       g_NUM_TRIGNUM_BITS_TO_CLOCKOUT : positive := 16;
       g_NUM_TRIGNUM_GUARDBITS : positive := 4
     );
     PORT( 
        clk_4x_logic_i          : IN     std_logic;
        logic_reset_i           : IN     std_logic;
        strobe_4x_logic_i       : IN     std_logic;
        trigger_counter_i       : IN     std_logic_vector (g_IPBUS_WIDTH-1 DOWNTO 0);
        not_clockout_trigger_i  : in     std_logic;
        trigger_i               : IN     std_logic;
        reset_or_clk_to_dut_i   : IN     std_logic;
        shutter_to_dut_i        : IN     std_logic;
        busy_o                  : OUT    std_logic;
        dut_busy_i       : IN     std_logic;
        dut_clk_o        : OUT    std_logic;
        dut_reset_or_clk_o : OUT    std_logic;
        dut_shutter_o      : OUT    std_logic;
        dut_trigger_o      : OUT    std_logic
     );
  end component;

  signal clk_4x_logic_i: std_logic;
  signal strobe_4x_logic_i: std_logic;
  signal trigger_counter_i: std_logic_vector (g_IPBUS_WIDTH-1 DOWNTO 0) := (others =>'0');
  signal s_not_clockout_trigger: std_logic;
  signal trigger_i: std_logic := '0';
  signal reset_or_clk_to_dut_i: std_logic := '0';
  signal shutter_to_dut_i: std_logic := '0';
  signal busy_o: std_logic;
  signal dut_busy_i: std_logic := '0' ;
  signal dut_clk_o: std_logic;
  signal dut_reset_or_clk_o: std_logic;
  signal dut_shutter_o: std_logic;
  signal dut_trigger_o: std_logic ;
  signal logic_reset : std_logic;

  constant clock_period: time := 10 ns;
  signal stop_the_clock: boolean;

  signal s_strobe_counter  : natural := 0;  -- cycles through 0-3
  signal s_cycle_counter, s_trigger_count : natural := 0;  -- counts clock++
                                                           -- cycles and triggers

begin

  uut: DUTInterface_AIDA generic map ( g_IPBUS_WIDTH                  => 32,
                                       g_GENERATE_CLOCK               => TRUE,
                                       g_NUM_TRIGNUM_BITS_TO_CLOCKOUT => 16,
                                       g_NUM_TRIGNUM_GUARDBITS        => 4 )
                            port map ( clk_4x_logic_i                 => clk_4x_logic_i,
				       logic_reset_i                  => logic_reset,
                                       strobe_4x_logic_i              => strobe_4x_logic_i,
                                       trigger_counter_i              => trigger_counter_i,
                                       not_clockout_trigger_i        => s_not_clockout_trigger,
                                       trigger_i                      => trigger_i,
                                       reset_or_clk_to_dut_i          => reset_or_clk_to_dut_i,
                                       shutter_to_dut_i               => shutter_to_dut_i,
                                       busy_o                         => busy_o,
                                       dut_busy_i                     => dut_busy_i,
                                       dut_clk_o                      => dut_clk_o,
                                       dut_reset_or_clk_o             => dut_reset_or_clk_o,
                                       dut_shutter_o                  => dut_shutter_o,
                                       dut_trigger_o                  => dut_trigger_o );

  stimulus: process
    variable seed1, seed2: positive;               -- seed values for random generator
    variable rand: real;   -- random real-number value in range 0 to 1.0  
    variable range_of_rand : real := 100.0;    -- the range of random values created will be 0 to +100.
    variable rand_time : time; 
  

  begin
  
    -- Put initialisation code here
    s_not_clockout_trigger <= '0';

    logic_reset <= '0';
    wait for clock_period *4 ;
    wait until rising_edge(clk_4x_logic_i);
    logic_reset <= '1';
    wait until rising_edge(clk_4x_logic_i);
    logic_reset <= '0';

--    reset_i <= '1';
--    wait for clock_period*4;
--    reset_i <= '0';


    -- Put test bench stimulus code here
    for trig in 1 to 20 loop

      
      uniform(seed1, seed2, rand);   -- generate random number
      rand_time := clock_period * (integer(rand*range_of_rand) + 20);  -- rescale to 0..1000, convert integer part 
      wait for rand_time;
      
      --wait for clock_period * 120;
      
      -- pulse trigger_i for one cycle
      wait until rising_edge(clk_4x_logic_i);
      if (busy_o = '0' ) then
         report "Injecting a pulse " & integer'image(trig) severity note;
         trigger_i <= '1' ;
      else
         report "Pulse vetoed " & integer'image(trig) severity note;
         trigger_i <= '0';
      end if;
      wait for clock_period * 2;
      trigger_i <= '0';
      s_trigger_count <= s_trigger_count + 1;
      
    end loop;  -- trig
    
       -- Put test bench stimulus code here
    wait for clock_period * 100;
    stop_the_clock <= true;
    wait;
  end process;


  clocking: process
  begin
    while not stop_the_clock loop
      clk_4x_logic_i <= '0', '1' after clock_period / 2;
      wait for clock_period;
    end loop;
    wait;
  end process;

  p_strobe: process (clk_4x_logic_i)
  begin  -- process p_strobe
    if rising_edge(clk_4x_logic_i) then
      s_cycle_counter <= (s_cycle_counter + 1 );
    end if;
  end process p_strobe;

trigger_counter_i <= std_logic_vector(to_unsigned(s_trigger_count , trigger_counter_i'length));
strobe_4x_logic_i <= '1' when (s_cycle_counter mod 4) = 0 else '0';

end;

