--==============================================================================
-- CERN (BE-CO-HT)
-- TTL-RS485 pkg package
--==============================================================================

--==============================================================================
-- 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.wishbone_pkg.all;
use work.genram_pkg.all;
use work.print_pkg.all;


package testbench_pkg is

  --============================================================================
  -- Constant declarations
  --============================================================================
  
  -- Clock periods
  constant C_CLK_20_PER   : time := 50 ns;
  constant C_CLK_125_PER  : time :=  8 ns;
  constant C_RESET_WIDTH  : time := 200 ns;

  
  -- DUT configuration (generics):
  constant C_NR_CHANS     : integer := 6;
  constant C_NR_INV_CHANS : integer := 4;
  constant C_BOARD_ID     : std_logic_vector(31 downto 0) := x"54343835";

   -- Number of I2C masters and slaves for the I2C bus model
  constant C_NR_MASTERS : positive := 1;
  constant C_NR_SLAVES  : positive := 1;

  constant C_RTM               : std_logic_vector(5 downto 0) := "010101";
  constant C_INITIAL_TST_VALUE : std_logic_vector(31 downto 0) := x"FFFFFFF0";

  constant C_I2C_MASTER_SLV_ADDR: std_logic_vector(6 downto 0) := "1011110";


  -- DUT register map:
  constant C_BIDR      : std_logic_vector(31 downto 0) := x"00000000";

  constant C_SR       : std_logic_vector(31 downto 0) := x"00000004";
  constant C_ERR      : std_logic_vector(31 downto 0) := x"00000008";
  constant C_CR       : std_logic_vector(31 downto 0) := x"0000000C";

  constant C_CH1FPPCR : std_logic_vector(31 downto 0) := x"00000010";
  constant C_CH2FPPCR : std_logic_vector(31 downto 0) := x"00000014";
  constant C_CH3FPPCR : std_logic_vector(31 downto 0) := x"00000018";
  constant C_CH4FPPCR : std_logic_vector(31 downto 0) := x"0000001C";
  constant C_CH5FPPCR : std_logic_vector(31 downto 0) := x"00000020";
  constant C_CH6FPPCR : std_logic_vector(31 downto 0) := x"00000024";

  constant C_CH1RPPCR : std_logic_vector(31 downto 0) := x"00000028";
  constant C_CH2RPPCR : std_logic_vector(31 downto 0) := x"0000002C";
  constant C_CH3RPPCR : std_logic_vector(31 downto 0) := x"00000030";
  constant C_CH4RPPCR : std_logic_vector(31 downto 0) := x"00000034";
  constant C_CH5RPPCR : std_logic_vector(31 downto 0) := x"00000038";
  constant C_CH6RPPCR : std_logic_vector(31 downto 0) := x"0000003C";

  constant CH1LTSCYR : std_logic_vector(31 downto 0) := x"0000005C";
  constant CH1LTSTLR : std_logic_vector(31 downto 0) := x"00000060";


  type t_reg is
  record
    reg_addr : std_logic_vector(31 downto 0);
    reg_name : string(1 to 8);
  end record;

  type t_reg_table is array (natural range <>) of t_reg;

  constant c_REG_MAP : t_reg_table(15 downto 0) :=
    (0 => (reg_addr => C_BIDR,
           reg_name => "BIDR    "),
     1 => (reg_addr => C_SR,
           reg_name => "SR      "),
     2 => (reg_addr => C_ERR,
           reg_name => "ERR     "),
     3 => (reg_addr => C_CR,
           reg_name => "CR      "),
     4 => (reg_addr => C_CH1FPPCR,
           reg_name => "CH1FPPCR"),
     5 => (reg_addr => C_CH2FPPCR,
           reg_name => "CH3FPPCR"),
     6 => (reg_addr => C_CH3FPPCR,
           reg_name => "CH3FPPCR"),
     7 => (reg_addr => C_CH4FPPCR,
           reg_name => "CH4FPPCR"),
     8 => (reg_addr => C_CH5FPPCR,
           reg_name => "CH5FPPCR"),
     9 => (reg_addr => C_CH6FPPCR,
           reg_name => "CH6FPPCR"),
     10 => (reg_addr => C_CH1RPPCR,
           reg_name => "CH1RPPCR"),
     11 => (reg_addr => C_CH2RPPCR,
           reg_name => "CH3RPPCR"),
     12 => (reg_addr => C_CH3RPPCR,
           reg_name => "CH3RPPCR"),
     13 => (reg_addr => C_CH4RPPCR,
           reg_name => "CH4RPPCR"),
     14 => (reg_addr => C_CH5RPPCR,
           reg_name => "CH5RPPCR"),
     15 => (reg_addr => C_CH6RPPCR,
           reg_name => "CH6RPPCR"));


  --============================================================================
  -- Types 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;

  --============================================================================
  -- Components declarations
  --============================================================================
  component conv_ttl_rs485 is
  generic (g_simul         : boolean := FALSE); 
  port
  (
    -- Clocks
    clk_20_i                : in  std_logic;
    clk_125_p_i             : in  std_logic;
    clk_125_n_i             : in  std_logic;

    -- I2C interface
    scl_i                   : in  std_logic;
    scl_o                   : out std_logic;
    scl_en_o                : out std_logic;
    sda_i                   : in  std_logic;
    sda_o                   : out std_logic;
    sda_en_o                : out std_logic;

    -- VME interface
    vme_sysreset_n_i        : in  std_logic;
    vme_ga_i                : in  std_logic_vector(4 downto 0);
    vme_gap_i               : in  std_logic;

    -- PCB version recognition
    pcbrev_i				: in std_logic_vector(5 downto 0);

    -- Channel enable
    global_oen_o            : out std_logic;
    ttl_oen_o               : out std_logic;
    inv_oen_o               : out std_logic;
    rs485_oen_o             : out std_logic;

    -- Front panel channels
    ttl_n_i                 : in  std_logic_vector(5 downto 0);
    ttl_o                   : out std_logic_vector(5 downto 0);
    inv_n_i                 : in  std_logic_vector(3 downto 0);
    inv_o                   : out std_logic_vector(3 downto 0);

    -- Rear panel channels
    rs485_n_i               : in  std_logic_vector(5 downto 0);
    rs485_fs_n_i            : in  std_logic_vector(5 downto 0);
    rs485_o                 : out std_logic_vector(5 downto 0);

    -- Rear input and output termination lines
    iterm_en_o              : out std_logic_vector(5 downto 0);
    oterm_en_o              : out std_logic_vector(5 downto 0);

    -- Channel leds
    led_front_n_o           : out std_logic_vector(5 downto 0);
    led_front_inv_n_o       : out std_logic_vector(3 downto 0);
    led_rear_n_o            : out std_logic_vector(5 downto 0);

    -- SPI interface to on-board flash chip
    flash_cs_n_o            : out std_logic;
    flash_sclk_o            : out std_logic;
    flash_mosi_o            : out std_logic;
    flash_miso_i            : in  std_logic;

    -- PLL DACs
    -- 20 MHz VCXO control
    dac20_din_o             : out std_logic;
    dac20_sclk_o            : out std_logic;
    dac20_sync_n_o          : out std_logic;
    -- 125 MHz clock generator control
    dac125_din_o            : out std_logic;
    dac125_sclk_o           : out std_logic;
    dac125_sync_n_o         : out std_logic;

    -- SFP lines
    sfp_los_i               : in    std_logic;
    sfp_present_i           : in    std_logic;
    sfp_rate_select_o       : out   std_logic;
    sfp_scl_b               : inout std_logic;
    sfp_sda_b               : inout std_logic;
    sfp_tx_disable_o        : out   std_logic;
    sfp_tx_fault_i          : in    std_logic;

    -- Thermometer data port
    thermometer_b           : inout std_logic;

    -- Switches
    sw_gp_n_i               : in   std_logic_vector(7 downto 0);
    sw_multicast_n_i        : in   std_logic_vector(3 downto 0);

    -- RTM lines
    rtmm_i                  : in  std_logic_vector(2 downto 0);
    rtmp_i                  : in  std_logic_vector(2 downto 0);

    -- Front panel bicolor LEDs
    led_ctrl0_o             : out std_logic;
    led_ctrl0_oen_o         : out std_logic;
    led_ctrl1_o             : out std_logic;
    led_ctrl1_oen_o         : out std_logic;
    led_gp_2_4_o            : out std_logic;
    led_gp_1_3_o            : out std_logic;
    led_oterm_wr_o          : out std_logic;
    led_iterm_syserror_o    : out std_logic;
    led_gf_syspw_o          : out std_logic;
    led_ttl_i2c_o           : out std_logic
  );
