--******************************************************************************
--* @short      Cancel-Out Unit in Logic FPGA 
--******************************************************************************
--* @author  SAKULIN Hannes  <hsakulin@dsy-srv3.cern.ch>
--* @date    $Date: 2005/01/31 15:17:29 $
--* @version $Revision: 1.8 $
--******************************************************************************
--/
library IEEE;
use IEEE.Std_logic_1164.all;
use WORK.GMTTypes.all;
use WORK.LFTypes.all;
use work.LFVMEAddrMap.all;
use work.LFVMERegDefaults.all;
use work.LFTiming.all;
use work.VMEMux.all;
--use STD.TEXTIO.all;
use IEEE.NUMERIC_STD.all;


-- TBD: use disable bits??

-- 
-- There are four cancel-out units, two in the barrel chip and two in the fwd chip.
--
-- the table below lists their indices and what muons they receive

-- idx   unit      where   mu1     mu2           1st is mtch  2nd is mtch
----------------------------------------------------------------------------
--  0   DT/CSC     brl     DT      CSC           DT           CSC          
--  1   CSC/DT     fwd     CSC     DT            CSC          DT           
--  2   bRPC/CSC   brl     bRPC    CSC           bRPC         CSC          
--  3   fRPC/DT    fwd     fRPC    DT            fRPC         DT           
--                         (mine)  (other chip)  (my matcher) (other chip matcher)

-- The first muons (mine) contain 6-bit eta and the eta has to be converted to
-- 4 bits in the ovletaconv (Overlap Eta Conversion) LUT. Empty muons are marked by empty bits (which
--  are generated from pt==0)
--
-- The second muons (other) contain only 4-bits  as only 4-bits are sent from
-- the Input FPGAs. Empty muons are marked by an out-of-range eta value.
--
--
-- The cancel-decsison-logic in each case computes a cancel-out bit for each of
-- the first (my chip) and second (other chip) input muons. 
--
--
-- idx   unit    Cancel_Mine   CancelOther(DT/CSC)
-------------------------------------------------------------------------------
--  0   DT/CSC       DT            would be CSC, not used
--  1   CSC/DT       CSC           would be DT, not used
--  2   bRPC/CSC     bRPC(n/used)  CSC (used often?)
--  3   fRPC/DT      FRPC          DT(n/used)
--                      
-- The cancel-out units are configured via the cdl_control register
-- 
-- The lowest four bits control under which conditions the first (my chip) muon
-- will be cancelled. Bits 4 to 7 control under which conditions the second (other chip)
-- muons are cancelled. 
--
-- A muon candidate can be cancelled if there is a match in the cancel-out unit
-- between the two types of candidates that are compared in this unit. The
-- decision on whether to cancel one of the candidates depends on wheter the
-- candidate and/or the candidate that it is matched to in the cancel-out unit
-- are matched with a complementary candidate in their own main matching unit.
--
-- e.g. in the first example below, a DT muon which is matched to a CSC muon in
-- the DT/CSC cancel-out unit will be cancelled only if the DT muon is not
-- matched to an RPC muon in then main matching unit and it the CSC muon is
-- matched to an RPC muon in its main matching unit.
-- 
-- Cancel Decision Logic Control Register for DT/CSC unit (brl chip) 
-- =================================================================
--
--  possible matching results               1  1  0  0  DT is matched w bRPC
--  of main matching units (00..11)         1  0  1  0  CSC is matched w FRPC
--
-- DT/CSC brl   |            |            | 0  0  1  0  | default programming
-- bit nr.                   | 7  6  5  4 | 3  2  1  0  |
--                                              cancel DT
--
--
-- Cancel Decision Logic Control Register for CSC/DT unit (fwd chip) 
-- =================================================================
--
--                              condition:  1  1  0  0  CSC is matched w fRPC 
--                                          1  0  1  0  DT is matched w bRPC
--
-- CSC/DT fwd   |            |            | 0  0  1  1  | default programming
-- bit nr.                   | 7  6  5  4 | 3  2  1  0  |
--                                             cancel CSC
-- 
--
-- Cancel Decision Logic Control Register for bRPC/CSC unit (brl chip) 
-- =================================================================
--
--                              condition:  1  1  0  0  bRPC is matched w DT 
--                                          1  0  1  0  CSC is matched w fRPC
--
--                 condition:  1  1  0  0  CSC is matched w fRPC
--                             1  0  1  0  bRPC is matched w DT
--
-- bRPC/CSC brl |            | 0  0  0  1 | 0  0  0  0  | default programming
-- bit nr.                   | 7  6  5  4 | 3  2  1  0  |
--                              cancel CSC   cancel bRPC
--                           in other chip
--
-- Cancel Decision Logic Control Register for fRPC/DT unit (fwd chip) 
-- =================================================================
--
--                              condition:  1  1  0  0  fRPC is matched w CSC
--                                          1  0  1  0  DT is matched w bRPC
--
--                 condition:  1  1  0  0  DT is matched w bRPC
--                             1  0  1  0  fRPC is matched w CSC
--
-- fRPC/DT fwd  |            | 0  0  0  0 | 0  0  0  1  | default programming
-- bit nr.                   | 7  6  5  4 | 3  2  1  0  |
--                               cancel DT   cancel fRPC
--                           in other chip
-- 
--

