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

-- the two main matching units receive:
--   6-bit eta, empty bits (which are generated from pt==0), disable bits (for
--   hot regions)

-- the four cancel-out matching units receive:
--   4-bit eta including empty code (which are generated from pt==0), disable bits (for
--   hot regions)
--
--  could just use only the lower bits of eta1, eta2
-- => attach '0' to the empty bits

entity LFMatchingUnit is
  
  generic (
    match_unit_idx : integer := 0; -- 0: DT/RPC, 1: CSC/RPC, 2: DT/CSC, 3: CSC/DT, 4: bRPC/CSC, 5: fRPC/DT
    match_unit_lat_start : integer := 0);          -- LATENCY at start

  port (
    phi1, phi2 : in TPhi_vec (0 to 3);
    eta1, eta2 : in TEta_vec (0 to 3);

    empty1     : in std_logic_vector (0 to 3);
    empty2     : in std_logic_vector (0 to 3);

    disable1   : in std_logic_vector (0 to 3);  -- arrive 1/2 clock later (main
                                                -- matching) or on time (COU)
                                          
    disable2   : in std_logic_vector (0 to 3);  -- arrive 1/2 clock later (main
                                                -- matching) or on time (COU)

    -- outputs
    oFirstIsMatched : out std_logic_vector (0 to 3);
    oFirstIsMatched_nr : out std_logic_vector (0 to 3);  -- not registered
    oSecondIsMatched   : out std_logic_vector (0 to 3);
    oPairMatrix : out TPairMatrix;
--    oMQMatrix : out TMQMatrix;


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

architecture behavioral of LFMatchingUnit is
  type TDeltaEtaMatrix is array (integer range 0 to 3, integer range 0 to 3) of std_logic_vector(3 downto 0);
  type TDeltaPhiMatrix is array (integer range 0 to 3, integer range 0 to 3) of std_logic_vector(2 downto 0);
  
  signal MQMatrix : TMQMatrix;
  signal MQMatrix_reg : TMQMatrix;
  signal MQMatrix_notempty : TMQMatrix;

  signal DeltaEtaMatrix : TDeltaEtaMatrix;
  signal DeltaEtaMatrix_temp : TDeltaEtaMatrix;
  signal DeltaPhiMatrix : TDeltaPhiMatrix;

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

  signal PairMatrix_i : TPairMatrix;

  signal empty1_d : std_logic_vector (0 to 3);
  signal empty2_d : std_logic_vector (0 to 3);
  signal disable1_d : std_logic_vector (0 to 3);
  signal disable2_d : std_logic_vector (0 to 3);
begin  -- architecture behavioral
  
  -- register empty & disable
  register_empty: process (clk) is
  begin  -- process register_empty
    if clk'event and clk = calc_lf_edge(match_unit_lat_start + LF_RLAT_MU_DELTAETA) then      
      empty1_d <= empty1;
      empty2_d <= empty2;

      disable1_d <= disable1;
      disable2_d <= disable2;      
    end if;
  end process register_empty;

  mq_i: for i in 0 to 3 generate
  begin  -- generate mq_i
    mq_j: for j in 0 to 3 generate
    begin  -- generate mq_j

      -------------------------------------------------------------------------
      -- in main matching units calculate delta eta from 6-bit eta values
      -------------------------------------------------------------------------
      main: if (match_unit_idx<2) generate
      begin  -- generate main_or_cou
      -- delta-eta LUT
      delta_eta_lut: entity work.lfdeltaetalut
        generic map (
          instance_idx        =>  match_unit_idx ,
          my_vme_base_address =>  LF_DeltaEtaLUT_base(0) + LF_DeltaEtaLUT_size * (i*4+j),
          edge                =>  calc_lf_edge(match_unit_lat_start + LF_RLAT_MU_DELTAETA - 1))
        port map (
          eta_dtcsc    => eta1(i),
          eta_rpc      => eta2(j),
          delta_eta    => DeltaEtaMatrix(i,j),
          
          clk          => clk,
          sinit        => sinit,
          vme_addr     => vme_addr,
          vme_data     => vme_data,
          vme_en       => vme_en,
          vme_wr       => vme_wr,
          vme_data_out => vme_data_out_i(i*4+j),
          vme_en_out   => vme_en_out_i(i*4+j),
          vme_clk      => clk);

      end generate main;

      -------------------------------------------------------------------------
      -- in cancel-out matching units calculate delta_eta from 4-bit eta values
      -- (use lower 4 bits)
      -------------------------------------------------------------------------
      cou: if (match_unit_idx>=2) generate
      begin  -- generate main_or_cou
      -- delta-eta LUT (distr ram)
      delta_eta_lut: entity work.lfcoudeltaetalut
        generic map (
          instance_idx        =>  match_unit_idx - 2,
          my_vme_base_address =>  LF_COUDeltaEtaLUT_base( (match_unit_idx-2) / 2) + LF_COUDeltaEtaLUT_size * (i*4+j) )
        port map (
          eta1         => eta1(i)(3 downto 0),
          eta2         => eta2(j)(3 downto 0),
          delta_eta    => DeltaEtaMatrix_temp(i,j),
          
          vme_addr     => vme_addr,
          vme_data     => vme_data,
          vme_en       => vme_en,
          vme_wr       => vme_wr,
          vme_data_out => vme_data_out_i(i*4+j),
          vme_en_out   => vme_en_out_i(i*4+j),
          vme_clk      => clk);        

          DeltaEtaMatrix(i,j) <= DeltaEtaMatrix_temp(i,j);

      end generate cou;



      -- delta-phi subtracter
      delta_phi_sub: entity work.LFDeltaPhiSub
        port map (
          phi1 => phi1(i),
          phi2 => phi2(j),
          dphi => DeltaPhiMatrix(i,j)
          );

      -- Match-Quality LUTs
      mq_lut: entity work.lfmatchquallut
        generic map (
          instance_idx        => match_unit_idx,
          my_vme_base_address => LF_MatchQualLUT_base(match_unit_idx / 2) + LF_MatchQualLUT_size * (i*4+j) )
        port map (
          delta_eta    => DeltaEtaMatrix(i,j),
          delta_phi    => DeltaPhiMatrix(i,j),
          mq           => MQMatrix(i,j),
          
          vme_addr     => vme_addr,
          vme_data     => vme_data,
          vme_en       => vme_en,
          vme_wr       => vme_wr,
          vme_data_out => vme_data_out_i(16+i*4+j),
          vme_en_out   => vme_en_out_i(16+i*4+j),
          vme_clk      => clk);

      register_mq: process (clk) is
        begin  
          if clk'event and clk = calc_lf_edge(match_unit_lat_start + LF_RLAT_MU_DELTAETA) then  
            MQMatrix_reg(i,j) <= MQMatrix(i,j);
          end if;
        end process register_mq;

      MQMatrix_notempty(i,j) <= MQMatrix_reg(i,j) when ((empty1_d(i) = '0')
                                              and (empty2_d(j)='0')
                                              and (disable1_d(i)='0')
                                              and (disable2_d(j)='0')) else "000000";
    end generate mq_j;
  end generate mq_i;

  -- forward match quality to output
--  xx : process (clk) is
--                    variable LO: line; 
--  begin  -- process xx
--    if clk'event and clk = calc_lf_edge(match_unit_lat_start + LF_DELAY_MU)  then
--      oMQMatrix <= MQMatrix_notempty;
--            write(lo, string'("[[[[phi1, phi2, eta1, eta2]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(phi1(r))));
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(phi2(r))));
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(eta1(r))));
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(eta2(r))));
--                writeline (OUTPUT, LO);      
--            end loop;  -- r