end component conv_ttl_rs485;

  -- procedure read_i2c (signal i2c_m_out  : in  t_i2c_master_out;
                      -- signal i2c_m_in   : out t_i2c_master_in;
                      -- signal slv_addr : in  std_logic_vector( 6 downto 0);
                      -- signal reg_addr : in  std_logic_vector(31 downto 0);
                      -- signal rcvd_val   : out std_logic_vector(31 downto 0));


  procedure generate_pulse (signal pulse_n_out : out std_logic;
                            nb_of_pulses       : in natural;
                            fp_or_rp           : in string (1 to 2);
                            signal rs485_fs_n  : out std_logic;
                            ns_on              : in time;
                            ns_off             : in time);


  procedure settings_config (constant glitch_filter_en : in std_logic;
                             constant ttl_out_bar_en   : in std_logic;
                             signal sw_gp_n  : out std_logic_vector(7 downto 0);
                             signal sw_other : out std_logic_vector(31 downto 0);
                             signal pcbrev   : out std_logic_vector(5 downto 0);
                             signal rtm      : out std_logic_vector(5 downto 0));

  procedure cnt_pulses (signal pulse_train : in std_logic;
                       signal nb_of_pulses_cnted  :  out natural;
                       nb_pulses_expected  : in integer);


  procedure write_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);
                       constant send_val : in  std_logic_vector(31 downto 0);
                                err      : out natural);

  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);
                      constant eval_result : in boolean;
                               err         : out natural);

  -- procedure read_i2c_err (signal i2c_m_in  : out t_i2c_master_in;
                          -- signal i2c_m_out : in  t_i2c_master_out);