entity LFCancelOutUnit is
  
  generic (
    cou_idx : integer := 0;     -- 0: DT/CSC, 1: CSC/DT, 2: bRPC/CSC, 3: fRPC/DT
    cou_lat_start : integer := 0);  -- LATENCY at start

  port (
    phi_mine : in TPhi_vec (0 to 3);
    eta_mine : in TEta_vec (0 to 3);

    phi_other : in TPhi_vec (0 to 3);
    eta_other : in TEta4_vec (0 to 3);

    empty_mine : in std_logic_vector (0 to 3);
    -- empty-other is coded in eta

    disable_mine    : in std_logic_vector (0 to 3);  -- arrive 1/2 clock later
    disable_other   : in std_logic_vector (0 to 3);  -- arrive 1/2 clock later
    -- disable signal arrive 1/2 bx later and will be registered 1.5 bx after
    -- the first input in the matcher (works ok)

    mine_is_matched   : in std_logic_vector (0 to 3); 
    other_is_matched  : in std_logic_vector (0 to 3); 
        
    -- outputs
    oPairMatrix    : out TPairMatrix;
--    oMQMatrix      : out TMQMatrix;

    oCancelMine    : out std_logic_vector(0 to 3);
    oCancelOther_nr: out std_logic_vector(0 to 3);


    -- VME port
    vme_addr       : in    std_logic_vector;
    vme_data       : in    std_logic_vector;
    vme_en         : in    std_logic;
    vme_wr         : in    std_logic;

    vme_data_out   : out   std_logic_vector(15 downto 0);
    vme_en_out     : out   std_logic;
        
    -- Clock and control
    clk            : in    std_logic;
    sinit          : in    std_logic
    );  
  
end entity LFCancelOutUnit;

architecture behavioral of LFCancelOutUnit is

  procedure calc_cancel_decision (
    constant PairMatrix      : in  TPairMatrix;
    constant FirstIsMatched  : in  std_logic_vector(0 to 3);
    constant SecondIsMatched : in  std_logic_vector(0 to 3);
    constant mine_is_matched : in  std_logic_vector(0 to 3);
    constant other_is_matched: in  std_logic_vector(0 to 3);
    constant cdl_config      : in  std_logic_vector(15 downto 0);
    signal   cancel_mine     : out std_logic_vector(0 to 3);
    signal   cancel_other    : out std_logic_vector(0 to 3)) is
    
    variable COU_match : std_logic;
    variable match_is_matched : std_logic;
    variable addr : std_logic_vector(1 downto 0);

--    variable lo : line;
    
  begin  -- process calc_cancel_decision
    -- first check if "my muon" has to be cancelled
    for i in 0 to 3 loop
      -- is there a COU match for my muon number i?
      COU_match := FirstIsMatched(i);

      -- is the match matched in its own main matching unit?
      match_is_matched := ( PairMatrix(i,0) and other_is_matched(0) ) or
                          ( PairMatrix(i,1) and other_is_matched(1) ) or
                          ( PairMatrix(i,2) and other_is_matched(2) ) or
                          ( PairMatrix(i,3) and other_is_matched(3) );
      
      addr := mine_is_matched(i) & match_is_matched;
      
      cancel_mine(i) <= COU_match and cdl_config( TO_INTEGER(UNSIGNED(addr)) );

