-- TestBench Template 

LIBRARY ieee;
LIBRARY unisim;
library modelsim_lib;

USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
USE unisim.vcomponents.ALL;
use modelsim_lib.util.ALL;

ENTITY trigger_top_tb IS
  END trigger_top_tb;

ARCHITECTURE behavior OF trigger_top_tb IS 

   constant c_NUMBER_OF_CHANNELS  : NATURAL := 6;
   constant CLK_PERIOD            : time    := 8 ns;
   constant CLK_PLL_PERIOD        : time    := 5 ns;

   signal s_clk            : STD_LOGIC;
   signal s_clk_125m_p     : STD_LOGIC;
   signal s_clk_125m_n     : STD_LOGIC;

   signal led_pw_o         : STD_LOGIC;
   signal led_err_o        : STD_LOGIC;
   signal led_ttl_o        : STD_LOGIC;

   signal fpga_o_en        : STD_LOGIC; 
   signal fpga_o_ttl_en    : STD_LOGIC;  
   signal fpga_o_inv_en    : STD_LOGIC; 
   signal fpga_o_blo_en    : STD_LOGIC; 

   signal level_i          : STD_LOGIC;
   signal switch_i         : STD_LOGIC;
   signal manual_rst_n_o   : STD_LOGIC; 

   signal pulse_i_front    : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);
   signal pulse_o_front    : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);

   signal pulse_i_rear     : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);
   signal pulse_o_rear     : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);

   signal led_o            : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);

   signal led_link_up_o    : STD_LOGIC; 
   signal led_pps_o        : STD_LOGIC; 
   signal led_wr_ok_o      : STD_LOGIC; 

   signal inv_i            : STD_LOGIC_VECTOR(3 downto 0);
   signal inv_o            : STD_LOGIC_VECTOR(3 downto 0);

   --! Signal sniffed from basic_trigger_top.vhd
   signal s_clk_200m       : STD_LOGIC;


   component basic_trigger_top is
      generic(g_NUMBER_OF_CHANNELS : NATURAL := 6);
      port (
         FPGA_CLK_P     : in STD_LOGIC;
         FPGA_CLK_N     : in STD_LOGIC;

         led_pw_o       : out STD_LOGIC;
         led_err_o      : out STD_LOGIC;
         led_ttl_o      : out STD_LOGIC;

         fpga_o_en      : out STD_LOGIC;
         fpga_o_ttl_en  : out STD_LOGIC;
         fpga_o_inv_en  : out STD_LOGIC;
         fpga_o_blo_en  : out STD_LOGIC;


         level          : in  STD_LOGIC;
         switch_i       : in  STD_LOGIC;   --! General enable
         manual_rst_n_o : out STD_LOGIC;   --! It allows power sequencing of the
                                           --! 24V rail after a security given
                                           --! delay
         pulse_i_front  : in  STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);
         pulse_o_front  : out STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);

         pulse_i_rear   : in  STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);
         pulse_o_rear   : out STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);

         led_o_front    : out STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);
         led_o_rear     : out STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS downto 1);

         --! WR LEDs not to let them ON
         led_link_up_o  : out STD_LOGIC;
         led_pps_o      : out STD_LOGIC;
         led_wr_ok_o    : out STD_LOGIC;

         inv_i          : in  STD_LOGIC_VECTOR(4 downto 1);
         inv_o          : out STD_LOGIC_VECTOR(4 downto 1));
   end component;

