-- **************************************************
--
--   Entity: GMTBoard_tb
--
--   Description: Testbench for GMT Board
-- 
--   The entire GMT board is simulated. Test data are read from file and fed
--   into the Input FPGAs and MIP/ISO Assingment units. Results at the output
--   of the sort chip are compared to the results from the test file.
--  
--   In parallel, the readout system is tested. Three L1 accepts are generated,
--   the output on the channel link is saved to file chlink1.out (in the
--   simulation directory)
--   
--
--   $Date: 2005/01/20 18:24:12 $
--   $Revision: 1.5 $
--
--   Author :
--   H. Sakulin                CERN EP 
--
-- **************************************************

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use WORK.GMTTypes.all;
use work.GMTHWTestFile.all;
use STD.TEXTIO.all;
--use work.GMTBoard_cfg.all;             -- switch beh/time simu
use work.ieee_to_string.all;

entity GMTBoard_tb is
end;

architecture Bench of GMTBoard_tb is

  -- purpose: calculate xor of all bits in d
  function parity (
    constant d : std_logic_vector)
    return std_logic is
    variable result  : std_logic;
  begin  -- function parity
    result := '0';
    for i in d'RANGE loop
      result := result xor d(i);
    end loop;  -- i
    return result;
  end function parity;

  -----------------------------------------------------------------------------
  -- LATENCIES (in half cycles)
  -----------------------------------------------------------------------------
  constant LAT_TOTAL              : integer := 19;
  
  -- LAT_TOTAL_CYCLES is the the number of delays of the input event
  -- there is no delay if the actual latency of an output was 0 or 1
  -- there is a 1bx delay if the latency of an output was 2 or 3
  constant LAT_TOTAL_CYCLES       : integer := LAT_TOTAL / 2;
  
  -- input latencies give the number of half-cycles from setting the earliest
  -- signals to setting the respective delayed signals
  constant LAT_IN_MIPISO          : integer := 4;  

  -- output latencies give the number of half-cycles from setting the earliest
  -- inputs to reading the respective output. (actually the time until the
  -- outputs are read is even longer as signals are checked just before the
  -- falling edge of a main cycle).
  --
  -- i.e. latency for OUT_CANCELDTCSC is same as for IN_CANCELDTCSC to other chip
  --
  -- With an input and an outpt FF in
  -- the chip, the minimum output latency of any signal is 2.
  constant LAT_OUT_DTCSCISMATCHED : integer := 5;--4;x
  constant LAT_OUT_CANCELDTCSC    : integer := 7;--7;

  constant LAT_OUT_PM_DEBUG   : integer := 6;
  constant LAT_OUT_COU_DEBUG  : integer := 8;
  -----------------------------------------------------------------------------
  constant t_period               : time    := 25 ns;
  constant t_halfperiod           : time    := t_period / 2;
  constant t_inbefore             : time    := 7 ns;
  constant t_afterck              : time    := t_halfperiod - t_inbefore;
  -----------------------------------------------------------------------------

  -- OUTPUTS from chip
--   signal sOutMuons_flat       : TFourGMTMu_flat;
--   signal sOutMuons            : TGMTMu_vector (0 to 3);
--   signal sSortRanks           : TSortRank_vector(0 to 3);
--   signal sIdxBits             : TIndexBits_vector(0 to 3);
--   signal sDTCSCisMatched      : std_logic_vector (0 to 3);
--   signal sCancelOtherDTCSC    : std_logic_vector (0 to 3);
--   signal sPairMatrix          : TPairMatrix;
--   signal sPairMatrix1         : TPairMatrix;
--   signal sPairMatrix2         : TPairMatrix;
--   signal sMQMatrix            : TMQMatrix;
--   signal sCancelBits1   : std_logic_vector(0 to 7);
--   signal sCancelBits2   : std_logic_vector(0 to 7);

