--------------------------------------------------------------------------------
-- Company:       ETH Zurich, Institute for Particle Physics
-- Engineer:      Q. Weitzel, P. Vogler
--
-- Create Date:   12.07.2010
-- Design Name:   
-- Module Name:   FTU_top_tb.vhd
-- Project Name:  
-- Target Device:  
-- Tool versions:  
-- Description:   Testbench for top level entity of FACT FTU board 
-- 
-- VHDL Test Bench Created by ISE for module: FTU_top
-- 
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
-- Notes: 
-- This testbench has been automatically generated using types std_logic and
-- std_logic_vector for the ports of the unit under test.  Xilinx recommends
-- that these types always be used for the top-level I/O of a design in order
-- to guarantee that the testbench will bind correctly to the post-implementation 
-- simulation model.
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

library UNISIM;
use UNISIM.VComponents.all;

entity FTU_top_tb is
end FTU_top_tb;

architecture behavior of FTU_top_tb is 

  -- Component Declaration for the Unit Under Test (UUT)
 
  component FTU_top
    port(
      -- global control
      ext_clk   : IN  STD_LOGIC;                      -- external clock from FTU board
      brd_add   : IN  STD_LOGIC_VECTOR(5 downto 0);   -- geographic board/slot address
      --brd_id    : IN  STD_LOGIC_VECTOR(7 downto 0);   -- local solder-programmable board ID
      
      -- rate counters LVDS inputs
      -- use IBUFDS differential input buffer
      patch_A_p     : IN  STD_LOGIC;                  -- logic signal from first trigger patch
      patch_A_n     : IN  STD_LOGIC;
      patch_B_p     : IN  STD_LOGIC;                  -- logic signal from second trigger patch
      patch_B_n     : IN  STD_LOGIC;
      patch_C_p     : IN  STD_LOGIC;                  -- logic signal from third trigger patch
      patch_C_n     : IN  STD_LOGIC;
      patch_D_p     : IN  STD_LOGIC;                  -- logic signal from fourth trigger patch
      patch_D_n     : IN  STD_LOGIC;
      trig_prim_p   : IN  STD_LOGIC;                  -- logic signal from n-out-of-4 circuit
      trig_prim_n   : IN  STD_LOGIC;

      -- DAC interface
      sck           : OUT STD_LOGIC;                  -- serial clock to DAC
      mosi          : OUT STD_LOGIC;                  -- serial data to DAC, master-out-slave-in
      clr           : OUT STD_LOGIC;                  -- clear signal to DAC
      cs_ld         : OUT STD_LOGIC;                  -- chip select or load to DAC

      -- RS-485 interface to FTM
      rx            : IN  STD_LOGIC;                  -- serial data from FTM
      tx            : OUT STD_LOGIC;                  -- serial data to FTM
      rx_en         : OUT STD_LOGIC;                  -- enable RS-485 receiver
      tx_en         : OUT STD_LOGIC;                  -- enable RS-485 transmitter

      -- analog buffer enable
      enables_A   : OUT STD_LOGIC_VECTOR(8 downto 0);  -- individual enables for analog inputs
      enables_B   : OUT STD_LOGIC_VECTOR(8 downto 0);  -- individual enables for analog inputs
      enables_C   : OUT STD_LOGIC_VECTOR(8 downto 0);  -- individual enables for analog inputs
      enables_D   : OUT STD_LOGIC_VECTOR(8 downto 0);  -- individual enables for analog inputs

      -- testpoints
      TP_A       : OUT STD_LOGIC_VECTOR(11 downto 0)   -- testpoints
    );
  end component;
    
  --Inputs
  signal ext_clk     : STD_LOGIC := '0';
  signal brd_add     : STD_LOGIC_VECTOR(5 downto 0) := (others => '0');
  --signal brd_id      : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
  signal patch_A_p   : STD_LOGIC := '0';
  signal patch_A_n   : STD_LOGIC := '0';
  signal patch_B_p   : STD_LOGIC := '0';
  signal patch_B_n   : STD_LOGIC := '0';
  signal patch_C_p   : STD_LOGIC := '0';
  signal patch_C_n   : STD_LOGIC := '0';
  signal patch_D_p   : STD_LOGIC := '0';
  signal patch_D_n   : STD_LOGIC := '0';
  signal trig_prim_p : STD_LOGIC := '0';
  signal trig_prim_n : STD_LOGIC := '0';
  signal rx          : STD_LOGIC := '1';

  --Outputs
  signal enables_A : STD_LOGIC_VECTOR(8 downto 0);
  signal enables_B : STD_LOGIC_VECTOR(8 downto 0);
  signal enables_C : STD_LOGIC_VECTOR(8 downto 0);
  signal enables_D : STD_LOGIC_VECTOR(8 downto 0);
  signal clr       : STD_LOGIC;
  signal cs_ld     : STD_LOGIC;
  signal sck       : STD_LOGIC;
  signal mosi      : STD_LOGIC;
  signal tx        : STD_LOGIC;
  signal rx_en     : STD_LOGIC;
  signal tx_en     : STD_LOGIC;
  signal TP_A      : STD_LOGIC_VECTOR(11 downto 0);

  --single-ended trigger signals
  signal patch_A_sig : STD_LOGIC := '0';
  signal patch_B_sig : STD_LOGIC := '0';
  signal patch_C_sig : STD_LOGIC := '0';
  signal patch_D_sig : STD_LOGIC := '0';
  signal trigger_sig : STD_LOGIC := '0';
  
  -- Clock period definitions
  constant ext_clk_period : TIME := 20 ns;
  constant baud_rate_period : TIME := 4 us;
  
