--------------------------------------------------------------------------------
-- 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 behav of testbench is

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

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

  type t_i2c_master_in is record
    i2c_master_start                  : std_logic;  
    i2c_master_rdwr                   : std_logic;
    i2c_master_slv_addr               : std_logic_vector(6 downto 0);
    i2c_master_reg_addr               : std_logic_vector(31 downto 0);
    i2c_master_send_val               : std_logic_vector(31 downto 0);
  end record;

  type t_i2c_master_out is record
    i2c_master_ready                  : std_logic;
    i2c_master_finish                 : std_logic;
    i2c_master_rcvd_val               : std_logic_vector(31 downto 0);
  end record;

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

  signal gf_en_n_in                   : std_logic;
  
  signal ttl_n_in, ttl_out            : std_logic_vector(C_NR_CHANS-1 downto 0);
  signal rs485_n_in                   : std_logic_vector(C_NR_CHANS-1 downto 0);
  
--  signal sscl_in                      : std_logic;
--  signal ssda_in                      : std_logic;
  signal sscl_out                     : std_logic;
  signal sscl_en_out                  : std_logic;
  signal ssda_out                     : std_logic;
  signal ssda_en_out                  : std_logic;
  
--  signal mscl_in                      : std_logic;
--  signal msda_in                      : 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 rtmm_in                      : std_logic_vector(2  downto 0);
  signal rtmp_in                      : std_logic_vector(2  downto 0);
  
  signal line_front_in                : std_logic_vector(C_NR_CHANS-1 downto 0);
  signal line_inv_in                  : std_logic_vector(C_NR_INV_CHANS-1 downto 0);
  signal line_rear_in                 : std_logic_vector(C_NR_CHANS-1 downto 0);
  signal line_front_fs_in             : std_logic_vector(C_NR_CHANS-1 downto 0);
  signal line_inv_fs_in               : std_logic_vector(C_NR_INV_CHANS-1 downto 0);
  signal line_rear_fs_in              : std_logic_vector(C_NR_CHANS-1 downto 0);

  signal i2c_master_rcvd_val, i2c_master_rcvd_val1,i2c_master_rcvd_val2, i2c_master_rcvd_val3, i2c_master_rcvd_val4           : std_logic_vector(31 downto 0);
  signal i2c_m_in                     : t_i2c_master_in;
  signal i2c_m_out                    : t_i2c_master_out;
  
  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 buf_byte_cnt                 : natural;