--   -- OUTPUTS delayed
--   signal sDTCSCisMatched_d      : std_logic_vector (0 to 3);
--   signal sCancelOtherDTCSC_d    : std_logic_vector (0 to 3);
--   signal sPairMatrix_d          : TPairMatrix;
--   signal sPairMatrix1_d         : TPairMatrix;
--   signal sPairMatrix2_d         : TPairMatrix;
-- --  signal sMQMatrix_d            : TMQMatrix;
--   signal sCancelBits1_d   : std_logic_vector(0 to 7);
--   signal sCancelBits2_d   : std_logic_vector(0 to 7);

  -- Clock, control, VME
  signal ck : std_logic := '0';         -- the clock
  signal ck_inputs : std_logic := '0';  -- shifted clock for inputs

  signal vme_addr_board_i   : std_logic_vector (23 downto 1);
  signal vme_data_i   : std_logic_vector (15 downto 0);

  signal dcm_locked_i : std_logic;
  signal reset_dcm_i        : std_logic;
  signal inactive_i   : std_logic;
  signal reset_i      : std_logic;



  -- Input muons
  signal sDTMu             : TInMuons_vec(0 to 3);
  signal sCSCMu            : TInMuons_vec(0 to 3);
  signal sbRPCMu           : TInMuons_vec(0 to 3);
  signal sfRPCMu           : TInMuons_vec(0 to 3);

  -- Flat version
  signal sDTMu_flat        : TFourInMuons_flat;
  signal sCSCMu_flat       : TFourInMuons_flat;
  signal sbRPCMu_flat      : TFourInMuons_flat;
  signal sfRPCMu_flat      : TFourInMuons_flat;

  -- Input MQ bits
  signal sfMQbits          : TCaloRing_vector (0 to 9);
  signal sbMQbits          : TCaloRing_vector (0 to 9);

  -- flat version
  signal sfMQbits_flat     : std_logic_vector(0 to 10*18-1);
  signal sbMQbits_flat     : std_logic_vector(0 to 10*18-1);
  -- flat delayed version
  signal sfMQbits_flat_d   : std_logic_vector(0 to 10*18-1);
  signal sbMQbits_flat_d   : std_logic_vector(0 to 10*18-1);

  -- Output muons
  signal sMuon12           : TGMTMu_flat;
  signal sMuon34           : TGMTMu_flat;
  
  signal oPairMatrix_LFF_i  : TPairMatrix;
  signal oPairMatrix1_LFF_i : TPairMatrix;
  signal oPairMatrix2_LFF_i : TPairMatrix;
  signal oMQMatrix_LFF_i    : TMQMatrix;
  signal oCancelBits1_LFF_i : std_logic_vector(0 to 7);
  signal oCancelBits2_LFF_i : std_logic_vector(0 to 7);
  signal oPairMatrix_LFB_i  : TPairMatrix;
  signal oPairMatrix1_LFB_i : TPairMatrix;
  signal oPairMatrix2_LFB_i : TPairMatrix;
  signal oCancelBits1_LFB_i : std_logic_vector(0 to 7);
  signal oCancelBits2_LFB_i : std_logic_vector(0 to 7);
  signal oMQMatrix_LFB_i    : TMQMatrix;
  signal oPhiSelBits_AUF_i  : TPhiSelBits_vec (0 to 15);
  signal oEtaSelBits_AUF_i  : TEtaSelBits_vec (0 to 15);
  signal oPhiSelBits_AUB_i  : TPhiSelBits_vec (0 to 15);
  signal oEtaSelBits_AUB_i  : TEtaSelBits_vec (0 to 15);

  signal DTACK_EXT_i        : std_logic;
  signal SINGLE_ACCESS_i    : std_logic;
  signal BLT_ACCESS_i       : std_logic;
  signal DSPULS_i           : std_logic;
  signal DSCYC_i            : std_logic;
  signal DSSYNC_i           : std_logic;
  signal WRITE_I_i          : std_logic := '0';
  signal vme_en_i           : std_logic := '0';
  signal vme_en_i_d         : std_logic := '0';

  signal ch_link1_i         : std_logic_vector(27 downto 0);
  signal ch_link1_clk_i     : std_logic;
  signal en_link1_i         : std_logic;
  
  signal l1a_i              : std_logic := '0';
  signal bcreset_i          : std_logic := '0';
  signal l1reset_i          : std_logic := '0';
  
  signal ro_bus_i           : std_logic_vector(11 downto 0);
  signal ro_strobe_i        : std_logic_vector(2 downto 0);
  signal ro_rdrqst_i        : std_logic;

