--******************************************************************************
--* @short      Sort FPGA (there is only one, so this is the chip)
--******************************************************************************
--* @author  SAKULIN Hannes  <hsakulin@dsy-srv3.cern.ch>
--* @date    $Date: 2006/06/02 09:29:39 $
--* @version $Revision: 1.24 $
--******************************************************************************
-- Revision 1.19: feed 0001  22nov2005
-- Revision 1.20: feed 0002  30nov2005  receive bad LFB bit 56 via spare bit 104
-- Revision 1.21: feed 0003  23may2006  inverted GTLp outputs to GTL board
-- Revision 1.22: feed 0004  29may2006  exchanged muon1 & muon2 in SFDDROutput
-- Revision 1.23: feed 0005  01jun2006  SFDOutput with 80MHz OFD, DCM with clk80
--/
library IEEE;
use IEEE.Std_logic_1164.all;
use WORK.GMTTypes.all;
use WORK.ReadOutBufTypes.all;
use WORK.VMEMux.all;
use WORK.SFVMEAddrMap.all;
                          
use IEEE.NUMERIC_STD.all;

entity SortFPGA is
  port (                                -- CHIP INPUTS
    SortRanks_brl : in TSortRank_vector(0 to 3);
    SortRanks_fwd : in TSortRank_vector(0 to 3);

    OutMuons_brl : in TFourGMTMu_flat;
    OutMuons_fwd : in TFourGMTMu_flat;

    IdxBits_brl : in TIndexBits_vector(0 to 3);
    IdxBits_fwd : in TIndexBits_vector(0 to 3);

    IsRPCMu_brl : in std_logic_vector (0 to 3);
    IsRPCMu_fwd : in std_logic_vector (0 to 3);

    -- OUTPUTS (include two spare bits, each)
    Muon12 : out std_logic_vector(29 downto 0);
    Muon34 : out std_logic_vector(29 downto 0);    

    -- VME port
    vme_addr       : in    std_logic_vector(19 downto 1);
    vme_data       : inout std_logic_vector(15 downto 0);
    vme_en_SRT     : in    std_logic;
    vme_wr_SRT     : in    std_logic;
    vme_ndtack_SRT : out   std_logic;       -- low active 
    vme_nirq_SRT   : out   std_logic;       -- low active 


    -- Readout Data Port
    ro_data_SRT    : out std_logic_vector(23 downto 0);
    ro_rdfifo_SRT  : in std_logic;
    ro_fetch_SRT   : in std_logic;
    l1a_SRT        : in std_logic;          -- Level-1 accept
    bcreset_SRT    : in std_logic;          -- Bunch Counter reset
    l1reset_SRT    : in std_logic;          -- Level-1 reset
    
    -- Clock and control
    clk_SRT        : in  std_logic;
    clk_test_SRT   : out std_logic;     -- clock1x from DCM
    clk_out_SRT    : out std_logic;     -- clock output for feedback (use optional)
                                        -- same as clk_test
    clk_fb_SRT     : in  std_logic;     -- clock feedback input (use optional)
    dcm_locked_SRT : out std_logic;     -- DCM is locked => goes to VME chip
    reset_dcm      : in  std_logic;     -- crate reset (crate reset button and after
                                        -- power-up)  => resets DCM
    inactive       : in  std_logic;     -- from front panel button (to GTS)

    reset_SRT      : in  std_logic;     -- hard reset from trigger control
                                        --
                                        -- register input
                                        -- same function as level-1 reset
                                        -- or hard reset from trigger control
                                        --
                                        -- generated by VME chip
                                        -- resets only state machines, FFs,
                                        -- counters, error flags
                                        -- but not memories and registers
    status_SRT     : out std_logic_vector(1 downto 0);
    test_SRT       : out std_logic_vector(3 downto 0);

    dummyb : in std_logic);
end;