begin
 
  -- Instantiate the Unit Under Test (UUT)
  uut: FTU_top
    port map(
      ext_clk     => ext_clk,
      brd_add     => brd_add,
      --brd_id      => brd_id,
      patch_A_p   => patch_A_p,
      patch_A_n   => patch_A_n,
      patch_B_p   => patch_B_p,
      patch_B_n   => patch_B_n,
      patch_C_p   => patch_C_p,
      patch_C_n   => patch_C_n,
      patch_D_p   => patch_D_p,
      patch_D_n   => patch_D_n,
      trig_prim_p => trig_prim_p,
      trig_prim_n => trig_prim_n,
      rx          => rx,
      rx_en       => rx_en,
      enables_A   => enables_A,
      enables_B   => enables_B,
      enables_C   => enables_C,
      enables_D   => enables_D,
      clr         => clr,
      cs_ld       => cs_ld,
      sck         => sck,
      mosi        => mosi,
      tx          => tx,
      tx_en       => tx_en,
      TP_A        => TP_A
    );

  --differential output buffer for patch A
  OBUFDS_LVDS_33_A : OBUFDS_LVDS_33
    port map(
      O  => patch_A_p,
      OB => patch_A_n,
      I  => patch_A_sig
    );

  OBUFDS_LVDS_33_B : OBUFDS_LVDS_33
    port map(
      O  => patch_B_p,
      OB => patch_B_n,
      I  => patch_B_sig
    );

  OBUFDS_LVDS_33_C : OBUFDS_LVDS_33
    port map(
      O  => patch_C_p,
      OB => patch_C_n,
      I  => patch_C_sig
    );

  OBUFDS_LVDS_33_D : OBUFDS_LVDS_33
    port map(
      O  => patch_D_p,
      OB => patch_D_n,
      I  => patch_D_sig
    );

  OBUFDS_LVDS_33_t : OBUFDS_LVDS_33
    port map(
      O  => trig_prim_p,
      OB => trig_prim_n,
      I  => trigger_sig
    );
  
  -- Stimulus process for clock
  ext_clk_proc: process
  begin
    ext_clk <= '0';
    wait for ext_clk_period/2;
    ext_clk <= '1';
    wait for ext_clk_period/2;
  end process ext_clk_proc;
 
  -- Stimulus process for trigger
  trigger_proc: process
  begin
    ---------------------------------------------------------------------------
    -- FTU not yet initialized
    ---------------------------------------------------------------------------
    wait for 10us;
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 99us;
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 1us;
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    ---------------------------------------------------------------------------
    -- now FTU is initialized
    ---------------------------------------------------------------------------
    wait for 4us; 
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 4us; 
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 22us; 
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 1430us;
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait for 400us;
    trigger_sig <= '1';
    wait for 5ns;
    trigger_sig <= '0';
    wait;    
  end process trigger_proc;

  -- Stimulus process for RS485
  rs485_proc: process
    
    procedure assign_rs485 (data: std_logic_vector(7 downto 0)) is
    begin
      rx <= '0'; --start bit
      wait for baud_rate_period;
      rx <= data(0); --bit 0
      wait for baud_rate_period;
      rx <= data(1); --bit 1
      wait for baud_rate_period;
      rx <= data(2); --bit 2
      wait for baud_rate_period;
      rx <= data(3); --bit 3
      wait for baud_rate_period;
      rx <= data(4); --bit 4
      wait for baud_rate_period;
      rx <= data(5); --bit 5
      wait for baud_rate_period;
      rx <= data(6); --bit 6
      wait for baud_rate_period;
      rx <= data(7); --bit 7
      wait for baud_rate_period;
      rx <= '1'; --stop bit
      wait for baud_rate_period;
      rx <= '1'; --stop bit
      wait for baud_rate_period;
    end assign_rs485;
    
  begin
    ---------------------------------------------------------------------------
    -- wait until FTU is initialized
    ---------------------------------------------------------------------------
    wait for 150us;
    ---------------------------------------------------------------------------
    -- test one RS485 command (28 byte)
    ---------------------------------------------------------------------------
    assign_rs485("01000000"); --start delimiter
    wait for 0us;
    assign_rs485("00000000"); --FTU address
    wait for 0ns;
    assign_rs485("11000000"); --FTM address
    wait for 0ns;
    assign_rs485("00000001"); --FTM firmware ID
    wait for 0ns;
    assign_rs485("00000010"); --instruction
    wait for 0us;
    assign_rs485("00000001"); --data byte 01
    wait for 0ns;
    assign_rs485("00000000"); --data byte 02
    wait for 0ns;
    assign_rs485("00000010"); --data byte 03
    wait for 0ns;
    assign_rs485("00000000"); --data byte 04
    wait for 0ns;
    assign_rs485("00000100"); --data byte 05
    wait for 0ns;
    assign_rs485("00000000"); --data byte 06
    wait for 0ns;
    assign_rs485("00001000"); --data byte 07
    wait for 0ns;
    assign_rs485("00000000"); --data byte 08
    wait for 0ns;
    assign_rs485("00010000"); --data byte 09
    wait for 0ns;
    assign_rs485("00000000"); --data byte 10
    wait for 0ns;
    assign_rs485("00000000"); --data byte 11
    wait for 0ns;
    assign_rs485("00000000"); --data byte 12
    wait for 0ns;
    assign_rs485("00000001"); --data byte 13
    wait for 0ns;
    assign_rs485("00000000"); --data byte 14
    wait for 0ns;
    assign_rs485("00000010"); --data byte 15
    wait for 0ns;
    assign_rs485("00000000"); --data byte 16
    wait for 0ns;
    assign_rs485("00000100"); --data byte 17
    wait for 0ns;
    assign_rs485("00000000"); --data byte 18
    wait for 0ns;
    assign_rs485("00001000"); --data byte 19
    wait for 0ns;
    assign_rs485("00000000"); --data byte 20
    wait for 0ns;
    assign_rs485("00010000"); --data byte 21
    wait for 0ns;
    assign_rs485("00000000"); --CRC error counter (not used)
    wait for 0ns;
    assign_rs485("00000000"); --check sum
    ---------------------------------------------------------------------------
    -- wait enough time and send another command
    ---------------------------------------------------------------------------
    wait for 1500us;
    assign_rs485("01000000"); --start delimiter
    wait for 0us;
    assign_rs485("00000000"); --FTU address
    wait for 0ns;
    assign_rs485("11000000"); --FTM address
    wait for 0ns;
    assign_rs485("00000001"); --FTM firmware ID
    wait for 0ns;
    assign_rs485("00000101"); --instruction
    wait for 0us;
    assign_rs485("00000001"); --data byte 01
    wait for 0ns;
    assign_rs485("00000000"); --data byte 02
    wait for 0ns;
    assign_rs485("00000010"); --data byte 03
    wait for 0ns;
    assign_rs485("00000000"); --data byte 04
    wait for 0ns;
    assign_rs485("00000100"); --data byte 05
    wait for 0ns;
    assign_rs485("00000000"); --data byte 06
    wait for 0ns;
    assign_rs485("00001000"); --data byte 07
    wait for 0ns;
    assign_rs485("00000000"); --data byte 08
    wait for 0ns;
    assign_rs485("00010000"); --data byte 09
    wait for 0ns;
    assign_rs485("00000000"); --data byte 10
    wait for 0ns;
    assign_rs485("00000000"); --data byte 11
    wait for 0ns;
    assign_rs485("00000000"); --data byte 12
    wait for 0ns;
    assign_rs485("00000001"); --data byte 13
    wait for 0ns;
    assign_rs485("00000000"); --data byte 14
    wait for 0ns;
    assign_rs485("00000010"); --data byte 15
    wait for 0ns;
    assign_rs485("00000000"); --data byte 16
    wait for 0ns;
    assign_rs485("00000100"); --data byte 17
    wait for 0ns;
    assign_rs485("00000000"); --data byte 18
    wait for 0ns;
    assign_rs485("00001000"); --data byte 19
    wait for 0ns;
    assign_rs485("00000000"); --data byte 20
    wait for 0ns;
    assign_rs485("00010000"); --data byte 21
    wait for 0ns;
    assign_rs485("00000000"); --CRC error counter (not used)
    wait for 0ns;
    assign_rs485("00000000"); --check sum
    ---------------------------------------------------------------------------
    -- don't forget final wait!
    ---------------------------------------------------------------------------
    wait;
    
  end process rs485_proc;

end;