begin

  -------------------------------------------------------------------------------
  -- instantiate the chip
  -------------------------------------------------------------------------------  
  sDTMu_flat     <= InMuons_vec_to_flat (sDTMu);
  sCSCMu_flat    <= InMuons_vec_to_flat (sCSCMu);
  sbRPCMu_flat   <= InMuons_vec_to_flat (sbRPCMu);
  sfRPCMu_flat   <= InMuons_vec_to_flat (sfRPCMu);

  sfMQbits_flat   <= CaloRing_vec_to_flat(sfMQbits);
  sbMQbits_flat   <= CaloRing_vec_to_flat(sbMQbits);

  theBoard: entity work.GMTBoard
    port map (
      DTMu             => sDTMu_flat,
      CSCMu            => sCSCMu_flat,
      bRPCMu           => sbRPCMu_flat,
      fRPCMu           => sfRPCMu_flat,
      
      fMQbits          => sfMQbits_flat_d,
      bMQbits          => sbMQbits_flat_d,
      
      Muon12           => sMuon12,
      Muon34           => sMuon34,
      
      oPairMatrix_LFF  => oPairMatrix_LFF_i,
      oPairMatrix1_LFF => oPairMatrix1_LFF_i,
      oPairMatrix2_LFF => oPairMatrix2_LFF_i,
      oMQMatrix_LFF    => oMQMatrix_LFF_i,
      oCancelBits1_LFF => oCancelBits1_LFF_i,
      oCancelBits2_LFF => oCancelBits2_LFF_i,
      oPairMatrix_LFB  => oPairMatrix_LFB_i,
      oPairMatrix1_LFB => oPairMatrix1_LFB_i,
      oPairMatrix2_LFB => oPairMatrix2_LFB_i,
      oCancelBits1_LFB => oCancelBits1_LFB_i,
      oCancelBits2_LFB => oCancelBits2_LFB_i,
      oMQMatrix_LFB    => oMQMatrix_LFB_i,
      oPhiSelBits_AUF  => oPhiSelBits_AUF_i,
      oEtaSelBits_AUF  => oEtaSelBits_AUF_i,
      oPhiSelBits_AUB  => oPhiSelBits_AUB_i,
      oEtaSelBits_AUB  => oEtaSelBits_AUB_i,
      
      vme_addr_board   => vme_addr_board_i,
      vme_data         => vme_data_i,
      
      BERR_EXT         => open,
      DTACK_EXT        => DTACK_EXT_i,
      SINGLE_ACCESS    => SINGLE_ACCESS_i,
      BLT_ACCESS       => BLT_ACCESS_i,
      DSPULS           => DSPULS_i,
      DSCYC            => DSCYC_i,
      DSSYNC           => DSSYNC_i,
      WRITE_I          => WRITE_I_i,

      VME_LED          => open,
      error_LED        => open,
      SET_RUNNING      => open,

      stat_GMT         => open,
      ch_link1         => ch_link1_i,
      ch_link1_clk     => ch_link1_clk_i,
      en_link1         => en_link1_i,
      ch_link2         => open,
      ch_link2_clk     => open,
      en_link2         => open,

      l1a              => l1a_i,
      bcreset          => bcreset_i,
      l1reset          => l1reset_i,
      
      ro_bus           => ro_bus_i,
      ro_strobe        => ro_strobe_i,
      ro_rdrqst        => ro_rdrqst_i,
      test_pins        => open,


      clk              => ck,
      reset            => reset_i,
      dcm_locked_LED   => dcm_locked_i,
      reset_dcm        => reset_dcm_i,
      inactive         => inactive_i);
  
  delay_b_MIP_ISO: entity work.DelayLine
    generic map ( n_halfcycles => LAT_IN_MIPISO)
    port map (sbMQbits_flat, sbMQbits_flat_d, ck_inputs);
  delay_f_MIP_ISO: entity work.DelayLine
    generic map ( n_halfcycles => LAT_IN_MIPISO)
    port map (sfMQbits_flat, sfMQbits_flat_d, ck_inputs);

  
   inactive_i <= '0';

 ------------------------------------------------------------------------------
 -- testbench process
 -------------------------------------------------------------------------------

   runtb                              : process
     file F                           : text open read_mode is "gmt_testfile.dat";
                                         -- synchronized muons from input FPGA
     variable L, LO                   : line;  -- input file line

     variable event : TGMTEvent;
     -- in the event pipe the events are delayed until the can be compated with
     -- the chip output
     variable event_pipe : TGMTEvent_vec(0 to LAT_TOTAL_CYCLES-1);
     
     variable vDTMu             : TInMuons_vec(0 to 3);
     variable vCSCMu            : TInMuons_vec(0 to 3);
     variable vbRPCMu           : TInMuons_vec(0 to 3);
     variable vfRPCMu           : TInMuons_vec(0 to 3);

     variable vOutMuons : TGMTMu_vector(0 to 3);
     variable vMuon12 : TGMTMu;
     variable vMuon34 : TGMTMu;