architecture behavioral of SortFPGA is
  attribute syn_useioff               : boolean;
  attribute syn_useioff of behavioral : architecture is true;

  constant date_string : string := "$Date: 2006/06/02 09:29:39 $";

  -- signal clk_frombuf   : std_logic;  -- not used by Hannes
  -- signal RST_i         : std_logic;  -- not used by Hannes
  signal clk, clk80    : std_logic;   --clk80 included 1.6.2006

  -- registered inputs
  signal sMuons_reg     : TGMTMu_vector(0 to 7);
  signal sSortRanks_reg : TSortRank_vector(0 to 7);

  signal sIdxBits_reg : TIndexBits_vector(0 to 7);
  signal sIsRPCMu_reg : std_logic_vector (0 to 7);

  signal reset : std_logic;

  -- output signals
  signal oMuons : TGMTMu_vector(0 to 3);
  signal sMuxMuons : TGMTMu_vector(0 to 3);
  signal sSimMuons0, sSimMuons1, sSimMuons2, sSimMuons3 : TGMTMu_flat;
  signal oIdxBits : TIndexBits_vector(0 to 3);
  signal oIsRPCMu : std_logic_vector(0 to 3);
  signal oIsFwd   : std_logic_vector(0 to 3);

  signal oSortRanks : TSortRank_vector(0 to 3);

  signal sEmpty : std_logic_vector(0 to 7);


  signal vme_addr_i : std_logic_vector (vme_addr'range);
  signal vme_data_i : std_logic_vector (vme_data'range);
  signal vme_en_i   : std_logic;
  signal vme_wr_i   : std_logic;

  -- VME data and enable from various units
  signal vme_data_out_i : TVMEData_vec (0 to 15);
  signal vme_en_out_i   : TVMEEnable_vec (0 to 15);

  -- VME data muxed
  signal vme_data_out_muxed : std_logic_vector (15 downto 0);
  signal vme_en_out_muxed   : std_logic;


  -- readout data 
  signal ro_rdfifo_reg  :  std_logic;
  signal ro_fetch_reg   : std_logic;
  signal l1a_reg        : std_logic;          -- Level-1 accept
  signal bcreset_reg    : std_logic;          -- Bunch Counter reset
  signal bcreset_or_dummy : std_logic;          -- Bunch Counter reset
  signal l1reset_reg    : std_logic;          -- Level-1 reset
  signal ro_data_i      : std_logic_vector(23 downto 0);
 

  -- readout data 
  signal readout_data     : TReadoutData_vec(0 to 13);
  signal spy_data         : TReadoutData_vec(0 to 4);
  signal sSimulatedData   : TReadoutData_vec(0 to 4);
  signal mon_data         : TReadoutMonData_vec(0 to 29);  -- 2x15 words
  signal sSimuMode        : std_logic;
  signal sDummyIsBCReset  : std_logic;

  signal sBCReset_pre     : std_logic;
  signal sBCReset_main    : std_logic;
  signal sBXCount : std_logic_vector(11 downto 0);  -- the bx count

  -- signal addrb_i : std_logic_vector(9 downto 0);  -- not used by Hannes
  signal sDummy       : std_logic_vector(15 downto 0);

  signal dummy_reg   : std_logic;
  signal dummy_reg_d : std_logic;
  signal dummy_pulse : std_logic;
  
  signal clk_fb_reg : std_logic;

  signal sOutMuons_brl_104_reg : std_logic;
  signal sOutMuons_fwd_104_reg : std_logic;
  signal OutMuons_brl_reg : TFourGMTMu_flat;  -- 30nov2005 input registers in 'flat'format

begin

-------------------------------------------------------------------------------
-- register inputs
-------------------------------------------------------------------------------

  -- muons
  register_inputs : process (clk, reset) is
  begin
    if reset = '1' then
      ro_rdfifo_reg <= '0';
      ro_fetch_reg  <= '0';      
      dummy_reg   <= '0';
      dummy_reg_d <= '0';
    elsif (clk'event and clk = '1') then
      sSortRanks_reg(0 to 3) <= SortRanks_brl;
      sSortRanks_reg(4 to 7) <= SortRanks_fwd;
      
      sMuons_reg(4 to 7) <= GMTMu_vec_from_flat( OutMuons_fwd );
   -- 30nov2005: replace original registers by the loop below
   -- sMuons_reg(0 to 3) <= GMTMu_vec_from_flat( OutMuons_brl );

   -- sOutMuons_brl_104_reg <= OutMuons_brl(104); 
      sOutMuons_fwd_104_reg <= OutMuons_fwd(104); 
      
      sIdxBits_reg(0 to 3) <= IdxBits_brl(0 to 3);
      sIdxBits_reg(4 to 7) <= IdxBits_fwd(0 to 3);

      sIsRPCMu_reg(0 to 3) <= IsRPCMu_brl(0 to 3);
      sIsRPCMu_reg(4 to 7) <= IsRPCMu_fwd(0 to 3);
      
      ro_rdfifo_reg <= ro_rdfifo_SRT;
      ro_fetch_reg  <= ro_fetch_SRT;

      clk_fb_reg <= clk_fb_SRT;
      dummy_reg <= dummyb;
      dummy_reg_d <= dummy_reg;
      
      -- 30nov2005: Connect spare bit 104 instead of bad bit 56
      for i in 0 to 103 loop
        if (i=56) then
	  OutMuons_brl_reg(56) <= OutMuons_brl(104);
	else
	  OutMuons_brl_reg(i) <= OutMuons_brl(i);
        end if;
      end loop;
      -- 30nov2005: Also keep bad bit 56 and connect it to dummy
      sOutMuons_brl_104_reg <= OutMuons_brl(56);
      
    end if;
  end process register_inputs;
  
  -- 30nov2005: Convert registered barrel muons to vector format
        sMuons_reg(0 to 3) <= GMTMu_vec_from_flat( OutMuons_brl_reg );

   -- Dummy pulse
  dummy_pulse <= dummy_reg and (not dummy_reg_d);
  
  

  check_empty : for i in 0 to 7 generate
    sEmpty(i) <= '1'when sMuons_reg(i).pt = "00000" else '0';
  end generate;  -- i

  -----------------------------------------------------------------------------
  -- Sorter
  -----------------------------------------------------------------------------

  sorter : entity work.SFSortStage2
    port map (
      iSortRanks => sSortRanks_reg,
      iEmpty     => sEmpty,
      iMuons     => sMuons_reg,
      oMuons     => oMuons,

      iIdxBits   => sIdxBits_reg,
      oIdxBits   => oIdxBits,
      iIsRPCMu   => sIsRPCMu_reg,
      oIsRPCMu   => oIsRPCMu,
      oIsFwd     => oIsFwd,
      oSortRanks => oSortRanks,
      
      clk   => clk,
      sinit => reset);


  -----------------------------------------------------------------------------
  -- Mux simulated data with muons
  -----------------------------------------------------------------------------
  muxProc:  process(sSimulatedData)
  begin
    for i in 0 to 25 loop
      sSimMuons0(i) <= sSimulatedData(0)(i);  -- convert TReadoutData_vec to
                                              -- TGMTMu_flat
      sSimMuons1(i) <= sSimulatedData(1)(i);
      sSimMuons2(i) <= sSimulatedData(2)(i);
      sSimMuons3(i) <= sSimulatedData(3)(i);
    end loop;
  end process muxProc;

  sMuxMuons(0) <= GMTMu_from_flat(sSimMuons0) when (sSimuMode = '1') else oMuons(0);
  sMuxMuons(1) <= GMTMu_from_flat(sSimMuons1) when (sSimuMode = '1') else oMuons(1);
  sMuxMuons(2) <= GMTMu_from_flat(sSimMuons2) when (sSimuMode = '1') else oMuons(2);
  sMuxMuons(3) <= GMTMu_from_flat(sSimMuons3) when (sSimuMode = '1') else oMuons(3);

  

  -----------------------------------------------------------------------------
  -- Double Data Rate Outputs
  -----------------------------------------------------------------------------
  -- 80 MHz FD added to receive muons correctly in GTL board
  -- SFDOutput works without DDR regs
  ddr_12 : entity work.SFDOutput
    port map (
      iMuon1 => sMuxMuons(0),
      iMuon2 => sMuxMuons(1),
      oMuon  => Muon12(25 downto 0),
      clk    => clk,
      clk80  => clk80);

  ddr_34 : entity work.SFDOutput
    port map (
      iMuon1 => sMuxMuons(2),
      iMuon2 => sMuxMuons(3),
      oMuon  => Muon34(25 downto 0),
      clk    => clk,
      clk80  => clk80);
  ---------------------------------
  -- Original code from Hannes S. with Double-Rate Registers
  --  ddr_12 : entity work.SFDDROutput
  --    port map (
  --      iMuon1 => sMuxMuons(0),
  --      iMuon2 => sMuxMuons(1),
  --      oMuon  => Muon12(25 downto 0),
  --      clk    => clk);

  --  ddr_34 : entity work.SFDDROutput
  --    port map (
  --      iMuon1 => sMuxMuons(2),
  --      iMuon2 => sMuxMuons(3),
  --      oMuon  => Muon34(25 downto 0),
  --      clk    => clk);
  -------------------------------------
  -- spare outputs
  Muon12 (29 downto 26) <= "0000";
  Muon34 (29 downto 26) <= "0000";


  -----------------------------------------------------------------------------
  -- register and decode TCS Signals
  -----------------------------------------------------------------------------

  tcs_decoder: entity work.TCSCommandDecoder
    port map (
      iL1A     => l1a_SRT,
      iBCReset => bcreset_SRT,
      iL1Reset => l1reset_SRT,

      oL1A     => l1a_reg,
      oBCReset => bcreset_reg,
      oL1Reset => l1reset_reg,
      oECReset => open,                 -- no event counter 
      
      clk      => clk,
      reset    => reset);
  

  
  -----------------------------------------------------------------------------
  -- generate BCreset (dummy_pulse for testing, otherwise external bcreset)
  -----------------------------------------------------------------------------
  bcreset_or_dummy <= dummy_pulse when sDummyIsBCReset = '1' else
                      bcreset_reg;
  

  -----------------------------------------------------------------------------
  -- BX counter
  -----------------------------------------------------------------------------

  bxcounter : entity work.BXCounter
    generic map (
      reg_addr         => SF_ReadoutSyncReg_addr,
      bcreset_delay_reg_default => "0000000000010101")
    port map (
      iBCReset         => bcreset_or_dummy,

      oPreBCReset      => sBCReset_pre,
      oBCReset         => sBCReset_main,
      oBXCounter       => sBXCount,

      vme_addr     => vme_addr_i,
      vme_data     => vme_data_i,
      vme_en       => vme_en_i,
      vme_wr       => vme_wr_i,
      vme_data_out => vme_data_out_i(0),
      vme_en_out   => vme_en_out_i(0),

      clk   => clk,
      reset => reset);
  
  -----------------------------------------------------------------------------
  -- Readout logic
  -----------------------------------------------------------------------------

  -- 14x 32bit readout data and (14+1)*2 = 30 registers
  -- 30x 8bit monitoring data
  
  -- assign readout data
  readout_muons : for i in 0 to 7 generate
  begin  -- generate i
    readout_data(i)(31)  <= sIsRPCMu_reg(i);
    readout_data(i)(30)  <= '0' when (i < 4) else '1';
    readout_data(i)(29 downto 26) <= sIdxBits_reg(i);
    readout_data(i)(25 downto 0)  <= GMTMu_to_flat(sMuons_reg(i));
  end generate readout_muons;

  gmt_muons: for i in 0 to 3 generate
  begin  -- generate gmt_muons
    readout_data(i+8)(31)  <= oIsRPCMu(i);
    readout_data(i+8)(30)  <= oIsFwd(i);
    readout_data(i+8)(29 downto 26) <= oIdxBits(i);
    readout_data(i+8)(25 downto 0) <= gmtmu_to_flat( oMuons(i) );
  end generate gmt_muons;
  
  readout_sortranks: for i in 0 to 3 generate
  begin  -- generate readout_sortranks
    readout_data(12)((i+1)*8-1 downto i*8) <= sSortRanks_reg(i);
    readout_data(13)((i+1)*8-1 downto i*8) <= sSortRanks_reg(i+4);
  end generate readout_sortranks;

  mon: for i in mon_data'range generate
  begin  -- generate mon_data
    mon_data(i) <= (others => '0');
  end generate mon;
  


  readout_logic: entity work.ReadOutLogic
    generic map (
      nwords => 14,
      lat_delay_reg_addr => SF_LATDelayReg_addr)
    port map (
      data           => readout_data,
      mondata        => mon_data,
      
      iBXCount       => sBXCount,
      iBCReset       => bcreset_or_dummy,
      iL1A           => l1a_reg,
      iL1Reset       => l1reset_reg,
      ro_data        => ro_data_i,
      ro_rdfifo      => ro_rdfifo_reg,
      ro_fetch       => ro_fetch_reg,
      
      vme_addr     => vme_addr_i,
      vme_data     => vme_data_i,
      vme_en       => vme_en_i,
      vme_wr       => vme_wr_i,
      vme_data_out => vme_data_out_i(2),
      vme_en_out   => vme_en_out_i(2),

      clk            => clk,
      reset          => reset);

  -----------------------------------------------------------------------------
  -- Simulation / Spy Logic
  -----------------------------------------------------------------------------

  --FIXME: in order to get the chip done, spy only one muon with a 4k buffer
  --(this needs 8 out of the remaining 11 block rams in an XC2V2000)
  -- In future need to change. (or move to an XC2V3000 chip)

  make_spy_data: for i in 0 to 3 generate
  begin  -- generate make_spy_data
    spy_data(i)(31)  <= oIsRPCMu(i);
    spy_data(i)(30)  <= oIsFwd(i);
    spy_data(i)(29 downto 26) <= oIdxBits(i);
    spy_data(i)(25 downto 0) <= gmtmu_to_flat( oMuons(i) );
  end generate make_spy_data;


  spy_data(4) <= oSortRanks(3) & oSortRanks(2) & oSortRanks(1) & oSortRanks(0);
  
  simuspy_logic: entity work.SimuSpyLogic
    generic map (
      depth1k                     => 1,
      nwords                      => 5,
      simuspyconfig_reg_addr      => SF_SimuSpyConfig_addr,
      spydepth_reg_addr           => SF_SpyDepth_addr,
      spyarmpulse_reg_addr        => SF_SpyArmPulse_waddr,
      spydone_reg_addr            => SF_SpyDone_raddr,
      simuspyram_vme_base_address => SF_SimuSpyRAM_base)
    port map (
      iData        => spy_data,
      
      iPreBCRes    => sBCReset_pre,
      iBCRes       => sBCReset_main,
      oData        => sSimulatedData,
      oSimuMode    => sSimuMode,
      oDummyIsBCReset => sDummyIsBCReset,
      
      vme_addr     => vme_addr_i,
      vme_data     => vme_data_i,
      vme_en       => vme_en_i,
      vme_wr       => vme_wr_i,
      vme_data_out => vme_data_out_i(1),
      vme_en_out   => vme_en_out_i(1),
      
      clk          => clk,
      reset        => reset);


  --TBD: use oSimuMode and oSimulatedData
  

  -----------------------------------------------------------------------------
  -- Chip ID and Revision
  -- Revision 1.19: feed 0001  22nov2005
  -- Revision 1.20: feed 0002  30nov2005 
  -- Revision 1.21: feed 0003  23may2006
  -- Revision 1.22: feed 0004  29may2006
  -- Revision 1.23: feed 0005  01jun2006
  -----------------------------------------------------------------------------

  ChipID: entity work.ChipIDRegisters
    generic map (
      idregisters_vme_base_address => SF_chip_id0_raddr,

      chipID_h  => 16#0001#,  chipID_l  => 16#9161#,
      chipRev_h => 16#FEED#,  chipRev_l => 16#0005#,
      date_string => date_string)

    port map (
      vme_addr     => vme_addr_i,
      vme_en       => vme_en_i,
      vme_wr       => vme_wr_i,
      vme_data_out => vme_data_out_i(3),
      vme_en_out   => vme_en_out_i(3),
      vme_clk      => clk,
      reset        => reset);

  -----------------------------------------------------------------------------
  -- Dummy signals
  -----------------------------------------------------------------------------
  register_dummy: process (clk, reset) is
  begin  -- process register_dummy
    if reset = '1' then
       sDummy(0) <= '0';
    else  
       if clk'event and clk = '1' then  -- rising clock edge
         sDummy(0) <=  clk_fb_reg or sOutMuons_brl_104_reg or sOutMuons_fwd_104_reg;
       end if;
    end if;
  end process register_dummy;
  
  sDummy(15 downto 1) <= (others => '0');
  -----------------------------------------------------------------------------
  --* Dummy register
  -----------------------------------------------------------------------------
  dummy_register: entity work.VMEStatusReg
    generic map (
      my_vme_base_address => SF_dummy_raddr)
    port map (
      data         => sDummy,
      vme_addr     => vme_addr_i,
      vme_en       => vme_en_i,
      vme_wr       => vme_wr_i,
      vme_data_out => vme_data_out_i(4),
      vme_en_out   => vme_en_out_i(4),
      vme_clk      => clk,
      reset        => reset);

--FIXME
  dummyvme : for i in 5 to 15 generate
  begin  -- generate dummyvme
    vme_data_out_i(i) <= (others => '0');
    vme_en_out_i(i)   <= '0';
  end generate dummyvme;

  -------------------------------------------------------------------------------
  -- multiplex VME outputs
  -------------------------------------------------------------------------------
  mux_vme(vme_data_out_i, vme_en_out_i, vme_data_out_muxed, vme_en_out_muxed);

  -------------------------------------------------------------------------------
  -- register outputs
  -------------------------------------------------------------------------------

  register_outputs : process (clk) is
  begin
    if (clk'event and clk = '1') then
      ro_data_SRT <= ro_data_i;
    end if;
  end process register_outputs;

  -----------------------------------------------------------------------------
  -- VME Logic
  -----------------------------------------------------------------------------

  vme_logic: entity work.InChipVMELogic
    port map (
      vme_addr     => vme_addr,
      vme_data     => vme_data,
      vme_en       => vme_en_SRT,
      vme_wr       => vme_wr_SRT,
      vme_ndtack   => vme_ndtack_SRT,
      vme_nirq     => vme_nirq_SRT,
      
      vme_addr_i   => vme_addr_i,
      vme_data_i   => vme_data_i,
      vme_en_i     => vme_en_i,
      vme_wr_i     => vme_wr_i,
      
      vme_data_out => vme_data_out_muxed,
      vme_en_out   => vme_en_out_muxed,
      
      clk          => clk,
      reset        => reset);
  
  -------------------------------------------------------------------------------
  -- Digital Clock Management & Chip Startup
  -------------------------------------------------------------------------------
-- 1.6.2006: A.T.: Changed to 80 MHz DCM unit
  startup_dcm: entity work.StartupDCM80Virtex2
    port map (
      iResetToGSR    => reset_SRT,
      iTristateToGTS => inactive,
      
      oResetNet      => reset,
      
      iClkFromPAD    => clk_SRT,
      oClkNet        => clk,
      oClk80         => clk80,
      iResetDCM      => reset_dcm,
      oDCMLocked     => dcm_locked_SRT);

-- Original code from Hannes without 80 MHz clock
--  startup_dcm: entity work.StartupDCMVirtex2
--    port map (
--      iResetToGSR    => reset_SRT,
--      iTristateToGTS => inactive,     
--      oResetNet      => reset,
--      iClkFromPAD    => clk_SRT,
--      oClkNet        => clk,
--      iResetDCM      => reset_dcm,
--      oDCMLocked     => dcm_locked_SRT);

  -- output clock on two pins
  clk_out_SRT  <= clk;
  clk_test_SRT <= clk80;   -- 1.6.2006 A.T: clk80 goes to test point


  status_SRT <= "00";                   --TODO!
  test_SRT(0) <= sSimuMode;
  test_SRT(1) <= sSimMuons0(0);
  test_SRT(3) <= oMuons(0).phi(0);
  test_SRT(2) <= sMuxMuons(0).phi(0);  
end architecture behavioral;