--******************************************************************************
--* @short      Single Assignment Unit (for 1 muon) in MIAU Chip
--******************************************************************************
--* @author  SAKULIN Hannes  <hsakulin@dsy-srv3.cern.ch>
--* @version $Revision: 1.4 $
--* @date    $Date: 2005/01/13 08:39:12 $
--******************************************************************************
--/

library IEEE;
use IEEE.Std_logic_1164.all;
use WORK.GMTTypes.all;
use work.MIAUVMEAddrMap.all;
use work.MIAUTiming.all;

entity MIAUSingleAssUnit is
  generic (Neta : integer;
           instance_idx : integer;      -- 0 to 7
           idxmuon : integer);
  port (iMuon          : in TSyncedMu;
 	iQuietBits     : in TCaloRing_vector (0 to Neta-1);
        oIsISO         : out std_logic;

-- synthesis translate_off
        oPhiSelBits : out TPhiSelBits;
        oEtaSelBits : out TEtaSelBits;
-- synthesis translate_on

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

architecture behavioral of MIAUSingleAssUnit is
  attribute syn_useioff : boolean;
  attribute syn_useioff of behavioral : architecture is true;
  
  component MIAUPhiProUnit is
    port (iCh            : in std_logic;                      -- charge
          iPt            : in std_logic_vector (4 downto 0);  -- 5 bit pT
	  iEta           : in std_logic_vector (5 downto 0);  -- 6 bit eta
  	  iPhi           : in std_logic_vector (7 downto 0);  -- 6 bit phi
	  oPhiSelectBits : out std_logic_vector (17 downto 0) );  -- phi select bits
  end component;

  signal sPhiSelectBits : std_logic_vector (17 downto 0);
  
  signal s1PhiSelectBits : std_logic_vector (17 downto 0);
  signal sEtaSelectBits : std_logic_vector (Neta-1 downto 0);
  signal sEtaSelectBitsBIG : std_logic_vector (9 downto 0);
  signal s1EtaSelectBits : std_logic_vector (Neta-1 downto 0);
  signal s1QuietBits     : TCaloRing_vector (0 to Neta-1);

  signal charge_i : std_logic_vector (0 downto 0);
  
  signal vme_data_out_i   : vme_dout_vec (0 to 3);
  signal vme_en_out_i     : vme_enout_vec (0 to 3);

  signal a1 : std_logic_vector(0 to 18*Neta-1);
  signal a2 : std_logic_vector(0 to 47);
  signal a3 : std_logic_vector(0 to 11);
  signal a4 : std_logic_vector(0 to 2);

  signal any_selected : std_logic;

-- very important attributes (otherwise synplify optimizes the AND144 for area - see below)
attribute syn_keep : boolean;
attribute syn_keep of a1:signal is true;
--attribute syn_keep of a2:signal is true;
attribute syn_keep of a3:signal is true;
--attribute syn_keep of a4:signal is true;

begin

  thePhiProUnit: entity work.MIAUPhiProUnit
    generic map (
      instance_idx => instance_idx,
      idxmuon      => idxmuon)
    port map (
      iCh            => iMuon.charge,
      iPt            => iMuon.pt,
      iEta           => iMuon.eta,
      iPhi           => iMuon.phi,
      oPhiSelectBits => sPhiSelectBits,
      
      vme_addr       => vme_addr,
      vme_data       => vme_data,
      vme_en         => vme_en,
      vme_wr         => vme_wr,

      vme_data_out => vme_data_out_i(0 to 2),
      vme_en_out => vme_en_out_i(0 to 2),
      
      clk            => clk,
      sinit          => sinit);
  
  
  charge_i(0) <= iMuon.charge;          -- convert to 1-element vector

  EtaProLUT: entity work.miauetaprolut
    generic map (
      instance_idx        => instance_idx,
      my_vme_base_address => MIAU_EtaProLUT_base(instance_idx mod 4) + idxmuon * MIAU_EtaProLUT_size) 
                            -- 4k per muon
    port map (
      eta      => iMuon.eta,
      pt       => iMuon.pt,
      charge   => charge_i,
      
      eta_sel  => sEtaSelectBitsBIG,
      
      vme_addr => vme_addr,
      vme_data => vme_data,
      vme_en   => vme_en,
      vme_wr   => vme_wr,
      vme_data_out => vme_data_out_i(3),
      vme_en_out => vme_en_out_i(3),
      vme_clk  => clk,
     
      clk      => clk,
      sinit    => sinit);

  sEtaSelectBits <= sEtaSelectBitsBIG (Neta-1 downto 0);

  -- multiplex vme_data_output
  vme_data_out <= vme_data_out_i(0) when vme_en_out_i(0) = '1' else
                  vme_data_out_i(1) when vme_en_out_i(1) = '1' else
                  vme_data_out_i(2) when vme_en_out_i(2) = '1' else
                  vme_data_out_i(3);

  vme_en_out <= vme_en_out_i(0) or
                vme_en_out_i(1) or
                vme_en_out_i(2) or
                vme_en_out_i(3);                 
  
  s1PhiSelectBits <= sPhiSelectBits;
  s1QuietBits <= iQuietBits;
  s1EtaSelectBits <= sEtaSelectBits;


-- notes on implementation of AND144:
--
-- synpilfy will optimize for area independent of the vhdl coding and generate
-- at least 7 levels of logic instead of the required 5 levels. All the below
-- coding styles are optimized in the same way:
--   - loop with variables
--   - loop with variables that can be matched to 4-input luts 
--   - generate of 4-input ANDs
--   - generate of 4-input ANDs inside processes
--
-- synplify will optimize for area even with strong timing constraints
--   - high frequency (100 MHz)
--   - reg_input_delay of 5 or 3 ns
--
-- only way to force syplify not to optmize for area seems to force it to keep
-- the intermediate signals with syn_keep (see above).
--

  -- check if any region is selected at all (if not => empty muon)
  any_selected <= '1' when s1PhiSelectBits /= "000000000000000000" else '0';

  L1: for i1 in 0 to 18*Neta-1 generate
  begin  -- generate L1
    a1(i1) <= (s1PhiSelectBits(i1 mod 18) and s1EtaSelectBits(i1 / 18) and s1QuietBits(i1 / 18)(i1 mod 18) ) or
              (not (s1PhiSelectBits(i1 mod 18) and s1EtaSelectBits(i1 / 18))) ;
  end generate L1;

  L2: for i2 in 0 to 9*Neta/2-1 generate
  begin  -- generate L2
    p2: process (a1(4*i2), a1(4*i2+1), a1(4*i2+2), a1(4*i2+3)) is
    begin  -- process p2
      a2(i2) <= a1(4*i2) and a1(4*i2+1) and a1(4*i2+2) and a1(4*i2+3);     
    end process p2;
  end generate L2;

  L2A: for i2 in 9*Neta/2 to 47 generate
  begin  -- generate L2A
    a2(i2) <= '1';
  end generate L2A;

  L3: for i3 in 0 to 11 generate
  begin  -- generate L3
    p3: process (a2(4*i3), a2(4*i3+1), a2(4*i3+2), a2(4*i3+3)) is
    begin                                                              
      a3(i3) <= a2(4*i3) and a2(4*i3+1) and a2(4*i3+2) and a2(4*i3+3);
    end process p3;
  end generate L3;
  
  L4: for i4 in 0 to 2 generate
  begin  -- generate L4
    p4: process (a3(4*i4), a3(4*i4+1), a3(4*i4+2), a3(4*i4+3)) is
    begin                                                                 
      a4(i4) <= a3(4*i4) and a3(4*i4+1) and a3(4*i4+2) and a3(4*i4+3);
    end process p4;
  end generate L4;
 
  -- select logic
  process (clk)
  begin
    if clk'event and clk = MIAU_EDGE_OUTPUT then 

-- synthesis translate_off
      oPhiSelBits <= sPhiSelectBits;
      oEtaSelBits <= (others => '0');      
      oEtaSelBits(Neta-1 downto 0) <= sEtaSelectBits;
-- synthesis translate_on

    end if;
  end process;

  oIsISO <= a4(0) and a4(1) and a4(2) and any_selected; -- a4(3);

--
-- alternate implementation that results in more stages of logic generated by synplify
--
  
--   -- select logic
--   process (clk)
--     variable tmpISO : std_logic;
--     variable selected : std_logic;
--     variable isISO : std_logic;
-- --     variable a1 : std_logic_vector(0 to 18*Neta-1);
-- --     variable a2 : std_logic_vector(0 to 63);
-- --     variable a3 : std_logic_vector(0 to 15);
-- --     variable a4 : std_logic_vector(0 to 3);
-- --     variable i : integer; 
--   begin
--     if clk'event and clk = MIAU_EDGE_OUTPUT then 
--       tmpISO := '1';
--       for iphi in 0 to 17 loop
--         for ieta in 0 to Neta-1 loop
-- --	  if ( s1PhiSelectBits(iphi) = '1' ) and ( s1EtaSelectBits(ieta) = '1' ) then
--             selected := s1PhiSelectBits(iphi) and s1EtaSelectBits(ieta);
--             isISO := ( selected and s1QuietBits(ieta)(iphi) ) or (not selected) ;
-- 	    tmpISO := tmpISO and isISO ;
-- --  	  end if;
--         end loop;	
--       end loop;
--       oIsISO <= tmpISO and any_selected;
-- -- synthesis translate_off
--       oPhiSelBits <= sPhiSelectBits;
--       oEtaSelBits <= (others => '0');      
--       oEtaSelBits(Neta-1 downto 0) <= sEtaSelectBits;
-- -- synthesis translate_on
--     end if;
--   end process;       
      

  
end architecture behavioral;