begin

   --! @brief Process to bypass internal signals requiered for correct
   --!        use of spi_analyser
   p_sig_spy : process is
   begin
      init_signal_spy("/uut/s_clk_200m", "s_clk_200m", 1);
      wait;
   end process p_sig_spy;

   s_clk125m_process :process
   begin
      s_clk <= '1';
      wait for CLK_PERIOD/2;
      s_clk <= '0';
      wait for CLK_PERIOD/2;
   end process;

   OBUFDS_inst : OBUFDS
   generic map (IOSTANDARD => "DEFAULT")
   port map (O => s_clk_125m_p,  OB => s_clk_125m_n,
             I => s_clk);

   uut: basic_trigger_top
   generic map(g_NUMBER_OF_CHANNELS => 6)
   port map(
      FPGA_CLK_P      => s_clk_125m_p,
      FPGA_CLK_N      => s_clk_125m_n,

      led_pw_o        => led_pw_o, 
      led_err_o       => led_err_o, 
      led_ttl_o       => led_ttl_o, 
                                      
      fpga_o_en       => fpga_o_en, 
      fpga_o_ttl_en   => fpga_o_ttl_en, 
      fpga_o_inv_en   => fpga_o_inv_en, 
      fpga_o_blo_en   => fpga_o_blo_en, 
       
      level           => level_i,
      switch_i        => switch_i, 
      manual_rst_n_o  => manual_rst_n_o , 

      pulse_i_front   => pulse_i_front, 
      pulse_o_front   => pulse_o_front, 

      pulse_i_rear    => pulse_i_rear, 
      pulse_o_rear    => pulse_o_rear, 

      led_o_front     => led_o, 
      led_o_rear      => led_o,

      led_link_up_o   => led_link_up_o, 
      led_pps_o       => led_pps_o, 
      led_wr_ok_o     => led_wr_ok_o, 
                                       
      inv_i           => inv_i, 
      inv_o           => inv_o); 


  tb_proc: process

    procedure initial_cond(level        : STD_LOGIC;
                           wait_link_up : BOOLEAN) is
    begin
--       s_rst           <= '1';
       level_i        <= level;
       switch_i       <= '1';
       --! The inputs that go to the FPGA are negated
       inv_i          <= (others => '1');
       --! The inputs that go to the FPGA are negated
       pulse_i_front  <= (others => level);
       --! The inputs that go to the FPGA are negated
       pulse_i_rear   <= (others => '1');
       if wait_link_up =  TRUE then
         wait until falling_edge(led_link_up_o); 
      end if;
         wait for CLK_PLL_PERIOD*50;
--         s_rst <= '0';
         wait for CLK_PLL_PERIOD*2;
         wait until rising_edge(s_clk_200m);
    end; 

    procedure input_pulse (signal pulse  : out
                           STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS - 1 downto 0);
                                  channel:     NATURAL;
                                  length :     time;
                                  level  :     STD_LOGIC) is
       variable v_pulse  : STD_LOGIC := level;
    begin
       --! As both the read and front are negated in V1 (rear is NOT negated
       --! in V2) we place the default signal level of the pulse as 'level'
       pulse(channel) <= level;
          wait until rising_edge(s_clk_200m);
       pulse(channel) <= not(level);
          wait for length; 
       pulse(channel) <= level;
         wait until rising_edge(s_clk_200m);
    end;

    procedure input_front_pulse (length :     time; channel : NATURAL;
                                 level_i:     STD_LOGIC) is
    begin
       input_pulse (pulse_i_front, channel, length, level_i);
    end;

    procedure input_rear_pulse (length : time; channel : NATURAL) is
    begin
       input_pulse (pulse_i_rear, channel, length, '1');
    end;

    procedure input_two_pulses (channel : NATURAL; level : STD_LOGIC;
                                length1 : time;
                                length2 : time;
                                offset  : time) is
    begin
       pulse_i_front(channel) <= level; 
       pulse_i_rear(channel)  <= '0';
         wait until rising_edge(s_clk_200m);
       pulse_i_rear(channel) <= '1';
         wait for offset;
         wait until rising_edge(s_clk_200m);
       pulse_i_front(channel) <= not(level);
         wait for length1 - offset;
         wait until rising_edge(s_clk_200m);
       pulse_i_rear(channel) <= '0';
         wait for length2 - length1;
         wait until rising_edge(s_clk_200m);
       pulse_i_front(channel) <= level;
         wait until rising_edge(s_clk_200m);
    end;

  begin


--    input_front_pulse (1200 ns, 0, '1');

    --! First we put one pulse from the Blocking part, then we wait one-clock
    --! and lastly we put another clock from the TTL input
    initial_cond('1', TRUE);
    input_rear_pulse  (1200 ns, 0);
      wait until rising_edge(s_clk_200m);
    input_front_pulse (1200 ns, 0, '1');

      wait for 100*CLK_PLL_PERIOD;
    --! We reboot the machine and change the switch to indicate level_i is
    --! TTL_N
    initial_cond('0', FALSE);
    input_rear_pulse  (1200 ns, 0);
      wait until rising_edge(s_clk_200m);
    input_front_pulse (1200 ns, 0, '1');

    input_two_pulses(0, '1',
                     1000 ns,
                     1000 ns,
                     212  ns);

    wait for 40*CLK_PLL_PERIOD;
    assert false report "End of test." severity failure;

  end process;

end;