--         write (lo,string'("mine: idx_cou=")) ;
--         write (lo,cou_idx);
--         write (lo,string'(" i=")) ;
--         write (lo,i);
--         write (lo,string'(" cou_match = ")) ;
--         write (lo,TO_BIT(COU_match));
--         write (lo,string'(" mine_is_matched = ")) ;
--         write (lo,TO_BIT(mine_is_matched(i)));
--         write (lo,string'(" match_is_matched = ")) ;
--         write (lo,TO_BIT(match_is_matched));
--         write (lo,string'("cdl_config ")) ;
--         write (lo,TO_BITVECTOR(cdl_config));
--         write (lo,string'("   at addr  ")) ;
--         write (lo,TO_INTEGER(UNSIGNED(addr))) ;
--         write (lo,string'(" is ")) ;
--         write (lo, TO_BIT( cdl_config( TO_INTEGER(UNSIGNED(addr) ))));
--         writeline(output, lo);
      
    end loop;  -- i
    -- 
    -- then check if "other muon" has to be cancelled
    for i in 0 to 3 loop
      -- is there a COU match for other muon nr i?
      COU_match := SecondIsMatched(i);

      -- is the match matched in its own main matching unit?
      match_is_matched := ( PairMatrix(0,i) and mine_is_matched(0) ) or
                          ( PairMatrix(1,i) and mine_is_matched(1) ) or
                          ( PairMatrix(2,i) and mine_is_matched(2) ) or
                          ( PairMatrix(3,i) and mine_is_matched(3) );
      
      addr := other_is_matched(i) & match_is_matched;
      cancel_other(i) <= COU_match and cdl_config( TO_INTEGER(UNSIGNED(addr)) + 4 );

--         write (lo,string'("other: idx_cou=")) ;
--         write (lo,cou_idx);
--         write (lo,string'(" i=")) ;
--         write (lo,i);
--         write (lo,string'(" cou_match = ")) ;
--         write (lo,TO_BIT(COU_match));
--         write (lo,string'(" other_is_matched = ")) ;
--         write (lo,TO_BIT(other_is_matched(i)));
--         write (lo,string'(" match_is_matched = ")) ;
--         write (lo,TO_BIT(match_is_matched));
--         write (lo,string'("cdl_config ")) ;
--         write (lo,TO_BITVECTOR(cdl_config));
--         write (lo,string'("   at addr  ")) ;
--         write (lo,TO_INTEGER(UNSIGNED(addr))+4) ;
--         write (lo,string'(" is ")) ;
--         write (lo, TO_BIT( cdl_config( TO_INTEGER(UNSIGNED(addr) )+4)));
--         writeline(output, lo);

    end loop;  -- i      

  end; -- procedure calc_cancel_decision
  

  
--  signal MQMatrix : TMQMatrix;

  signal vme_data_out_i   : TVMEData_vec (0 to 6);
  signal vme_en_out_i     : TVMEEnable_vec (0 to 6);

  signal nclk : std_logic;

  signal PairMatrix_i : TPairMatrix;
  signal FirstIsMatched_i :  std_logic_vector (0 to 3);
  signal SecondIsMatched_i   :  std_logic_vector (0 to 3);

  signal eta4_mine        : TEta4_vec (0 to 3);
  signal eta4in6_mine     : TEta_vec (0 to 3);
  signal eta4in6_other    : TEta_vec (0 to 3);
  signal eta4in6_mine_d   : TEta_vec (0 to 3);
  signal eta4in6_other_d  : TEta_vec (0 to 3);

  signal low : std_logic_vector(0 to 3);

  signal cdl_config : std_logic_vector(15 downto 0);

  -- delayed inputs
  signal phi_mine_d : TPhi_vec (0 to 3);
  signal eta_mine_d : TEta_vec (0 to 3);
  signal phi_other_d : TPhi_vec (0 to 3);
  signal eta_other_d : TEtaCOU_vec (0 to 3);
  signal mine_is_matched_d : std_logic_vector (0 to 3);
  
  signal sCancelMine  : std_logic_vector(0 to 3);
  signal sCancelOther : std_logic_vector(0 to 3);

begin  -- architecture behavioral

  -----------------------------------------------------------------------------
  -- convert my eta and store in vec of 6-bit eta
  -----------------------------------------------------------------------------
  n: for i in 0 to 3 generate
  begin  -- generate n
    eta_conv: entity work.lfovletaconvlut
      generic map (
        instance_idx        => cou_idx,
        my_vme_base_address => LF_OvlEtaConvLUT_base(cou_idx / 2) + i * LF_OvlEtaConvLUT_size)
      port map (
        eta6         => eta_mine(i),
        eta_ovl      => eta4_mine(i),
        
        vme_addr     => vme_addr,
        vme_data     => vme_data,
        vme_en       => vme_en,
        vme_wr       => vme_wr,
        vme_data_out => vme_data_out_i(i),
        vme_en_out   => vme_en_out_i(i),
        vme_clk      => clk);
    -- set to "out of range" if muon is empty
    eta4in6_mine(i) <= ( "00" & eta4_mine(i) ) when empty_mine(i) = '0' else "000111";    
    eta4in6_other(i) <= ( "00" & eta_other(i) ) when phi_other(i) /= "11111111" else "000111";
  end generate n;

  -----------------------------------------------------------------------------
  -- delay inputs
  -----------------------------------------------------------------------------
  delay_inputs: process (clk) is
  begin  -- process delay_inputs
    if clk'event and clk = calc_lf_edge(cou_lat_start+LF_RLAT_COU_DELAY) then 
      phi_mine_d <= phi_mine;
      eta4in6_mine_d <= eta4in6_mine;

      phi_other_d  <= phi_other;
      eta4in6_other_d <= eta4in6_other;

    end if;
  end process delay_inputs;

  -----------------------------------------------------------------------------
  -- delay mine is matched
  -----------------------------------------------------------------------------
  delay_mineismatched: process (clk) is
  begin 
    if clk'event and clk = calc_lf_edge(cou_lat_start+LF_RLAT_COU_MU) then 
      mine_is_matched_d <= mine_is_matched;
    end if;
  end process delay_mineismatched;
  
  -----------------------------------------------------------------------------
  -- Matching unit
  -----------------------------------------------------------------------------  
  low <= "0000";
  Matcher: entity work.LFMatchingUnit
    generic map (
      match_unit_idx => cou_idx+2,
      match_unit_lat_start => cou_lat_start + LF_RLAT_COU_DELAY)
    port map (
      -- inputs
      phi1            => phi_mine_d,
      phi2            => phi_other_d,
      eta1            => eta4in6_mine_d,
      eta2            => eta4in6_other_d,
      empty1          => low,
      empty2          => low,
      disable1        => disable_mine,
      disable2        => disable_other,

      -- outputs
      oFirstIsMatched  => FirstIsMatched_i,
      oFirstIsMatched_nr  => open,
      oSecondIsMatched => SecondIsMatched_i,
      oPairMatrix      => PairMatrix_i,
--      oMQMatrix       => MQMatrix,

      -- VME
      vme_addr        => vme_addr,
      vme_data        => vme_data,
      vme_en          => vme_en,
      vme_wr          => vme_wr,
      vme_data_out    => vme_data_out_i(4),
      vme_en_out      => vme_en_out_i(4),
      clk             => clk,
      sinit           => sinit);

  -----------------------------------------------------------------------------
  -- Cancel decision Logic
  -----------------------------------------------------------------------------

  cdl_control: entity work.VMEReg
    generic map (
      init_val            => std_logic_vector(to_unsigned(LF_CDLConfig_defaults(cou_idx),16)),
      my_vme_base_address => LF_CDLConfig_addr(cou_idx/2))
    port map (
      data         => cdl_config,
      reset        => sinit,
      
      vme_addr     => vme_addr,
      vme_data     => vme_data,
      vme_en       => vme_en,
      vme_wr       => vme_wr,
      vme_data_out => vme_data_out_i(5),
      vme_en_out   => vme_en_out_i(5),
      vme_clk      => clk);

    calc_cancel_decision (
      PairMatrix      => PairMatrix_i,
      FirstIsMatched  => FirstIsMatched_i,
      SecondIsMatched => SecondIsMatched_i,
      mine_is_matched => mine_is_matched_d,
      other_is_matched=> other_is_matched,
      cdl_config      => cdl_config,
      cancel_mine     => sCancelMine,
      cancel_other    => sCancelOther);
  
  cancel_decision: process (clk) is
    begin
      if (clk'event and clk = calc_lf_edge(cou_lat_start + LF_RLAT_COU_CDL )) then
        oCancelMine <= sCancelMine;
        oPairMatrix <= PairMatrix_i;    -- for debug only
      end if;      
  end process cancel_decision;

  oCancelOther_nr <= sCancelOther;      -- not registered

  -- multiplex vme_data_output
  mux_vme(vme_data_out_i, vme_en_out_i, vme_data_out, vme_en_out);

end architecture behavioral;