--------------------------------------------------------------------------------
-- CERN (BE-CO-HT)
-- Converter Common Gateware
-- https://www.ohwr.org/projects/conv-common-gw
--------------------------------------------------------------------------------
--
-- unit name:     Top-level simulation of conv_common_gw
--
-- description:
--
--
--
--------------------------------------------------------------------------------
-- Copyright (c) 2010-2012 CERN / BE-CO-HT
--------------------------------------------------------------------------------
-- GNU LESSER GENERAL PUBLIC LICENSE
--------------------------------------------------------------------------------
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.genram_pkg.all;
use work.conv_common_gw_pkg.all;
use work.testbench_pkg.all;
use work.print_pkg.all;

entity testbench is
end entity testbench;


architecture arch of testbench is

  --============================================================================
  -- Functions and procedures
  --============================================================================

  --============================================================================
  -- Signal declarations
  --============================================================================

  signal i2c_m_in                       : t_i2c_master_in;
  signal i2c_m_out                      : t_i2c_master_out;

  signal clk_20, clk_125                : std_logic;
  signal clk_125_p, clk_125_n           : std_logic;
  signal vme_sysreset_n                 : std_logic := '0';

  signal ttl_n_in, ttl_out              : std_logic_vector(C_NR_CHANS-1 downto 0) := (others => '1');
  signal rs485_n_in, rs485_fs_n_in      : std_logic_vector(C_NR_CHANS-1 downto 0) := (others => '1');
  signal rs485_out                      : std_logic_vector(C_NR_CHANS-1 downto 0) := (others => '1');
  signal inv_n_in, inv_out              : std_logic_vector(C_NR_INV_CHANS-1 downto 0) := (others => '0');

  signal sscl_out                       : std_logic;
  signal sscl_en_out                    : std_logic;
  signal ssda_out                       : std_logic;
  signal ssda_en_out                    : std_logic;

  signal mscl_out                       : std_logic;
  signal mscl_en_out                    : std_logic;
  signal msda_out                       : std_logic;
  signal msda_en_out                    : std_logic;

  signal sw_gp_n_in                     : std_logic_vector(7  downto 0);
  signal sw_other_in                    : std_logic_vector(31 downto 0);
  signal pcbrev                         : std_logic_vector(5  downto 0);
  signal rtm_in                         : std_logic_vector(5  downto 0);
  signal i2c_master_rcvd_val            : std_logic_vector(31 downto 0);
  signal i2c_master_rcvd_val1           : std_logic_vector(31 downto 0);

  signal mscl, msda                     : std_logic_vector(C_NR_MASTERS-1 downto 0);
  signal sscl, ssda                     : std_logic_vector(C_NR_SLAVES-1 downto 0);
  signal scl, sda                       : std_logic;

  signal rst_copm, rst_cnt              : std_logic := '0';
  type cnt_ch_array                      is array (C_NR_CHANS-1 downto 0) of integer;
  signal fp_rp_same_err                 : cnt_ch_array;
  signal fp_rp_oppos_err                : cnt_ch_array;
  signal in_out_oppos_err               : cnt_ch_array;
  signal in_out_same_err                : cnt_ch_array;
  signal pulse_cnt_ttl, pulse_cnt_rs485 : cnt_ch_array;
  signal inv_err                        : cnt_ch_array;
  signal rtm_db9_ch0_err                : integer;
  signal rtm_db9_ch1_err                : integer;
  signal test_id                        : string(7 downto 1);

  type cnt_inv_ch_array                is array (C_NR_INV_CHANS-1 downto 0) of integer;


  --==============================================================================
  --  architecture begin
  --==============================================================================