end testbench_pkg;
package body testbench_pkg is 
--==================================================================================================
--  Procedures
--==================================================================================================
  procedure settings_config (constant glitch_filter_en : in std_logic;
                             constant ttl_out_bar_en   : in std_logic;
                             signal sw_gp_n  : out std_logic_vector(7 downto 0);
                             signal sw_other : out std_logic_vector(31 downto 0);
                             signal pcbrev   : out std_logic_vector(5 downto 0);
                             signal rtm      : out std_logic_vector(5 downto 0)) is
  begin
    sw_gp_n(0)          <= not glitch_filter_en;
    if glitch_filter_en = '1' then
      print_now("Glitch filter enabled");
    else
      print_now("Glitch filter disabled");
    end if;

    sw_gp_n(7)          <= ttl_out_bar_en;
    if ttl_out_bar_en = '1' then
      print_now("TTL output BAR enabled");
    else
      print_now("TTL output BAR disabled");
    end if;

    --print_now_s_std("RTMP & RTMM set to x", C_RTM);

    sw_gp_n(6 downto 1) <= (others => '0'); -- not used
    sw_other            <= (others => '0'); -- not used
    pcbrev              <= "111100";        -- not used
    rtm                 <= C_RTM;           -- not used
  end procedure;

----------------------------------------------------------------------------------------------------
  procedure generate_pulse (signal pulse_n_out  : out std_logic;
                            nb_of_pulses : in natural;
                            fp_or_rp     : in string (1 to 2);
                            signal rs485_fs_n   : out std_logic;
                            ns_on        : in time;
                            ns_off       : in time) is

    variable numb_cnt : natural;
    begin
      numb_cnt := 0;

      while not(numb_cnt = nb_of_pulses) loop
        if fp_or_rp = "fp" then
          rs485_fs_n <= '0'; -- stays inactive

          pulse_n_out <= '1';
          wait for ns_off;
          pulse_n_out <= '0';
          wait for ns_on; 
          pulse_n_out <= '1';
        else
          pulse_n_out <= '1';
          rs485_fs_n <= '0';
          wait for ns_off;
          pulse_n_out <= '0';
          rs485_fs_n <= '1';
          wait for ns_on; 
          pulse_n_out <= '1';
          rs485_fs_n <= '0';
        end if;
        numb_cnt:= numb_cnt + 1;
      end loop;
      wait for 500ns; -- wait for all pulses to propagate to the output
  end procedure generate_pulse;


----------------------------------------------------------------------------------------------------
  procedure cnt_pulses (signal pulse_train        : in std_logic;
                        signal nb_of_pulses_cnted : out integer;
                               nb_pulses_expected : in integer) is

  variable pulse_cnt : integer :=0;
  variable err_sum   : integer:=0;
  begin
    if rising_edge(pulse_train) then
      pulse_cnt := pulse_cnt +1;
    end if;

    nb_of_pulses_cnted <= pulse_cnt;

    if pulse_cnt = nb_pulses_expected then
      print_now_s_i("[OK] counted: ", pulse_cnt);
    else
      print_now_s_i("[ERR] counted: ", pulse_cnt);
    end if;

  end procedure cnt_pulses;

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

  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);
                      constant eval_result : in boolean;
                               err      : out natural) is
  variable err_cnt : natural := 0;
  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 <= C_I2C_MASTER_SLV_ADDR;
    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 eval_result = TRUE then
      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 from " & reg_name & ": ", i2c_m_out.i2c_master_rcvd_val,
                              "; expected value: ",exp_val);
        err_cnt := err_cnt +1;
      end if;
    end if; 

    err := err_cnt;
    print_now("---------------");
  end procedure read_i2c;

----------------------------------------------------------------------------------------------------
  procedure write_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);
                       constant send_val : in  std_logic_vector(31 downto 0);
                                err      : out natural) is
  variable err_cnt : natural := 0;
  begin
    print_now("---------------");
    print_now("write_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 <= C_I2C_MASTER_SLV_ADDR;
    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_send_val <= send_val;
    i2c_m_in.i2c_master_start    <= '1';
    i2c_m_in.i2c_master_rdwr     <= '0'; --0: write
    wait for C_CLK_20_PER;
    i2c_m_in.i2c_master_start    <= '0';
    wait until i2c_m_out.i2c_master_ready = '1';
    print_now_s_std_s("write_i2C: Value ", send_val, " written to reg " & reg_name);
    print_now("---------------");
    err := 0;

  end procedure write_i2c;


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



end;


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