--     variable vSortRanks : TSortRank_vector(0 to 3);

     variable bMatch   : boolean;
     variable bTestOk  : boolean;
     variable ntestfailed : integer := 0;
     variable ievent   : integer;

     variable zz : TFourSyncedMu_flat;
   begin  -- process runtb
     ---------------------------------------------------------------------------
     -- reset variables
     ---------------------------------------------------------------------------
     ck <= '0';
     ck_inputs <= '0';
     for i in 0 to 3 loop
       sDTMu(i) <= ( others => '0');
       sCSCMu(i) <= ( others => '0');
       sbRPCMu(i) <= ( others => '0');
       sfRPCMu(i) <= ( others => '0');
     end loop;  -- i
     reset_i <= '0';
     reset_dcm_i <= '0';
     l1reset_i <= '0';
     bcreset_i <= '0';
     ---------------------------------------------------------------------------
     -- wait
     ---------------------------------------------------------------------------
     wait for 100 ns;
     ---------------------------------------------------------------------------
     -- start clock & apply reset for 5 cycles
     ---------------------------------------------------------------------------
     reset_dcm_i <= '1';
     for i in 1 to 5 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;  
     reset_dcm_i <= '0';
     ---------------------------------------------------------------------------
     -- run a few cycles to allow DCM to lock
     ---------------------------------------------------------------------------
     for i in 1 to 100 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;
     assert dcm_locked_i = '1' report "DCM did not lock !" severity failure;

     reset_i <= '1';
     for i in 1 to 5 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;  
     reset_i <= '0';
     for i in 1 to 5 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;  
     l1reset_i <= '1';
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     l1reset_i <= '0';
     for i in 1 to 5 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;  
     bcreset_i <= '1';
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     bcreset_i <= '0';
     for i in 1 to 2 loop 
       wait for t_halfperiod;
       ck <= '1';
       wait for t_halfperiod;
       ck <= '0';
     end loop;  
     -------------------------------------------------------------------------------
     -- loop over events
     -------------------------------------------------------------------------------
     bTestOk := true;        
     write (LO, string'("******************* start of tests  ********************** "));
     writeline (OUTPUT, LO);
     write (LO, string'("******************* start of tests  ********************** "));
     writeline (OUTPUT, LO);
     write (LO, string'("******************* start of tests  ********************** "));
     writeline (OUTPUT, LO);

     ievent := 0;

     while not endfile(F) loop
       --------------------------------------------------------------------------
       -- read an event
       --------------------------------------------------------------------------
       ievent := ievent+1;
       write (LO, string'("++++++++++++++++++++ reading event nr "));
       write (LO, ievent);
       write (LO, string'(" ++++++++++++++++++++"));
       writeline (OUTPUT, LO);        
       ReadEvent(F, event);
       DumpEvent( event );

       -- convert input muons
       for i in 0 to 3 loop
         vDTMu(i)   := "000000" & parity(SyncedMu_to_flat (event.DTMuons(i)))   & SyncedMu_to_flat (event.DTMuons(i));
         vCSCMu(i)  := "000000" & parity(SyncedMu_to_flat (event.CSCMuons(i)))  & SyncedMu_to_flat (event.CSCMuons(i));
         vbRPCMu(i) := "000000" & parity(SyncedMu_to_flat (event.bRPCMuons(i))) & SyncedMu_to_flat (event.bRPCMuons(i));
         vfRPCMu(i) := "000000" & parity(SyncedMu_to_flat (event.fRPCMuons(i))) & SyncedMu_to_flat (event.fRPCMuons(i));
       end loop;  -- i


      -------------------------------------------------------------------------
      -- set signals on rising edge (unless they are delayed, later)
      -------------------------------------------------------------------------
      wait for t_afterck;
      -- input clock which is phase shifted to occur t_inbefore before the
      -- main clock 
      -- all the input signals are set after the edge of the input clock
      -- the next clock egde occurs 1/2 bx later which will be the next
      -- time when data in the input delay pipelines are shifted


      ck_inputs <= '1';
      sDTMu   <= vDTMu;
      sCSCMu  <= vCSCMu;   
      sbRPCMu <= vbRPCMu;  
      sfRPCMu <= vfRPCMu;  

      sbMQbits <= event.MIPbits( 2 to 11 );  
      sfMQbits <= event.MIPbits( 0 to 4 ) & event.MIPbits( 9 to 13);


      -- read signals to be read after falling  edge
      vOutMuons(1) := GMTMu_from_flat(sMuon12);
      vOutMuons(3) := GMTMu_from_flat(sMuon34);
       
      wait for t_inbefore;
      ck <= '1';
      -------------------------------------------------------------------------
      -- set signals on falling edge
      -------------------------------------------------------------------------
      wait for t_afterck;

      ck_inputs <= '0';
      sbMQbits <= event.Quietbits( 2 to 11 );
      sfMQbits <= event.Quietbits( 0 to 4 ) & event.Quietbits ( 9 to 13);

      -- read signals to be read after rising edge
      vOutMuons(0) := vMuon12;
      vOutMuons(2) := vMuon34;
       
      vMuon12 := GMTMu_from_flat(sMuon12); -- save for next cycle
      vMuon34 := GMTMu_from_flat(sMuon34); -- save for next cycle
       
       
      wait for t_inbefore;

      -------------------------------------------------------------------------
      -- read output and check if it matches the ORCA output
      -------------------------------------------------------------------------
       if ievent > LAT_TOTAL_CYCLES then
         write (LO, string'("-------------------- checking event nr "));
         write (LO, ievent-LAT_TOTAL_CYCLES);
         write (LO, string'(" --------------------"));
         writeline (OUTPUT, LO);        

         ----------------------------------------------------------------------
         -- check GMT muons
         ----------------------------------------------------------------------         

         bMatch       := true;
         for i in 0 to 3 loop
           if ( (vOutMuons(i).sysign /= event_pipe(0).GMTMuons(i).sysign) or
                (vOutMuons(i).mip    /= event_pipe(0).GMTMuons(i).mip   ) or
                (vOutMuons(i).isol   /= event_pipe(0).GMTMuons(i).isol  ) or
                (vOutMuons(i).eta    /= event_pipe(0).GMTMuons(i).eta   ) or
                (vOutMuons(i).phi    /= event_pipe(0).GMTMuons(i).phi   ) or
                (vOutMuons(i).pt     /= event_pipe(0).GMTMuons(i).pt    ) ) then
             bMatch := false;
           end if;
           -- do some basic quality check
           if TO_INTEGER(UNSIGNED(event_pipe(0).GMTMuons(i).qual)) /= TO_INTEGER(UNSIGNED(vOutMuons(i).qual)) then 
             bMatch := false;
           end if;      

         end loop;  -- i
         
         if bMatch = false then
           write (LO, string'("******* disagreement in out muons ******** "));
           writeline (OUTPUT, LO);
           bTestOk    := false;
           ntestfailed := ntestfailed + 1;
         end if;
         write (LO, string'("Simulated Out muons : "));
         writeline (OUTPUT, LO);
         for i in 0 to 3 loop
           write (LO, vOutMuons(i));
           writeline (OUTPUT, LO);                 
         end loop;  -- i
         
         write (LO, string'("from ORCA Out muons : "));
         writeline (OUTPUT, LO);
         for i in 0 to 3 loop
           write (LO, event_pipe(0).GMTMuons(i));
           writeline (OUTPUT, LO);                 
         end loop;  -- i

       else         
         write (LO, string'("-------------------- skipping checks for  event nr "));
         write (LO, ievent-2);
         write (LO, string'(" --------------------"));
         writeline (OUTPUT, LO);        
       end if;

       -------------------------------------------------------------------------
       -- end of cycle
       -------------------------------------------------------------------------      
       ck <= '0';
       -- delay GMT event for checks
       for i  in event_pipe'low to event_pipe'high-1 loop
         event_pipe(i) := event_pipe(i+1);
       end loop;  -- i
       event_pipe(event_pipe'high) := event;
    end loop;

    for i in 1 to 700 loop
      wait for t_afterck;
      ck_inputs <= '1';
      wait for t_inbefore;
      ck <= '1';
      wait for t_afterck;
      ck_inputs <= '0';
      wait for t_inbefore;
      ck <= '0';
    end loop;  

    if bTestOk then
      write(LO,string'("**** all tests succeeded."));
    else
      write(LO,string'("**** some tests failed. ("));
      write(LO, ntestfailed);
      write(LO,string'(") "));      
    end if;
    writeline (OUTPUT,LO);
    wait;

  end process runtb;

  -----------------------------------------------------------------------------
  -- Test the ROP (runs in parallel to the main test)
  -----------------------------------------------------------------------------

       
       
  test_ROP: process (ck_inputs, reset_i) is
    variable ev_count  : integer := 0;

                                
  begin  -- process test_ROP
    if (reset_i = '1') then
      l1a_i <= '0'; 
    elsif ck_inputs'event and ck_inputs = '1' then  -- rising clock edge
        

      -- generate three L1As
--      if (ev_count = 20 or ev_count = 23 or ev_count = 30) then
      if (ev_count = 20 or ev_count = 25 or ev_count = 34) then
        l1a_i <= '1';
      else
        l1a_i <= '0';
      end if;
          
      ev_count := ev_count + 1;
    end if;
  end process test_ROP;

  -----------------------------------------------------------------------------
  -- read the readout data from channel link and save to file
  -----------------------------------------------------------------------------

  read_chlink: process (ch_link1_clk_i) is
    file F                           : text open write_mode is "chlink1.out";
                                         -- synchronized muons from input FPGA
    variable LO                      : line;  -- input file line
    variable chipnr,chip_nr_prev     : integer;
    variable second_round            : boolean := false;
    variable prev_data               : std_logic_vector (23 downto 0);
    variable imu : integer;                                        
  begin  -- process read_chlink
    if ch_link1_clk_i'event and ch_link1_clk_i = '1' then  
                                        -- rising clock edge
      write (LO, to_string(ch_link1_i));
      case (ch_link1_i(27 downto 24)) is
        when "0000" => write (LO, string'(" I ")); 
        when "1010" => write (LO, string'(" A ")); 
        when "1011" => write (LO, string'(" B ")); 
        when "1100" => write (LO, string'(" C ")); 
        when "1101" => write (LO, string'(" D ")); 
        when "0001" => write (LO, string'(" 1 ")); 
        when "0010" => write (LO, string'(" 2 ")); 
        when "0011" => write (LO, string'(" 3 ")); 
        when "0100" => write (LO, string'(" 4 ")); 
        when "0101" => write (LO, string'(" 5 ")); 
        when "1110" => write (LO, string'(" E ")); 
        when "1111" => write (LO, string'(" F ")); 
        when others => null;
      end case;

      -- decode A and B fields
      if (ch_link1_i(27 downto 24) = "1011") then
        write (LO, string'(" BX errors: "));
        write (LO, to_string(ch_link1_i(12 downto 8)));
        write (LO, string'("; event number: "));
        write (LO, to_integer(unsigned( ch_link1_i(7 downto 0) & prev_data(15 downto 0))));
      end if;
      
      -- decode C field
      if (ch_link1_i(27 downto 24) = "1100") then
        write (LO, string'(" BX in event: "));
        write (LO, to_integer(signed( ch_link1_i(15 downto 12))));
        write (LO, string'("; Reference BX count: "));
        write (LO, to_integer(unsigned( ch_link1_i(11 downto 0))));
      end if;
      
      -- decode D field
      if (ch_link1_i(27 downto 24) = "1101") then
        write (LO, string'(" Board ID: "));
        write (LO, to_integer(unsigned( ch_link1_i(15 downto 8))));
        write (LO, string'("; Length of record(words): "));
        write (LO, to_integer(unsigned( ch_link1_i(4 downto 0))));
      end if;
      
      case (ch_link1_i(27 downto 24)) is
        when "0001" => chipnr := 1;
        when "0010" => chipnr := 2;
        when "0011" => chipnr := 3;
        when "0100" => chipnr := 4;
        when "0101" => chipnr := 5;
        when others => chipnr := -1;
      end case;

      if chipnr >= 1 and chipnr <= 5 then
        if chip_nr_prev /= chipnr then
          imu := 0;
        end if;
        if (second_round) then
          -- decode input muons
          if (chipnr < 5) then
            case (ch_link1_i(15 downto 14)) is
              when "00" => write (LO, string'(" DT   ")); 
              when "01" => write (LO, string'(" BRPC ")); 
              when "10" => write (LO, string'(" CSC  ")); 
              when "11" => write (LO, string'(" FRPC ")); 
              when others => null;
            end case;
            -- remove inversion of lower 16 bits
            write (lo,SyncedMu_from_flat(ch_link1_i(8 downto 0) & (not prev_data(15 downto 8)) & prev_data(7 downto 0)));

          -- decode GMT muons and sort ranks
          else
            if (imu < 4) then
              write (lo,string'(" BrlGMT "));
            elsif (imu < 8) then
              write (lo,string'(" FwdGMT "));
            elsif (imu < 12) then
              write (lo,string'("    GMT "));
            else
              write (lo,string'(" S-Rank "));              
            end if;
            if (imu <12) then
              write (lo,string'(" IsRPC:"));
              write (lo,to_bit(ch_link1_i(15)));
              write (lo,string'(" IsFwd:"));
              write (lo,to_bit(ch_link1_i(14)));
              write (lo,string'(" IdxRPC:"));
              write (lo,to_integer(unsigned(ch_link1_i(13 downto 12))));
              write (lo,string'(" IdxDTCSC:"));
              write (lo,to_integer(unsigned(ch_link1_i(11 downto 10))));
              write (lo,string'(" "));
              write (lo,GMTMu_from_flat(ch_link1_i(9 downto 0) & prev_data(15 downto 0)));           
            else
              for i in 1 downto 0 loop
                write (lo, to_integer(unsigned(ch_link1_i(8*(i+1)-1 downto 8*i))));
                write (lo, string'(" "));
              end loop;  -- i
              for i in 1 downto 0 loop
                write (lo, to_integer(unsigned(prev_data(8*(i+1)-1 downto 8*i))));
                write (lo, string'(" "));
              end loop;  -- i
             end if;
          end if;
          imu := imu+1;
        end if;
        second_round := not second_round;
        chip_nr_prev := chipnr;
      else
       second_round := false;
       chip_nr_prev := -1;
      end if;

      prev_data := ch_link1_i(23 downto 0);

      writeline (F, LO);       
    end if;
  end process read_chlink;


  -------------------------------------------------------------------------------
  -- VME signals
  -------------------------------------------------------------------------------

  vme_addr_board_i   <= (others => 'L');
  vme_data_i   <= (others => 'Z');

  SINGLE_ACCESS_i  <= '1';
  BLT_ACCESS_i     <= '0';
  DSPULS_i         <= 'L';
  DSCYC_i          <= 'L';
  DSSYNC_i         <= 'L';
  WRITE_I_i        <= 'L';

  vme_en_i <= 'L';

  ------------------------------------------------------------------------------
  -- test the VME Spy (runs in parallel to the main test)
  ------------------------------------------------------------------------------

  
  ds: process (ck_inputs) is
  begin  -- process ds
    if ck_inputs'event and ck_inputs = '1' then  -- rising clock edge
      vme_en_i_d <= vme_en_i;
    end if;
  end process ds;

  DSCYC_i <= vme_en_i;
  DSSYNC_i <= vme_en_i;
  DSPULS_i <= vme_en_i and (not vme_en_i_d);

  
  test_VME: process (ck_inputs) is

    procedure readVME (
      variable start_ev : in  integer;
      variable curr_ev  : in  integer;
      address : in integer;
      signal vme_addr : out std_logic_vector;
      signal vme_data : in std_logic_vector;
      signal vme_en : out std_logic;
      signal vme_wr : out std_logic;
      signal vme_dtack : in std_logic;
      file F : text;
      variable finished : inout integer) is 

    variable data : std_logic_vector(15 downto 0);
    variable LO  : line;
--    variable finished  : integer := 0;

    variable addr_vec : std_logic_vector(23 downto 0);
    
  begin
    addr_vec := std_logic_vector(to_unsigned(address,24));
    if (curr_ev = start_ev) then
      -- apply read address
      vme_addr(addr_vec'high downto 1) <= addr_vec(addr_vec'high downto 1);
      vme_wr <= '0';
      vme_en <= '1';
      finished := 0;
      write (LO,string'("+++ start read addr "));
      write (LO,to_string(addr_vec));         
      write (LO,string'("  curr_ev = "));
      write (LO,curr_ev);
      writeline(F, LO);        
    elsif (curr_ev > start_ev) and (curr_ev < start_ev+9) then
      if vme_dtack = '1' then
        data := vme_data(data'range);
        write (LO,string'("**** VME output at addr "));
        write (LO,to_string(addr_vec));         
        write (LO,string'(": "));
        write (LO,to_string(data));
        write (LO,string'("  curr_ev = "));
        write (LO,curr_ev);
        writeline(F, LO);        
        vme_en <= '0';
        vme_addr <= (others => 'Z');
        finished := 1;
      end if;
    elsif (curr_ev = start_ev+9 and finished = 0) then
      write (LO,string'(" ***** did not receive dtack"));
      writeline(F, LO);
      vme_en <= '0';
      vme_addr <= (others => 'Z');         
    end if;      
  end;

  procedure writeVME (
    variable start_ev : in  integer;
    variable curr_ev  : in  integer;
    address : in integer;
    variable value : in std_logic_vector(15 downto 0);
    signal vme_addr : out std_logic_vector;
    signal vme_data : out std_logic_vector;
    signal vme_en : out std_logic;
    signal vme_wr : out std_logic;
    signal vme_dtack : in std_logic;
    file F : text;
    variable finished : inout integer) is 

    variable data : std_logic_vector(15 downto 0);
    variable LO  : line;
--    variable finished  : integer := 0;

    variable addr_vec : std_logic_vector(23 downto 0);
    
  begin
    addr_vec := std_logic_vector(to_unsigned(address,24));
    if (curr_ev = start_ev) then
      -- apply read address
      vme_addr(addr_vec'high downto 1) <= addr_vec(addr_vec'high downto 1);
      vme_data <= value;
      vme_wr <= '1';
      vme_en <= '1';
      finished := 0;
      write (LO,string'("+++ start write addr "));
      write (LO,to_string(addr_vec));         
      write (LO,string'("  data = "));
      write (LO,to_string(value));         
      write (LO,string'("  curr_ev = "));
      write (LO,curr_ev);
      writeline(F, LO);        
    elsif (curr_ev > start_ev) and (curr_ev < start_ev+9) then
      if vme_dtack = '1' then
        write (LO,string'("**** write cycle completed "));
        write (LO,string'("  curr_ev = "));
        write (LO,curr_ev);
        writeline(F, LO);        
        vme_en <= '0';
        vme_addr <= (others => 'Z');
        vme_data <= (others => 'Z');
        finished := 1;
      end if;
    elsif (curr_ev = start_ev+9 and finished = 0) then
      write (LO,string'(" ***** did not receive dtack"));
      writeline(F, LO);
      vme_en <= '0';
      vme_addr <= (others => 'Z');         
      vme_data <= (others => 'Z');
    end if;      
  end;


  variable ev_count         : integer := 0;
  variable finished         : integer := 0;
  variable spy_base_IND     : integer := 16#310000#;  -- Spy RAM
  variable etaconv_base_AUF : integer := 16#500400#;  -- asynch LUT
  variable phipro_base_AUF  : integer := 16#510000#;  -- synch LUT
  variable reg_base_ROP     : integer := 16#000000#;  -- synch LUT
  file F                    : text open write_mode is "vmetest.out";



  begin  -- process test_VME
    if ck_inputs'event and ck_inputs = '1' then  -- rising clock edge
      if ev_count = 0 then
        vme_en_i <= '0';
        WRITE_I_i <= '0';
        vme_data_i <= (others => 'Z');
        vme_addr_board_i <= (others =>'0');
      end if;

      -- read SPY MEMORY
      readVME(25, ev_count, spy_base_IND,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(35, ev_count, spy_base_IND+2,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(45, ev_count, spy_base_IND+4,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      -- write
      writeVME(55, ev_count, spy_base_IND+6, "0000111100001111",
               vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      -- read it back
      readVME(65, ev_count, spy_base_IND+6,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);

      -- read from nonexisting register - this should fail
      readVME(75, ev_count, 16#30000A#,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      -- read from register
      readVME(85, ev_count, 16#300010#,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(95, ev_count, spy_base_IND+14,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(105, ev_count,spy_base_IND+16,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(115, ev_count,etaconv_base_AUF,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(125, ev_count,etaconv_base_AUF+2,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(135, ev_count,etaconv_base_AUF+4,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(145, ev_count,etaconv_base_AUF+6,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(155, ev_count,phipro_base_AUF,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(165, ev_count,phipro_base_AUF+2,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(175, ev_count,phipro_base_AUF+4,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(185, ev_count,reg_base_ROP+16#60#,  -- DCM locked reg
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(195, ev_count,reg_base_ROP+16#61#,  -- status 0 reg
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
       writeVME(205, ev_count,reg_base_ROP+16#40#, "0001110001110001",  -- en-prog
               vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      -- read it back
      readVME(215, ev_count, reg_base_ROP+16#40#,  -- en_prog
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      
      readVME(225, ev_count,reg_base_ROP+26,  -- JTAG mode reg 0
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(235, ev_count,reg_base_ROP+28,  -- JTAG mode reg 1
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(245, ev_count,spy_base_IND+44,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(255, ev_count,spy_base_IND+46,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);
      readVME(265, ev_count,spy_base_IND+48,
              vme_addr_board_i, vme_data_i, vme_en_i, WRITE_I_i, DTACK_EXT_i, F, finished);

      
      ev_count := ev_count + 1;
    end if;
  end process test_VME;
end;