--            write(lo, string'("matchunit idx = "));
--            write(lo, match_unit_idx);
--            writeline(output, lo);

--       write(lo, string'("[[[[delta eta Matrix]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--              for c in 0 to 3 loop
-- --                write (LO,string'(" "));
-- --                write (LO,r);
-- --                write (LO,string'(" "));
-- --                write (LO,c);
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(SIGNED(DeltaEtaMatrix(r,c))));
--              end loop;  -- c
--              writeline (OUTPUT, LO);      
--            end loop;  -- r
--            writeline (OUTPUT, LO);
      
--            write(lo, string'("[[[[delta phi Matrix]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--              for c in 0 to 3 loop
-- --                write (LO,string'(" "));
-- --                write (LO,r);
-- --                write (LO,string'(" "));
-- --                write (LO,c);
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(SIGNED(DeltaPhiMatrix(r,c))));
--              end loop;  -- c
--                writeline (OUTPUT, LO);      
--            end loop;  -- r
--            writeline (OUTPUT, LO);
      
--            write(lo, string'("[[[[MQ Matrix]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--              for c in 0 to 3 loop
-- --                write (LO,string'(" "));
-- --                write (LO,r);
-- --                write (LO,string'(" "));
-- --                write (LO,c);
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(MQMatrix_notempty(r,c))));
--              end loop;  -- c
--                writeline (OUTPUT, LO);      
--            end loop;  -- r
--            writeline (OUTPUT, LO);

--            write(lo, string'("[[[[MQ Matrix before empty check]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--              for c in 0 to 3 loop
-- --                write (LO,string'(" "));
-- --                write (LO,r);
-- --                write (LO,string'(" "));
-- --                write (LO,c);
--                write (LO,string'(" "));
--                write (LO, TO_INTEGER(UNSIGNED(MQMatrix(r,c))));
--              end loop;  -- c
--                writeline (OUTPUT, LO);      
--            end loop;  -- r
--            writeline (OUTPUT, LO);

      
--            write(lo, string'("[[[[Pair Matrix (previous)]]]]"));
--            writeline(output, lo);
--            for r in 0 to 3 loop
--              for c in 0 to 3 loop
-- --                write (LO,string'(" "));
-- --                write (LO,r);
-- --                write (LO,string'(" "));
-- --                write (LO,c);
--                write (LO,string'(" "));
--                write (LO, TO_BIT((PairMatrix_i(r,c))));
--              end loop;  -- c
--                writeline (OUTPUT, LO);      
--            end loop;  -- r
--            writeline (OUTPUT, LO);      

--    end if;
--  end process xx;
  
  -- MAX-DIS-PAIR logic
  pairlogic: entity work.LFPairLogic
    port map (
      iMQMatrix   => MQMatrix_notempty,
      oPairMatrix => PairMatrix_i,
      clk        => clk);

  
  reg_pm : process (clk) is
  begin                         
    if clk'event and clk = calc_lf_edge(match_unit_lat_start+LF_RLAT_MU_MQPAIR) then                         
      oPairMatrix <= PairMatrix_i;
      for i in 0 to 3 loop
        oFirstIsMatched(i) <= PairMatrix_i(i,0) or PairMatrix_i(i,1) or PairMatrix_i(i,2) or PairMatrix_i(i,3);
        oSecondIsMatched(i) <= PairMatrix_i(0,i) or PairMatrix_i(1,i) or PairMatrix_i(2,i) or PairMatrix_i(3,i);        
      end loop;  -- i
    end if;  
  end process reg_pm;

  -- non registered first is matched for other chip
  matched_nr: for i in 0 to 3 generate
  begin  -- generate matched_nr
    oFirstIsMatched_nr(i) <= PairMatrix_i(i,0) or PairMatrix_i(i,1) or PairMatrix_i(i,2) or PairMatrix_i(i,3);    
  end generate matched_nr;
  
  -- multiplex vme_data_output
  mux_vme(vme_data_out_i, vme_en_out_i, vme_data_out, vme_en_out);

end architecture behavioral;