begin

  --============================================================================
  -- Instantiate DUT
  --============================================================================
  cmp_DUT : conv_ttl_rs485
  generic map (g_Simul     => true,
               g_rst_time  => 10)
  port map
  (
    -- Clocks
    clk_20_i                => clk_20,
    clk_125_p_i             => clk_125_p,
    clk_125_n_i             => clk_125_n,
    -- I2C interface
    scl_i                   => scl,
    scl_o                   => sscl_out,
    scl_en_o                => sscl_en_out,
    sda_i                   => sda,
    sda_o                   => ssda_out,
    sda_en_o                => ssda_en_out,
    -- VME interface
    vme_sysreset_n_i        => vme_sysreset_n,
    vme_ga_i                => "11110", --
    vme_gap_i               => '0',
    -- PCB version recognition
    pcbrev_i                => pcbrev,
    -- Channel enable
    global_oen_o            => open,
    ttl_oen_o               => open,
    inv_oen_o               => open,
    rs485_oen_o             => open,
    -- Front panel channels
    ttl_n_i                 => ttl_n_in,
    ttl_o                   => ttl_out,
    inv_n_i                 => inv_n_in,
    inv_o                   => inv_out,
    -- Rear panel channels
    rs485_n_i               => rs485_n_in,
    rs485_fs_n_i            => rs485_fs_n_in,
    rs485_o                 => rs485_out,
    -- Rear input and output termination lines
    iterm_en_o              => open,
    oterm_en_o              => open,
    -- Channel leds
    led_front_n_o           => open,
    led_front_inv_n_o       => open,
    led_rear_n_o            => open,
    -- SPI interface to on-board flash chip
    flash_cs_n_o            => open,
    flash_sclk_o            => open,
    flash_mosi_o            => open,
    flash_miso_i            => 'Z',
    -- PLL DACs
    dac20_din_o             => open,
    dac20_sclk_o            => open,
    dac20_sync_n_o          => open,
    dac125_din_o            => open,
    dac125_sclk_o           => open,
    dac125_sync_n_o         => open,
    -- SFP lines
    sfp_los_i               => '1',
    sfp_present_i           => '0',
    sfp_rate_select_o       => open,
    sfp_sda_b               => open,
    sfp_scl_b               => open,
    sfp_tx_disable_o        => open,
    sfp_tx_fault_i          => '1',
    -- Thermometer data port
    thermometer_b           => open,
    -- Switches
    sw_gp_n_i               => sw_gp_n_in,
    sw_multicast_n_i        => (others => '0'), -- not used
    -- RTM lines
    rtmm_i                  => rtm_in(5 downto 3),
    rtmp_i                  => rtm_in(2 downto 0),
    -- Front panel bicolor LEDs
    led_ctrl0_o             => open,
    led_ctrl0_oen_o         => open,
    led_ctrl1_o             => open,
    led_ctrl1_oen_o         => open,
    led_gp_2_4_o            => open,
    led_gp_1_3_o            => open,
    led_oterm_wr_o          => open,
    led_iterm_syserror_o    => open,
    led_gf_syspw_o          => open,
    led_ttl_i2c_o           => open);



  sscl(0) <= sscl_out when sscl_en_out = '1' else '1';
  ssda(0) <= ssda_out when ssda_en_out = '1' else '1';


  --============================================================================
  -- Generate clock and reset signals
  --============================================================================
  p_clk_20 : process
  begin
    clk_20 <= '0';
    wait for C_CLK_20_PER/2;
    clk_20 <= '1';
    wait for C_CLK_20_PER/2;
  end process p_clk_20;

  p_clk_125 : process
  begin
    clk_125 <= '0';
    wait for C_CLK_125_PER/2;
    clk_125 <= '1';
    wait for C_CLK_125_PER/2;
  end process p_clk_125;

  clk_125_p <= clk_125;
  clk_125_n <= not clk_125;

  p_vme_sysreset_n : process
  begin
    vme_sysreset_n <= '0';
    wait for c_reset_width;
    vme_sysreset_n <= '1';
    wait for c_reset_width;
    vme_sysreset_n <= '0';
    wait for c_reset_width;
    vme_sysreset_n <= '1';
    wait;
  end process p_vme_sysreset_n;


  --============================================================================
  -- Instantiate I2C master to access DUT
  --============================================================================

  cmp_i2c_master_and_driver : entity work.i2c_master_and_driver
    port map (
      clk_i                            => clk_20,
      rst_n_i                          => vme_sysreset_n,
      ready_o                          => i2c_m_out.i2c_master_ready,
      start_i                          => i2c_m_in.i2c_master_start,
      finish_o                         => i2c_m_out.i2c_master_finish,
      rdwr_i                           => i2c_m_in.i2c_master_rdwr,
      slv_addr_i                       => i2c_m_in.i2c_master_slv_addr,
      reg_addr_i                       => i2c_m_in.i2c_master_reg_addr,
      send_val_i                       => i2c_m_in.i2c_master_send_val,
      rcvd_val_o                       => i2c_m_out.i2c_master_rcvd_val,

      scl_i                            => scl,
      scl_o                            => mscl_out,
      scl_oen                          => mscl_en_out,
      sda_i                            => sda,
      sda_o                            => msda_out,
      sda_oen                          => msda_en_out
  );

  -- Tri-state buffers on the I2C lines
  mscl(0) <= mscl_out when mscl_en_out = '0' else '1';
  msda(0) <= msda_out when msda_en_out = '0' else '1';

  --============================================================================
  -- Bus model connection to master and slaves
  --============================================================================
  cmp_i2c_bus : entity work.i2c_bus_model
    generic map(
      g_nr_masters => C_NR_MASTERS,
      g_nr_slaves  => C_NR_SLAVES
    )
    port map (
      mscl_i => mscl,
      msda_i => msda,
      sscl_i => sscl,
      ssda_i => ssda,
      scl_o  => scl,
      sda_o  => sda
    );


  --============================================================================
  -- Check if TTL_out, TTL_IN and RS485 match or are opposite
  --============================================================================
  compare_front_rear_pulses : process (clk_125)
  begin
    if rising_edge (clk_125) then
      if vme_sysreset_n = '0' or rst_copm = '1' then
        fp_rp_same_err   <= (others => (0));
        fp_rp_oppos_err  <= (others => (0));
        in_out_same_err  <= (others => (0));
        in_out_oppos_err <= (others => (0));
        rtm_db9_ch0_err  <= 0;
        rtm_db9_ch1_err  <= 0;
      else
        for i in 0 to C_NR_CHANS-1 loop  -- loop: 7 channels
          if ttl_out(i) /= rs485_out(i) then
            fp_rp_same_err(i) <= fp_rp_same_err(i)+1;
          end if;

          if ttl_out(i) /= not rs485_out(i) then
            fp_rp_oppos_err(i) <= fp_rp_oppos_err(i)+1;
          end if;

          if ttl_out(i) /= not ttl_n_in(i) then
            in_out_oppos_err(i) <= in_out_oppos_err(i)+1;
          end if;

          if ttl_out(i) /= ttl_n_in(i) then
            in_out_same_err(i) <= in_out_same_err(i)+1;
          end if;

        end loop;

        if (rs485_out(0) /= rs485_out(1)) or (ttl_out(0) /= rs485_out(0)) then
          rtm_db9_ch0_err <= rtm_db9_ch0_err+1;
        end if;

        if (rs485_out(4) /= rs485_out(5)) or (ttl_out(1) /= rs485_out(4)) then
          rtm_db9_ch1_err <= rtm_db9_ch1_err+1;
        end if;

      end if;
    end if;
  end process compare_front_rear_pulses;


  --============================================================================
  -- Check INV channels
  --============================================================================
  compare_inv_i_inv_o : process (clk_125)
  begin
    if rising_edge (clk_125) then
      if vme_sysreset_n = '0' then
        inv_err  <= (others => (0));
      else
        for i in 0 to C_NR_INV_CHANS-1 loop -- loop: 4 INV channels
          if inv_n_in(i) /= inv_out(i) then
            inv_err(i) <= inv_err(i)+1;
          end if;
        end loop;
      end if;
    end if;
  end process compare_inv_i_inv_o;

  --============================================================================
  -- pulse counter
  --============================================================================
  pulse_counter_ttl : process (ttl_out, vme_sysreset_n, rst_cnt)
  begin
    if vme_sysreset_n = '0' or rst_cnt = '1' then
      pulse_cnt_ttl  <= (others => (0));
    else
    -- for i in 0 to C_NR_CHANS-1 loop
      if rising_edge(ttl_out(0)) then
        pulse_cnt_ttl(0) <= pulse_cnt_ttl(0) +1;
      end if;
      if rising_edge(ttl_out(1)) then
        pulse_cnt_ttl(1) <= pulse_cnt_ttl(1) +1;
      end if;
      if rising_edge(ttl_out(2)) then
        pulse_cnt_ttl(2) <= pulse_cnt_ttl(2) +1;
      end if;
      if rising_edge(ttl_out(3)) then
        pulse_cnt_ttl(3) <= pulse_cnt_ttl(3) +1;
      end if;
      if rising_edge(ttl_out(4)) then
        pulse_cnt_ttl(4) <= pulse_cnt_ttl(4) +1;
      end if;
      if rising_edge(ttl_out(5)) then
        pulse_cnt_ttl(5) <= pulse_cnt_ttl(5) +1;
      end if;
    -- end loop;
    end if;
  end process pulse_counter_ttl;

  pulse_counter_rs485 : process (rs485_out, vme_sysreset_n, rst_cnt)
  begin
    if vme_sysreset_n = '0' or rst_cnt = '1' then
      pulse_cnt_rs485  <= (others => (0));
    else
  -- for i in 0 to C_NR_CHANS-1 loop
      if rising_edge(rs485_out(0)) then
        pulse_cnt_rs485(0) <= pulse_cnt_rs485(0) +1;
      end if;
      if rising_edge(rs485_out(1)) then
        pulse_cnt_rs485(1) <= pulse_cnt_rs485(1) +1;
      end if;
      if rising_edge(rs485_out(2)) then
        pulse_cnt_rs485(2) <= pulse_cnt_rs485(2) +1;
      end if;
      if rising_edge(rs485_out(3)) then
        pulse_cnt_rs485(3) <= pulse_cnt_rs485(3) +1;
      end if;
      if rising_edge(rs485_out(4)) then
        pulse_cnt_rs485(4) <= pulse_cnt_rs485(4) +1;
      end if;
      if rising_edge(rs485_out(5)) then
        pulse_cnt_rs485(5) <= pulse_cnt_rs485(5) +1;
      end if;
    -- end loop;
    end if;
  end process pulse_counter_rs485;

  --============================================================================
  -- processes to provide stimulus and checks
  --============================================================================

  -- i2c access
  p_stim : process
    variable i                        : integer := 0;
    variable nb_pulses_to_send_fp     : integer := 17;
    variable nb_pulses_to_send_fp_slv : std_logic_vector(31 downto 0);
    variable nb_pulses_to_send_rp     : integer := 5;
    variable nb_pulses_to_send_rp_slv : std_logic_vector(31 downto 0);
    variable nb_pulses_to_send_inv    : integer := 5;
    variable nb_pulses_to_send_bar    : integer := 6;
    variable glitch_filter_en         : std_logic := '0'; -- glitch filter disabled
    variable ttl_out_bar_en           : std_logic := '0'; -- TTLbar disabled
    variable rtm                      : std_logic_vector(5 downto 0) := C_NO_RTM;
    variable err_cnt, err             : integer := 0;
    variable time_intrvl, rst_timetag : integer := 0;
    variable pulse_timetag            : integer := 0;
    constant c_K                        : time := 1 nS;

  begin
    print("*****************************************************************************");
    print("                                 Start of Tests");
    print("*****************************************************************************");

    ---------------------------------------------------------------------------
    -- Board settings
    print_now("----------------------------------------------------------------");
    print_now("---> Configure board settings");
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in,
    pcbrev, rtm_in);

    ---------------------------------------------------------------------------
    -- VME reset
    print_now("----------------------------------------------------------------");
    print_now("---> VME reset");
    print_now("Sending VME reset");
    wait until vme_sysreset_n = '1';
    print_now("VME reset completed");
    wait for 6500 ns; -- for resets and inhibit_pulse to have finished
    rst_timetag := now/c_K;
    ---------------------------------------------------------------------------

    ---------------------------------------------------------------------------
    -- I2C readings
    test_id <= "TEST 1 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 01: I2C reg reading");
    print_now("----------------------------------------------------------------");

    -- I2C reading of the board ID register
    print_now_s("I2C reading of the register ", c_REG_MAP(0).reg_name);
    read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(0).reg_addr,
    c_REG_MAP(0).reg_name, i2c_master_rcvd_val, C_BOARD_ID, TRUE, err);
    err_cnt := err_cnt +err;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- I2C writing
    test_id <= "TEST 2 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 02: I2C reg writing");
    print_now("----------------------------------------------------------------");

   -- I2C writing of a big value to Front Panel Counter 1
    print_now_s("I2C writing to the register ", c_REG_MAP(4).reg_name);
    write_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(4).reg_addr,
    c_REG_MAP(4).reg_name, C_INITIAL_TST_VALUE, err);
    err_cnt := err_cnt +err;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Pulses to the front TTL input
    test_id <= "TEST 3 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 03: Sending TTL pulses and checking the outputs");
    print_now("----------------------------------------------------------------");

    -- Sending pulses to Front Panel Channel 1
    nb_pulses_to_send_fp_slv := std_logic_vector(to_unsigned(nb_pulses_to_send_fp,
    nb_pulses_to_send_fp_slv'length));
    print_now_s_i("Sending TTL pulses to TTL_N_I(0)", nb_pulses_to_send_fp);
    generate_pulse (ttl_n_in(0), nb_pulses_to_send_fp, "fp", rs485_fs_n_in(0), 200 ns, 200 ns);
    -- 200ns so that the counters can count correctly

    -- Checking if the number of TTL output pulses matches the sent ones
    print_now("Check that the pulses arrived to the TTL_O(0)");
    if pulse_cnt_ttl(0) = nb_pulses_to_send_fp then
      print_now_s_i("[OK] Number of pulses measured at TTL_O(0): ",pulse_cnt_ttl(0));
    else
      print_now_s_i("[ERR] Number of pulses measured at TTL_O(0): ",pulse_cnt_ttl(0));
      err_cnt := err_cnt + 1;
    end if;

    -- Check if TTL_N_IN out and TTL_O out are opposite
    print_now("Check that TTL_O(0) is the inverted TTL_N_IN(0)");
    if in_out_oppos_err(0) = 0 then
      print_now("[OK] TTL_O(0) is exactly the inverted TTL_N_IN(0)");
    else
      print_now("[ERR] TTL_O(0) is not exactly the inverted TTL_N_IN(0)");
      err_cnt := err_cnt + 1;
    end if;

    -- Check if TTL out and RS485 out are the same
    print_now("Check that the pulses arrived to the RS485_O(0)");
    if fp_rp_same_err(0) = 0 then
      print_now("[OK] RS485_O(0) matches TTL_O(0)");
    else
      print_now("[ERR] RS485_O(0) does not match TTL_O(0)");
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Counters reading
    test_id <= "TEST 4 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 04: Reading of the pulse counters through I2C");
    print_now("----------------------------------------------------------------");

    -- Reading from the I2C Front Panel regs; read only two counters to speed up testbench
    while not(i = 3) loop
      if i = 0 then
        -- Front Panel Channel 1 register should roll over and read
        print_now_s("I2C reading of the register ", c_REG_MAP(i+4).reg_name);
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+4).reg_addr,
        c_REG_MAP(i+4).reg_name, i2c_master_rcvd_val,
        std_logic_vector(unsigned(C_INITIAL_TST_VALUE)+unsigned(nb_pulses_to_send_fp_slv)), TRUE, err);
        err_cnt := err_cnt + err;
      elsif i = 1 then
        -- Front Panel Channel 2 register should have not counted any pulses
        print_now_s("I2C reading of the register ", c_REG_MAP(i+4).reg_name);
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+4).reg_addr,
        c_REG_MAP(i+4).reg_name, i2c_master_rcvd_val, (others => '0'), TRUE, err);
        err_cnt := err_cnt + err;
      end if;
      i := i + 1;
    end loop;

    print_now("Checking that the rear pulse counters have not registered any pulses");

    -- Reading from the I2C Rear Panel regs; read only two counters to speed up testbench
    i := 0;
    while not(i = 2) loop
      if i = 0 then
        -- Check Rear Panel Channel 1 counter has not counted any pulses
        print_now_s("I2C reading of the register ", c_REG_MAP(i+10).reg_name);
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+10).reg_addr,
        c_REG_MAP(i+10).reg_name, i2c_master_rcvd_val, (others => '0'), TRUE, err);
        err_cnt := err_cnt + err;
      elsif i = 1 then
        -- Check Rear Panel Channel 1 counter has not counted any pulses
        print_now_s("I2C reading of the register ", c_REG_MAP(i+10).reg_name);
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+10).reg_addr,
        c_REG_MAP(i+10).reg_name, i2c_master_rcvd_val, (others => '0'), TRUE, err);
        err_cnt := err_cnt + err;
      end if;
      i := i + 1;
    end loop;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Pulses to the rear RS485 input
    test_id <= "TEST 5 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 05: Sending pulses to the RS485 and checking the outputs");
    print_now("----------------------------------------------------------------");

    -- Sending pulses to Rear Panel Channel 1
    nb_pulses_to_send_rp := 5;
    nb_pulses_to_send_rp_slv := std_logic_vector(to_unsigned(nb_pulses_to_send_rp, nb_pulses_to_send_rp_slv'length));
    print_now_s_i("Sending pulses to RS485_N_I(0): ", nb_pulses_to_send_rp);
    generate_pulse (rs485_n_in(0), nb_pulses_to_send_rp, "rp", rs485_fs_n_in(0), 200 ns, 200 ns);
    pulse_timetag := (now/c_K);

    -- Checking if the number of RS485 output pulses matches the sent ones
    print_now("Check that the pulses arrived to the RS485_O(0)");
    if pulse_cnt_rs485(0) = nb_pulses_to_send_rp+nb_pulses_to_send_fp then
      print_now_s_i("[OK] Number of pulses measured at RS485_O(0) :",pulse_cnt_rs485(0));
    else
      print_now_s_i("[ERR] Number of pulses measured in the RS485_O(0) : ",pulse_cnt_rs485(0));
      err_cnt := err_cnt + 1;
    end if;

    -- Check if TTL out and RS485 out are the same
    print_now("Check that the pulses arrived to the TTL_O(0)");
    if fp_rp_same_err(0) = 0 then
      print_now("[OK] RS485_O(0) matches TTL_O(0)");
    else
      print_now("[ERR] RS485_O(0) does not match TTL_O(0)");
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Counters reading
    test_id <= "TEST 6 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 06: Reading of the pulse counters through I2C");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Reading from the I2C Rear Panel regs; read two reg to speed up testbench
    i := 0;
    while not(i = 2) loop
      if i = 0 then
        -- Check Rear Panel Channel 1 counter has correctly counted the pulses
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+10).reg_addr,
        c_REG_MAP(i+10).reg_name, i2c_master_rcvd_val, nb_pulses_to_send_rp_slv, TRUE, err);
        err_cnt := err_cnt + err;
      elsif i = 1 then
        -- Check Rear Panel Channel 2 has not counted any pulses
        read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, c_REG_MAP(i+10).reg_addr,
        c_REG_MAP(i+10).reg_name, i2c_master_rcvd_val, (others => '0'), TRUE, err);
        err_cnt := err_cnt + err;
      end if;
      i := i + 1;
    end loop;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Timetag reading
    test_id <= "TEST 7 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 07: Reading of the timetag register through I2C");
    print_now("----------------------------------------------------------------");
    read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, CH1LTSCYR, "CH1LTSCY",
    i2c_master_rcvd_val1, (others => '0'), FALSE, err);
    time_intrvl := (pulse_timetag - rst_timetag)/8;
    print_now_s_i("Estimated timetag cycles: ",time_intrvl);
    if to_integer(unsigned(i2c_master_rcvd_val1(27 downto 0))) > time_intrvl+1000