--  signal nb_pulses_to_send_slv           : std_logic_vector(31 downto 0);
  signal reg_id : integer := 0;
  signal adr : std_logic_vector(31 downto 0);


    procedure read_i2c (signal   i2c_m_in : out t_i2c_master_in;
                        signal   i2c_m_out: in  t_i2c_master_out;
                        constant slv_addr : in  std_logic_vector(6 downto 0);
                        constant reg_addr : in  std_logic_vector(31 downto 0);
                        constant reg_name : string(1 to 8);
                        signal   rcvd_val : out std_logic_vector(31 downto 0);
                        constant exp_val  : in  std_logic_vector(31 downto 0)) is
  begin
    print_now ("-------------------------------------------------");
    print_now("read_i2c: start"); 
    i2c_m_in.i2c_master_start    <= '0';
    i2c_m_in.i2c_master_rdwr     <= '0'; --mst_fsm_op
    i2c_m_in.i2c_master_slv_addr <= "1011110";
    i2c_m_in.i2c_master_reg_addr <= (others => '0');
    i2c_m_in.i2c_master_send_val <= (others => '1');
    wait for 1us;
    i2c_m_in.i2c_master_slv_addr  <= slv_addr;
    i2c_m_in.i2c_master_reg_addr  <= reg_addr;
    i2c_m_in.i2c_master_start     <= '1';
    i2c_m_in.i2c_master_rdwr      <= '1'; --0: write
    wait for C_CLK_20_PER;
    i2c_m_in.i2c_master_start     <= '0';
    wait until i2c_m_out.i2c_master_finish = '1';
    rcvd_val             <= i2c_m_out.i2c_master_rcvd_val;
    print_now_s_std_s_std("I2C value read from 0x", reg_addr(7 downto 0),
                          " is 0x",i2c_m_out.i2c_master_rcvd_val);
    wait until i2c_m_out.i2c_master_finish = '0';
    print_now("read_i2c: completed"); 
    if i2c_m_out.i2c_master_rcvd_val = exp_val then
      print_now_s_s("[OK] Correct reading from reg ", reg_name, "; expected value matches read value");
    else
      print_now_s_std_s_std("-------> [ERR]: Read value", i2c_master_rcvd_val,
                            " expected value ",exp_val);
    end if;
    print_now ("-------------------------------------------------");
  end procedure read_i2c;

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

  --============================================================================
  -- Instantiate DUT
  --============================================================================
  cmp_DUT: conv_ttl_rs485
  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,  --: in  std_logic_vector(5 downto 0);
    ttl_o                   => ttl_out,   --: out std_logic_vector(5 downto 0);
    inv_n_i                 => (others => '0'), --inv_n_in,  --: in  std_logic_vector(3 downto 0);
    inv_o                   => open, --inv_n_out, --: out std_logic_vector(3 downto 0);
    -- Rear panel channels
    rs485_n_i               => rs485_n_in,      --: in  std_logic_vector(5 downto 0);
    rs485_fs_n_i            => (others => '0'), --: in  std_logic_vector(5 downto 0); --failsafe?
    rs485_o                 => open,       --: out std_logic_vector(5 downto 0);
    -- 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                  => rtmm_in,
    rtmp_i                  => rtmp_in,
    -- 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_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;

  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
    );
    
  --============================================================================
  -- processes to provide stimulus and checks
  --============================================================================

  --i2c access
  p_stim_i2c : process
  variable i                     : integer := 0;
  variable nb_pulses_to_send     : integer := 150;
  variable nb_pulses_to_send_slv : std_logic_vector(31 downto 0);

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

    ---------------------------------------------------------------------------
    -- 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
    ---------------------------------------------------------------------------

    ---------------------------------------------------------------------------
    -- I2C reading of the board ID register
    read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(0).reg_addr, c_REG_MAP(0).reg_name, i2c_master_rcvd_val, C_BOARD_ID);
    ---------------------------------------------------------------------------

    ---------------------------------------------------------------------------
    -- Sending pulses to Front Panel Channel 1      
    nb_pulses_to_send_slv := std_logic_vector(to_unsigned(nb_pulses_to_send, nb_pulses_to_send_slv'length));
    print_now_s_i("Sending TTL front pulses to Channel 1: ", nb_pulses_to_send);    
    generate_pulse (ttl_n_in(0), nb_pulses_to_send, 200ns, 200ns);
    ---------------------------------------------------------------------------


    ---------------------------------------------------------------------------
    -- Reading from the I2C Front Panel regs; read only first 2 Channels to speed up testbench
    while not(i = 3) loop
      if i = 0 then
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+4).reg_addr, c_REG_MAP(i+4).reg_name , i2c_master_rcvd_val, nb_pulses_to_send_slv);
      else
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+4).reg_addr, c_REG_MAP(i+4).reg_name, i2c_master_rcvd_val, (others =>'0'));
      end if;
      i := i + 1;
    end loop;
    ---------------------------------------------------------------------------
    -- Reading from the I2C Rear Panel regs; read only first 2 Channels to speed up testbench
    i := 0;
    while not(i = 3) loop
      if i = 0 then
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+10).reg_addr, c_REG_MAP(i+10).reg_name , i2c_master_rcvd_val, (others =>'0'));
      else
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+10).reg_addr, c_REG_MAP(i+10).reg_name, i2c_master_rcvd_val, (others =>'0'));
      end if;
      i := i + 1;
    end loop;
    ---------------------------------------------------------------------------

    ---------------------------------------------------------------------------
    -- Sending 150 more pulses to Front Panel Channel 1, to make the counter roll   
    print_now_s_i("Sending TTL front pulses to Channel 1: ", nb_pulses_to_send);    
    generate_pulse (ttl_n_in(0), nb_pulses_to_send, 200ns, 200ns);
    ---------------------------------------------------------------------------
    -- Reading from the I2C Front Panel regs; read one reg read to speed up testbench
    i := 0;
    while not(i = 2) loop
      if i = 0 then
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+4).reg_addr, c_REG_MAP(i+4).reg_name , i2c_master_rcvd_val, nb_pulses_to_send_slv);
      else
        read_i2c (i2c_m_in, i2c_m_out, "1011110", c_REG_MAP(i+4).reg_addr, c_REG_MAP(i+4).reg_name, i2c_master_rcvd_val, (others =>'0'));
      end if;
      i := i + 1;
    end loop;



  end process p_stim_i2c;



end architecture behav;
--==============================================================================
--  architecture end
--==============================================================================


