--******************************************************************************
--* @short      Merge method selector. Selects merge method for each parameter.
--******************************************************************************
--* @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 IEEE.Numeric_Std.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;

entity LFMergeMethodSelector is
  
  generic (
    mms_idx       : integer := 0;   -- 0: brl, 1:fwd
    mms_lat_start : integer := 0);  -- start latency

  port (
    -- INPUTS
    iDTCSCMuons : in TSyncedMu_vector4;  -- at 0
    iRPCMuons   : in TSyncedMu_vector4;  -- at 0

    -- OUTPUTS
    oSelectMatrix_SRK    : out TPairMatrix;
    oSelectMatrix_phi    : out TPairMatrix;
    oSelectMatrix_eta    : out TPairMatrix;
    oSelectMatrix_pt     : out TPairMatrix;
    oSelectMatrix_charge : out TPairMatrix;
    oSelectMatrix_MIP    : out TPairMatrix;
    oSelectMatrix_ISO    : out TPairMatrix;
    
    oSpecialBit_eta    : out std_logic;
    oSpecialBit_pt     : out std_logic;
    oSpecialBit_charge : out std_logic;
    oSpecialBit_MIP    : out std_logic;
    oSpecialBit_ISO    : out std_logic;

    oSpecialMIPMergeUseAND : out std_logic;
    oSpecialISOMergeUseAND : out std_logic;
    oHaloOverwritesMatched : out std_logic;
    
    -- 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 LFMergeMethodSelector;

architecture behavioral of LFMergeMethodSelector is

  component comp5
    port (
      A: IN std_logic_VECTOR(4 downto 0);
      B: IN std_logic_VECTOR(4 downto 0);
      A_LE_B: OUT std_logic);
  end component;

  component comp8
    port (
      A: IN std_logic_VECTOR(7 downto 0);
      B: IN std_logic_VECTOR(7 downto 0);
      A_GE_B: OUT std_logic);
  end component;

-- Synplicity black box declaration
  attribute syn_black_box : boolean;
  attribute syn_black_box of comp5: component is true;
  attribute syn_black_box of comp8: component is true;

  procedure sel (
    constant iSelectMatrix_rank  : in  TPairMatrix;
    constant iSelectMatrix_minpt : in  TPairMatrix;
    constant iSelectMatrix_combi : in  TPairMatrix;
    constant iMMConfig           : in  std_logic_vector(15 downto 0);
    signal   oSelectMatrix       : out TPairMatrix;
    signal   oSpecialBit         : out std_logic) is
  begin
    -- mux Select Matrix
    for i in 0 to 3 loop
      for j in 0 to 3 loop
        oSelectMatrix(i,j) <= ( '1' and iMMConfig(5) ) or
                              ( '0' and iMMConfig(4) ) or  -- condition not necessary
                              ( iSelectMatrix_rank(i,j) and iMMConfig(3) ) or
                              ( iSelectMatrix_minpt(i,j) and iMMConfig(2) ) or
                              ( iSelectMatrix_combi(i,j) and iMMConfig(1) );
      end loop;  -- j
    end loop;  -- i
    oSpecialBit <= iMMConfig(0);
  end;
                                                   
-- Merge Rank
  subtype TMergeRank is Std_logic_vector(7 downto 0);
  type TMergeRank_vector is array (integer range <>) of TMergeRank;

  signal vme_data_out_i   : TVMEData_vec (0 to 14);
  signal vme_en_out_i     : TVMEEnable_vec (0 to 14);
  
  signal sMergeRankRPC    : TMergeRank_vector(0 to 3);
  signal sMergeRankDTCSC  : TMergeRank_vector(0 to 3);

  signal sFlagDTCSC : std_logic_vector(0 to 3);
  signal sFlagRPC   : std_logic_vector(0 to 3);
  
  signal sSelectMatrix_minpt : TPairMatrix;
  signal sSelectMatrix_rank  : TPairMatrix;
  signal sSelectMatrix_combi : TPairMatrix;

  signal sDTCSCMuons_d : TSyncedMu_vector4; 
  signal sRPCMuons_d   : TSyncedMu_vector4;

  signal sMMconfig_SRK    : std_logic_vector(15 downto 0);
  signal sMMConfig_phi    : std_logic_vector(15 downto 0);
  signal sMMConfig_eta    : std_logic_vector(15 downto 0);
  signal sMMConfig_pt     : std_logic_vector(15 downto 0);
  signal sMMConfig_charge : std_logic_vector(15 downto 0);
  signal sMMConfig_MIP    : std_logic_vector(15 downto 0);
  signal sMMConfig_ISO    : std_logic_vector(15 downto 0);

  signal dummy1, dummy2 : std_logic;
  
begin  -- architecture behavioral

  -----------------------------------------------------------------------------
  -- instantiate rank assignment units
  -----------------------------------------------------------------------------
  
  merge_rank_DTCSC: for i in 0 to 3 generate
  begin  -- generate merge_rank_DTCSC
    DTCSC: entity work.LFMergeRankAU
      generic map (
        mergerank_au_idx       => 2*mms_idx,
        idxmuon                => i,
        mergerank_au_lat_start => mms_lat_start)
      port map (
        iMuon        => iDTCSCMuons(i),
        oMergeRank   => sMergeRankDTCSC(i),
        oFlag        => sFlagDTCSC(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),
        clk          => clk,
        sinit        => sinit);
  end generate merge_rank_DTCSC;


  merge_rank_RPC: for i in 0 to 3 generate
  begin  -- generate merge_rank_RPC
    RPC: entity work.LFMergeRankAU
      generic map (
        mergerank_au_idx       => 2*mms_idx+1,
        idxmuon                 => i,
        mergerank_au_lat_start => mms_lat_start)
      port map (
        iMuon        => iRPCMuons(i),
        oMergeRank   => sMergeRankRPC(i),
        oFlag        => sFlagRPC(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+4),
        vme_en_out   => vme_en_out_i(i+4),
        clk          => clk,
        sinit        => sinit);
  end generate merge_rank_RPC;

  -----------------------------------------------------------------------------
  -- register input muons
  -----------------------------------------------------------------------------
  reg_mus: process (clk) is
  begin  
    if clk'event and clk = calc_lf_edge(mms_lat_start + LF_RLAT_MMS_LUT2) then
      sDTCSCMuons_d <= iDTCSCMuons;
      sRPCMuons_d <= iRPCMuons;
    end if;
  end process reg_mus;

  
  -----------------------------------------------------------------------------
  -- calculate select matrix based on merge ranks
  -----------------------------------------------------------------------------
  -- DT/CSC wins if equal rank
  f1: for i in 0 to 3 generate
    f2: for j in 0 to 3 generate
      x: comp8
        port map (
          A => sMergeRankDTCSC(i),
          B => sMergeRankRPC(j),
          A_GE_B => sSelectMatrix_rank(i,j));
    end generate;
  end generate;

  -----------------------------------------------------------------------------
  -- calculate select matrix based on pt
  -----------------------------------------------------------------------------
  -- DT/CSC wins if same pt
  g1: for i in 0 to 3 generate
    g2: for j in 0 to 3 generate
      x: comp5
        port map (
          A => sDTCSCMuons_d(i).pt,
          B => sRPCMuons_d(j).pt,
          A_LE_B => sSelectMatrix_minpt(i,j));
    end generate;
  end generate;

  -----------------------------------------------------------------------------
  -- calculate combined select matrix
  -----------------------------------------------------------------------------

  --TBD: optimized combined merging
  -- (min pt or max rank depending on eta, q of both muons)

  -- do min rank if A and B want it <=> do min pt if a or B want it (current)
  -- do min pt if A and B want it <=> do min rank if A or B want it

  -- both is not very flexible but eta,q,eta,q => decision LUT (18->1) is too big

  -- current version (flag means Use Rank)
  combi_i: for i in 0 to 3 generate
  begin
    combi_j: for j in 0 to 3 generate
    begin
      sSelectMatrix_combi(i,j) <= sSelectMatrix_rank(i,j) when (sFlagDTCSC(i) and sFlagRPC(j)) = '1' else
                                  sSelectMatrix_minpt(i,j);     
    end generate combi_j;
  end generate combi_i;

  -----------------------------------------------------------------------------
  -- merge method configuration registers (exist for each parameter)
  -----------------------------------------------------------------------------
  --
  -- reg layout: (only one of bits 0 => 5 must be set)
  --
  -- Bits Nr.  6      5       4       3        2         1       0
  --                  DT/CSC  RPC     byRank   byMinPt   byCombi bySpecial
  --                  always  always
  ------------------------------------------------------------------------------
  -- SRK       6:H/M(*)             
  -- phi                    
  -- eta                    
  -- pt                     
  -- charge                 
  -- MIP       6:AND             
  -- ISO       6:AND             
  --                  
  -- (*) 1 if Halo should overwrite matched muons
  
  mms_reg_SRK: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_SRK_defaults(mms_idx),16)),
                     -- "0000000001100000",  -- takeDT, HaloOverwritesMatched
                 my_vme_base_address => LF_MMConfig_SRK_addr)
    port map (sMMConfig_SRK, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(8), vme_en_out_i(8), clk, sinit);
  mms_reg_phi: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_Phi_defaults(mms_idx),16)),
                     -- "0000000000100000",  -- take DT
                 my_vme_base_address => LF_MMConfig_Phi_addr)
    port map (sMMConfig_phi, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(9), vme_en_out_i(9), clk, sinit);
  mms_reg_eta: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_Eta_defaults(mms_idx),16)),
                     -- "0000000000000001",  -- special
                 my_vme_base_address => LF_MMConfig_Eta_addr)
    port map (sMMConfig_eta, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(10), vme_en_out_i(10), clk, sinit);
  mms_reg_pt: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_Pt_defaults(mms_idx),16)),
                     -- "0000000000000100",  -- min pT
                 my_vme_base_address => LF_MMConfig_Pt_addr)
    port map (sMMConfig_pt, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(11), vme_en_out_i(11), clk, sinit);
  mms_reg_charge: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_Charge_defaults(mms_idx),16)),
                     -- "0000000000100000",  -- take DT
                 my_vme_base_address => LF_MMConfig_Charge_addr)
    port map (sMMConfig_charge, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(12), vme_en_out_i(12), clk, sinit);
  mms_reg_MIP: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_MIP_defaults(mms_idx),16)),
                     -- "0000000000000001",  -- special, OR
                 my_vme_base_address => LF_MMConfig_MIP_addr)
    port map (sMMConfig_MIP, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(13), vme_en_out_i(13), clk, sinit);
  mms_reg_ISO: entity work.VMEReg
    generic map (init_val            =>
                 std_logic_vector(to_unsigned(LF_MMConfig_ISO_defaults(mms_idx),16)),
                     -- "0000000001000001",  -- special, AND
                 my_vme_base_address => LF_MMConfig_ISO_addr)
    port map (sMMConfig_ISO, vme_addr, vme_data, vme_en, vme_wr, vme_data_out_i(14), vme_en_out_i(14), clk, sinit);

  oSpecialMIPMergeUseAND <= sMMConfig_MIP(6); 
  oSpecialISOMergeUseAND <= sMMConfig_ISO(6); 
  oHaloOverwritesMatched <= sMMConfig_SRK(6); 
  
  -----------------------------------------------------------------------------
  -- mux select matrix
  -----------------------------------------------------------------------------
  reg_result: process (clk) is
  begin  
    if clk'event and clk = calc_lf_edge(mms_lat_start + LF_RLAT_MMS_DONE) then 
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_SRK, oSelectMatrix_SRK, dummy1);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_phi, oSelectMatrix_phi, dummy2);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_eta, oSelectMatrix_eta, oSpecialBit_eta);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_pt,  oSelectMatrix_pt,  oSpecialBit_pt);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_charge, oSelectMatrix_charge, oSpecialBit_charge);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_MIP, oSelectMatrix_MIP, oSpecialBit_MIP);
      sel (sSelectMatrix_rank, sSelectMatrix_minpt, sSelectMatrix_combi, sMMConfig_ISO, oSelectMatrix_ISO, oSpecialBit_ISO);
    end if;
  end process reg_result;
  
  -- multiplex vme_data_output
  mux_vme(vme_data_out_i, vme_en_out_i, vme_data_out, vme_en_out);
  
end architecture behavioral;