or to_integer(unsigned(i2c_master_rcvd_val1(27 downto 0))) < time_intrvl-1000
    then
      print_now_s_std_s_std("[ERR] Registered cycles: ", i2c_master_rcvd_val1,
      " is far from the estimated: ", std_logic_vector(to_unsigned(time_intrvl,28)));
      err_cnt := err_cnt + 1;
    else
      print_now_s_std_s_std("[OK] Registered cycles: ", i2c_master_rcvd_val1(27 downto 0),
      " is close to the estimated: ", std_logic_vector(to_unsigned(time_intrvl,28)));
    end if;
    read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, CH1LTSTLR, "CH1LTSTL",
    i2c_master_rcvd_val, (others => '0'), TRUE, err);
    err_cnt := err_cnt + err;
    read_i2c (i2c_m_in, i2c_m_out, C_I2C_MASTER_SLV_ADDR, CH1LTSCYR, "CH1LTSCY",
    i2c_master_rcvd_val, i2c_master_rcvd_val1, TRUE, err);
    err_cnt := err_cnt + err;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- INV channels
    test_id <= "TEST 8 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 08: INV channel check");
    print_now("----------------------------------------------------------------");
    -- Note INV channels just take INV_N_I and output the same signal to INV_O;
    -- inversion happens through inv buffers; there is no associated counter, timetag, rear channel
    print_now_s_i("Sending pulses to INV_N_I(3): ", nb_pulses_to_send_inv);
    generate_pulse (inv_n_in(3), nb_pulses_to_send_inv, "fp", rs485_fs_n_in(3), 40 ns, 20 ns);

    print_now("Check INV_O(3) output");
    if inv_err(3) = 0 then
      print_now("[OK] INV_O(3) matches INV_N_I(3)");
    else
      print_now("[ERR] INV_O(3) does not match INV_N_I(3)");
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- Glitch filter test
    test_id <= "TEST 9 ";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 09: Test Glitch filter");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '1';
    ttl_out_bar_en   := '0';
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    print_now_s_i("Sending pulses 50ns-long each to TTL_N_I(3): ", nb_pulses_to_send_fp);
    generate_pulse (ttl_n_in(3), nb_pulses_to_send_fp, "fp", rs485_fs_n_in(3), 50 ns, 50 ns);

    -- Checking if any pulse was passed to the output
    print_now("Check that the glitches were filtered and there is no pulse in the TTL_O(3) output");
    if pulse_cnt_ttl(3) = 0 then
      print_now("[OK] Glitch filter filtered the glitches");
    else
      print_now_s_i("[ERR] Number of pulses measured at TTL_O(3): ",pulse_cnt_ttl(3));
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------
    -- TTL bar test
    test_id <= "TEST 10";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 10: Test TTL BAR");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '0';
    ttl_out_bar_en   := '1';
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    rst_copm <= '1';
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    rst_copm <= '0';

    print_now_s_i("Sending pulses to TTL_N_IN(5): ", nb_pulses_to_send_bar);
    generate_pulse (ttl_n_in(5), nb_pulses_to_send_bar, "fp", rs485_fs_n_in(5), 200 ns, 200 ns);

    -- Check if TTL_N_IN is the opposite of TTL_O; Note: TTLbar does not invert the output!
    print_now("Check that TTL_O(5) matches TTL_N_IN(5)");
    if in_out_oppos_err(5) = 0 then
      print_now("[OK] TTL_O(5) matches TTL_N_IN(5)");
    else
      print_now("[ERR] TTL_O(5) does not match TTL_N_IN(5)");
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------

    -- Glitch filter enabled with TTL bar test
    test_id <= "TEST 11";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 11: Test glitch filter enabled with TTL BAR");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '1';
    ttl_out_bar_en   := '1';
    nb_pulses_to_send_bar := 2;
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    rst_copm <= '1';
    rst_cnt  <= '1';
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    rst_copm <= '0';
    rst_cnt  <= '0';

    -- create a 50ns glitch during a 450ns pulse: ------|____|-|____|------
    print_now("Sending a 50ns glitch during a 450ns pulse to TTL_N_IN(5)");
    generate_pulse (ttl_n_in(5), nb_pulses_to_send_bar, "fp", rs485_fs_n_in(5), 200 ns, 50 ns);

    -- Checking that a single pulse passed to the output
    print_now("Check that the 50ns glitch within the 450ns pulse was filtered out and a single 450ns pulse arrived to the output");
    if pulse_cnt_ttl(5) = 1 then
      print_now("[OK] Glitch filter filtered the glitch");
    else
      print_now_s_i("[ERR] Number of pulses measured at TTL_O(5): ",pulse_cnt_ttl(5));
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------


    ---------------------------------------------------------------------------

    -- Glitch filter disabled with TTL bar testt
    test_id <= "TEST 12";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 12: Test glitch filter disable with TTL BAR");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '0';
    ttl_out_bar_en   := '1';
    nb_pulses_to_send_bar := 2;
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    rst_copm <= '1';
    rst_cnt  <= '1';
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    rst_copm <= '0';
    rst_cnt  <= '0';

    -- create a 50ns glitch during a 450ns pulse: ------|____|-|____|------
    print_now("Sending a 50ns glitch during a 450ns pulse to TTL_N_IN(5)");
    generate_pulse (ttl_n_in(5), nb_pulses_to_send_bar, "fp", rs485_fs_n_in(5), 200 ns, 50 ns);

    -- Checking that the glitch passed to the output
    print_now("Check that the 50ns glitch within the 450ns pulse passed to the output");
    if pulse_cnt_ttl(5) = 2 then
      print_now("[OK] Glitch filter correctly disabled");
    else
      print_now_s_i("[ERR] Number of pulses measured at TTL_O(5): ",pulse_cnt_ttl(5));
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------

    -- DB9 RTM test from TTL_N_IN -> RS485_O
    test_id <= "TEST 13";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 13: Test RS485 outputs with DB9 RTM");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '0';
    ttl_out_bar_en   := '0';
    nb_pulses_to_send_fp := 2;
    rtm := C_RTM_DB9;
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    rst_copm <= '1';
    rst_cnt  <= '1';
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    rst_copm <= '0';
    rst_cnt  <= '0';

    -- generate pulses
    print_now_s_i("Sending pulses to TTL_N_IN(0): ", nb_pulses_to_send_fp);
    generate_pulse (ttl_n_in(0), nb_pulses_to_send_fp, "fp", rs485_fs_n_in(0), 200 ns, 200 ns);

    nb_pulses_to_send_fp := 4;
    print_now_s_i("Sending pulses to TTL_N_IN(1): ", nb_pulses_to_send_fp);
    generate_pulse (ttl_n_in(1), nb_pulses_to_send_fp, "fp", rs485_fs_n_in(1), 200 ns, 200 ns);

    -- Check that the TTL_N_IN(0) pulses arrived to both Rear Panel Channel 1 and Rear Panel Channel 2
    print_now("Check that the TTL_N_IN(0) pulses arrived to the RS485_O(0) and RS485_O(1)");
    if rtm_db9_ch0_err = 0  then
      print_now("[OK] RS485_O(0) and RS485_O(1) match TTL_O(0)");
    else
      print_now("[ERR] RS485_O(0) or RS485_O(1) do not match TTL_O(0)");
      err_cnt := err_cnt + 1;
    end if;

    -- Check that the TTL_N_IN(1) pulses arrived to both Rear Panel Channel 1 and Rear Panel Channel 2 have
    print_now("Check that the TTL_N_IN(1) pulses arrived to the RS485_O(4) and RS485_O(5)");
    if rtm_db9_ch1_err = 0  then
      print_now("[OK] RS485_O(4) and RS485_O(5) match TTL_O(1)");
    else
      print_now("[ERR] RS485_O(4) or RS485_O(5) do not match TTL_O(1)");
      err_cnt := err_cnt + 1;
    end if;
    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------


    ---------------------------------------------------------------------------

    -- DB9 RTM test from RS485_N_I -> TTL_O
    test_id <= "TEST 14";
    print_now("----------------------------------------------------------------");
    print_now("---> Test 14: Test TTL outputs with DB9 RTM");
    print_now("----------------------------------------------------------------");
    ---------------------------------------------------------------------------
    -- Change board settings
    print_now("---> Configure board settings");
    glitch_filter_en := '0';
    ttl_out_bar_en   := '0';
    nb_pulses_to_send_rp := 10;
    rtm := C_RTM_DB9;
    settings_config (glitch_filter_en, ttl_out_bar_en, rtm, sw_gp_n_in, sw_other_in, pcbrev, rtm_in);
    rst_copm <= '1';
    rst_cnt  <= '1';
    wait for 2 us; -- ML: does not work without this wait, no idea way?
    rst_copm <= '0';
    rst_cnt  <= '0';

    -- generate pulses
    print_now_s_i("Sending pulses to RS485_N_I(4): ", nb_pulses_to_send_rp);
    generate_pulse (rs485_n_in(4), nb_pulses_to_send_rp, "rp", rs485_fs_n_in(4), 200 ns, 200 ns);

    print_now("Check that the pulses arrived to the RS485_O(4)");
    if pulse_cnt_rs485(4) = nb_pulses_to_send_rp then
      print_now_s_i("[OK] Number of pulses measured at RS485_O(4) :",pulse_cnt_rs485(4));
    else
      print_now_s_i("[ERR] Number of pulses measured in the RS485_O(4) : ",pulse_cnt_rs485(4));
      err_cnt := err_cnt + 1;
    end if;


    -- Check that the RS485_N_I(4) pulses arrived to RS485_OUT(4), RS485_OUT(5) and TTL_O(1)
    print_now("Check that the RS485_N_I(4) pulses arrived to RS485_OUT(4), RS485_OUT(5) and TTL_O(1)");
    if rtm_db9_ch1_err = 0  then
      print_now("[OK] TTL_O(1) and RS485_N_I(5) match RS485_N_I(4)");
    else
      print_now("[ERR] TTL_O(1) or RS485_OUT(5) does not match RS485_OUT(4)");
      err_cnt := err_cnt + 1;
    end if;

    ---------------------------------------------------------------------------

    print_now_s_i("Errors so far:             ", err_cnt);

    ---------------------------------------------------------------------------

    print("*****************************************************************************");
    print("                                     Tests Summary");
    print("*****************************************************************************");
    print_now_s_i("Total number of errors:    ", err_cnt);
    print("*****************************************************************************");
    wait for 500 us;

  end process p_stim;


end architecture arch;
--==============================================================================
--  architecture end
--==============================================================================
