diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/CMakeLists.txt b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/CMakeLists.txt index 106b7ab0412d9e2635c2e56cfe2983a915fd1972..255407b674e81c18cce6734223cb58e08e564439 100644 --- a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/CMakeLists.txt +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/CMakeLists.txt @@ -10,7 +10,9 @@ find_package( tdaq-common COMPONENTS eformat_write ) atlas_add_component( L1CaloFEXByteStream src/*.cxx src/components/*.cxx - INCLUDE_DIRS ${TDAQ-COMMON_INCLUDE_DIRS} + src/bytestreamDecoder/src/*.cxx + DEFINITIONS OFFLINE_DECODER + INCLUDE_DIRS ${TDAQ-COMMON_INCLUDE_DIRS} src/bytestreamDecoder LINK_LIBRARIES TrigT1ResultByteStreamLib xAODTrigger ${TDAQ-COMMON_LIBRARIES} ) # Install files from the package: diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/python/L1CaloFEXByteStreamConfig.py b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/python/L1CaloFEXByteStreamConfig.py index 744e24704c8be6a3232afb53114684b97ed7ffa8..8d610ddf80932b26ca744e4c7bbae5d4fe84e828 100644 --- a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/python/L1CaloFEXByteStreamConfig.py +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/python/L1CaloFEXByteStreamConfig.py @@ -4,6 +4,37 @@ from AthenaConfiguration.ComponentFactory import CompFactory from libpyeformat_helper import SourceIdentifier, SubDetector +def eFexByteStreamToolCfg(name, flags, writeBS=False): + tool = CompFactory.eFexByteStreamTool(name) + efex_roi_moduleids = [0x1000,0x1100] + tool.ROBIDs = [int(SourceIdentifier(SubDetector.TDAQ_CALO_FEAT_EXTRACT_ROI, moduleid)) for moduleid in efex_roi_moduleids] + if writeBS: + # write BS == read xAOD + tool.eEMTOBContainerReadKey ="L1_eFexEMTOB" + tool.eTAUTOBContainerReadKey ="L1_eFexTauRoI" + tool.eEMxTOBContainerReadKey="L1_eFexEMxRoI" + tool.eTAUxTOBContainerReadKey ="L1_eFexTauxRoI" + + + tool.eEMTOBContainerWriteKey ="" + tool.eTAUTOBContainerWriteKey ="" + tool.eEMxTOBContainerWriteKey="" + tool.eTAUxTOBContainerWriteKey ="" + else: + # read BS == write xAOD + tool.eEMTOBContainerReadKey ="" + tool.eTAUTOBContainerReadKey ="" + tool.eEMxTOBContainerReadKey="" + tool.eTAUxTOBContainerReadKey ="" + + + tool.eEMTOBContainerWriteKey ="L1_eFexEMRoI" + tool.eTAUTOBContainerWriteKey ="L1_eFexTauRoI" + tool.eEMxTOBContainerWriteKey="L1_eFexEMxRoI" + tool.eTAUxTOBContainerWriteKey ="L1_eFexTauxRoI" + + return tool + def jFexByteStreamToolCfg(name, flags, writeBS=False): tool = CompFactory.jFexByteStreamTool(name) diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/README.md b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c34ab583b8b540c2354196a886e076ce44e87d8b --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/README.md @@ -0,0 +1 @@ +These files are copied from https://gitlab.cern.ch/atlas-l1calo-online/bytestreamDecoder/-/tree/offline - should not be modified by humans in this repo, any changes should be made in the online software repo instead. \ No newline at end of file diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderRun3.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderRun3.h new file mode 100644 index 0000000000000000000000000000000000000000..deac8fc4a63c99d9bd8990321de715d1ddf20924 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderRun3.h @@ -0,0 +1,111 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_BS_DECODER_RUN3_H +#define L1CALO_BS_DECODER_RUN3_H + +#include <list> +#include <stdint.h> + +#include "bytestreamDecoder/L1CaloRdoFexTob.h" + +class L1CaloRdoEfexTob; +class L1CaloRdoEfexTower; +class L1CaloRdoGfexTob; +class L1CaloRdoGfexTower; +class L1CaloRdoJfexTob; +class L1CaloRdoJfexTower; +class L1CaloRdoMuonTob; // **FIXME** Different class for run 3? +class L1CaloRdoRodInfo; + +class L1CaloBsDecoderRun3 +{ +public: + L1CaloBsDecoderRun3(); +#ifndef OFFLINE_DECODER + void decodeEfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#endif + void decodeEfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#ifndef OFFLINE_DECODER + void decodeJfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoJfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + void decodeJfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoJfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + void decodeGfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoGfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + void decodeGfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoGfexTob>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + void decodePh1TopoData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTob>& etob, + std::list<L1CaloRdoJfexTob>& jtob, + std::list<L1CaloRdoGfexTob>& gtob, + std::list<L1CaloRdoMuonTob>& mtob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + //>>void decodePh1TopoHits( const uint32_t* beg, const uint32_t* end, + //>> std::list<L1CaloRdoPh1TopoHit>& dat, + //>> std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#endif + void setVerbosity( bool verbosity ); + +private: +#ifndef OFFLINE_DECODER + uint32_t decodeEfexDataChan( const uint32_t payload[], + const uint32_t efexNumber, + const uint32_t shelfNumber, + const uint32_t errorMask, + std::list<L1CaloRdoEfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#endif + bool decodeEfexTobSlice( const uint32_t payload[], size_t& index, + const uint32_t efexNumber, const uint32_t shelfNumber, + const uint32_t numSlices, const uint32_t errorMask, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#ifndef OFFLINE_DECODER + uint32_t decodeJfexDataChan( const uint32_t payload[], + const uint32_t jfexNumber, + const uint32_t fpgaNumber, + const uint32_t errorMask, + std::list<L1CaloRdoJfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + bool decodeJfexTobSlice( const uint32_t payload[], size_t& index, + const uint32_t jfexNumber, const uint32_t fpgaNumber, + const uint32_t sliceNumber, const uint32_t numSlices, + const uint32_t errorMask, + std::list<L1CaloRdoJfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + uint32_t decodeGfexDataChan( const uint32_t payload[], + const uint32_t fpgaNumber, + const uint32_t chanNumber, + const uint32_t errorMask, + std::list<L1CaloRdoGfexTower>& dat, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); +#endif + void decodeOneEfexTob( const uint32_t word[], const uint32_t shelfNumber, + const uint32_t efexNumber, const uint32_t fpgaNumber, + const uint32_t errorMask, + const uint32_t numSlices, const uint32_t sliceNum, + L1CaloRdoFexTob::TobType tobType, + L1CaloRdoFexTob::TobSource tobSource, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ); + + int m_verbosity; +}; + +#endif diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderUtil.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderUtil.h new file mode 100644 index 0000000000000000000000000000000000000000..1f2c28c0ef122d16633a11b2b3f88fdf30f10952 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloBsDecoderUtil.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_BS_DECODER_UTIL_H +#define L1CALO_BS_DECODER_UTIL_H + +#include <list> + +class L1CaloRdoRodInfo; + +namespace eformat { + template <class TPointer> class ROBFragment; +} + +class L1CaloBsDecoderUtil +{ +public: + + static void decodeRodInfo( const eformat::ROBFragment<const uint32_t*>* rod, + std::list<L1CaloRdoRodInfo>& dat ); + + template <typename Tar, typename Dat, typename Iter> + static Tar& findRdo( const Tar& target, Dat& data, Iter begin, Iter end ); + + template <typename Tar, typename Dat> + static Tar& findRdo( const Tar& target, Dat& data ); + +private: + L1CaloBsDecoderUtil(); +}; + +template <typename Tar, typename Dat, typename Iter> Tar& +L1CaloBsDecoderUtil::findRdo( const Tar& target, Dat& data, Iter begin, Iter end ) +{ + while ( (begin != end) && ( target < (*begin) ) ) + ++begin; + + if ( begin == end ) + return *(data.insert( end.base(), target )); + + if ( begin->sameDatum( target ) ) + return *begin; + + return *(data.insert( begin.base(), target )); +} + +template <typename Tar, typename Dat> Tar& +L1CaloBsDecoderUtil::findRdo( const Tar& target, Dat& data ) +{ + return findRdo( target, data, data.rbegin(), data.rend() ); +} + +#endif + diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdo.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdo.h new file mode 100644 index 0000000000000000000000000000000000000000..4a0f1ab172d44a65e6e319a3e286fd576fb7b6cd --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdo.h @@ -0,0 +1,92 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_RDO_H +#define L1CALO_RDO_H + +#include <vector> +#include <list> +#include <string> + +#ifndef OFFLINE_DECODER +#include "channelMappings/L1CaloDetectorRegion.h" +#endif + +class L1CaloRdoRodInfo; + +class L1CaloRdo +{ +public: + virtual ~L1CaloRdo(); + + int getCrate( ) const; + int getModule( ) const; + int getEta( ) const; + int getPhi( ) const; + int getLayer( ) const; + int getValue( size_t slice ) const; + int getValue( ) const; + int getFlag( size_t slice ) const; + int getFlag( ) const; + int getL1aPos( ) const; +#ifndef OFFLINE_DECODER + L1CaloDetectorRegion getRegion( ) const; +#else + void setWord0( uint32_t val, size_t slice ){ if(slice < m_word0s.size()) m_word0s[slice] = val; }; + void setWord1( uint32_t val, size_t slice ){ if(slice < m_word1s.size()) m_word1s[slice] = val; }; + uint32_t getWord0( size_t slice ) const { return (slice < m_word0s.size()) ? m_word0s[slice] : 0; }; + uint32_t getWord1( size_t slice ) const { return (slice < m_word1s.size()) ? m_word1s[slice] : 0; }; +#endif + const std::list<L1CaloRdoRodInfo>::const_iterator& getRodInfo( ) const; + int getModuleStatus( ) const; + + bool getModuleErrorGlinkParity( ) const; + bool getModuleErrorGlinkProtocol( ) const; + bool getModuleErrorBcnMismatch( ) const; + bool getModuleErrorFifoOverflow( ) const; + bool getModuleErrorSpecific( ) const; + bool getModuleErrorUnused( ) const; + bool getModuleErrorGlinkTimeout( ) const; + bool getModuleErrorGlinkDown( ) const; + + size_t numSlices( ) const; + bool sameDatum( const L1CaloRdo& rhs ) const; + + void setValue( int val, size_t slice ); + void setValue( int val ); + void setFlag( int flag, size_t slice ); + void setFlag( int flag ); + void setRodInfo( std::list<L1CaloRdoRodInfo>::const_iterator& rodInfo ); + + void info( ) const; + virtual void infoSpecific( ) const; + virtual std::string getType( ) const = 0; + +protected: + L1CaloRdo( int crate, int module, int eta, int phi, + int layer, int numSlices ); +#ifndef OFFLINE_DECODER + void setRegion( const L1CaloDetectorRegion& region ); +#endif +private: + L1CaloRdo( ); + + int m_crate; + int m_module; + int m_eta; + int m_phi; + int m_layer; + std::vector<int> m_vals; + std::vector<int> m_flags; + int m_l1aPos; +#ifndef OFFLINE_DECODER + L1CaloDetectorRegion m_region; +#else + std::vector<uint32_t> m_word0s,m_word1s; +#endif + std::list<L1CaloRdoRodInfo>::const_iterator m_rodInfo; +}; + +bool operator<(const L1CaloRdo& lhs, const L1CaloRdo& rhs); + +#endif diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoEfexTob.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoEfexTob.h new file mode 100644 index 0000000000000000000000000000000000000000..b06c4ceda94ba3fbc8dfc55c9059f555605b121d --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoEfexTob.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_RDO_EFEX_EMTAU_H +#define L1CALO_RDO_EFEX_EMTAU_H + +#include "bytestreamDecoder/L1CaloRdoFexTob.h" + +class L1CaloRdoEfexTob : public L1CaloRdoFexTob +{ +public: + L1CaloRdoEfexTob( int crate, int module, int eta, int phi, int numSlices, + TobType tobType, TobSource source, int id = 0, int fibre = 0, int tobSeq = 0 ); + + virtual void infoSpecific( ) const override; + + //??bool getOverflow( size_t slice ) const; + //??bool getOverflow( ) const; + + int getClusterEt( size_t slice ) const; + int getClusterEt( ) const; + int getIsol( size_t slice ) const; + int getIsol( ) const; + +private: +}; + +#endif diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoFexTob.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoFexTob.h new file mode 100644 index 0000000000000000000000000000000000000000..7ac9304f7ca332dee94d03cef30d3a67cf49cde9 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoFexTob.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_RDO_FEX_TOB_H +#define L1CALO_RDO_FEX_TOB_H + +#include <string> + +#include "bytestreamDecoder/L1CaloRdo.h" + +class L1CaloRdoFexTob : public L1CaloRdo +{ +public: + enum TobType { Invalid, EM, Tau, LargeJet, SmallJet, Energy, Muon }; + enum TobSource { EfexTob, EfexXtob, JfexTob, JfexXtob, GfexTob, GfexXtob, Ph1Topo }; + + L1CaloRdoFexTob( int crate, int module, int eta, int phi, int numSlices, + TobType tobType, TobSource source, int id = 0, int fibre = 0, int tobSeq = 0 ); + + bool sameDatum( const L1CaloRdoFexTob& rhs ) const; + virtual std::string getType( ) const override; + + TobType getTobType( ) const; + std::string getTobTypeString( ) const; + int getTobID( ) const; + bool getIsEM( ) const; + bool getIsTau( ) const; + bool getIsJet( ) const; + bool getIsLargeJet( ) const; + bool getIsSmallJet( ) const; + bool getIsEnergy( ) const; + bool getIsMuon( ) const; + + TobSource getTobSource( ) const; + std::string getTobSourceString( ) const; + bool getIsTob( ) const; + bool getIsXtob( ) const; + bool getIsEfex( ) const; + bool getIsJfex( ) const; + bool getIsGfex( ) const; + bool getIsTopo( ) const; + + int getFibre( ) const; + int getTobSeq( ) const; + +private: + TobType m_tobType; + TobSource m_source; + int m_id; + int m_fibre; + int m_tobSeq; +}; + +bool operator<(const L1CaloRdoFexTob& lhs, const L1CaloRdoFexTob& rhs); + +#endif diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoRodInfo.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoRodInfo.h new file mode 100644 index 0000000000000000000000000000000000000000..c24742df17b088fd7035bf2f2503d83834a56131 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/bytestreamDecoder/L1CaloRdoRodInfo.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#ifndef L1CALO_RDO_ROD_INFO_H +#define L1CALO_RDO_ROD_INFO_H + +#include <string> +#include <vector> + +class L1CaloRdoRodInfo +{ +public: + L1CaloRdoRodInfo( int system, int rod, int run, int bcnum, int triggerType, int detType, int version ); + std::string getType( ) const; + + void info( ) const; + int getMinorVersion( ) const; + bool getIsRun1( ) const; + bool getIsRun2( ) const; + bool getIsRun3( ) const; + int getSystemId( ) const; + int getRodId( ) const; + int getSourceId( ) const; + int getRunNumber( ) const; + int getBcNumber( ) const; + int getTriggerType( ) const; + + int getLevel1Id( ) const; + int getDataSize( ) const; + int getStepNumber( ) const; + int getOrbitCount( ) const; + + bool getBcnumMismatch( ) const; + bool getGlinkTimeout( ) const; + bool getDataTransportError( ) const; + bool getRodOverflow( ) const; + + bool getModuleLinkError( ) const; + bool getCmmParityError( ) const; + bool getGlinkError( ) const; + + bool getRoiOverflow( ) const; + + bool getTriggerTypeTimeout( ) const; + + int getModuleStatus( const int module ) const; + + void setLvl1Id( int lvl1 ); + void setSize( int size ); + void setStatus1( int status ); + void setStatus2( int status ); + void setModuleStatus( int module, int status ); + + // Minimum ROD minor version for Run 2. + static const unsigned int s_minRun2Version = 0x1004; + +private: + int m_system; + int m_rod; + int m_run; + int m_bcnum; + int m_triggerType; + int m_detType; + int m_lvl1; + int m_size; + int m_status1; + int m_status2; + int m_version; + std::vector<int> m_status; +}; + +#endif diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderRun3.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderRun3.cxx new file mode 100644 index 0000000000000000000000000000000000000000..96b6e46908eeaaecc0f211f2165b873703f5d164 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderRun3.cxx @@ -0,0 +1,1386 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include <algorithm> +#include <bitset> +#include <iostream> +#include <iomanip> +#include <map> +#include <vector> + +#include "eformat/ROBFragment.h" + +#include "bytestreamDecoder/L1CaloBsDecoderRun3.h" +#include "bytestreamDecoder/L1CaloBsDecoderUtil.h" +#include "bytestreamDecoder/L1CaloRdoEfexTob.h" +#ifndef OFFLINE_DECODER +#include "bytestreamDecoder/L1CaloRdoEfexTower.h" +#include "bytestreamDecoder/L1CaloRdoGfexTob.h" +#include "bytestreamDecoder/L1CaloRdoGfexTower.h" +#include "bytestreamDecoder/L1CaloRdoJfexTob.h" +#include "bytestreamDecoder/L1CaloRdoJfexTower.h" +#include "bytestreamDecoder/L1CaloRdoMuonTob.h" // **FIXME** Different class for run 3? +#include "channelMappings/EfexCellMapping.h" +#include "channelMappings/GfexCellMapping.h" +#include "channelMappings/JfexCellMapping.h" +#include "defsL1Calo/CrateDefs.h" +#include "defsL1Calo/EfexDefs.h" +#include "infraL1Calo/EfexLatomeFibrePacker.h" +#include "infraL1Calo/EfexTrexFibrePacker.h" +#include "infraL1Calo/GfexLatomeCentralFibrePacker.h" +#include "infraL1Calo/GfexLatomeForwardFibrePacker.h" +#include "infraL1Calo/GfexTrexFibrePacker.h" +#include "infraL1Calo/JfexLatomeFibrePacker.h" +#include "infraL1Calo/JfexTrexFibrePacker.h" +#endif + +#include "ers/ers.h" +#define ERS_ERROR(message) { std::stringstream s; s << __FUNCTION__ << " - " << message;ers::error(ers::Message(ERS_HERE, s.str()));} + + +/*! + * \class L1CaloBsDecoderRun3 + * Decoder for Run 3 data formats: eFEX, jFEX, gFEX and phase 1 Topo. + * Utility methods shared with Runs 1 & 2 are now in L1CaloBsDecoderUtil. + */ + +L1CaloBsDecoderRun3::L1CaloBsDecoderRun3() +: m_verbosity(0) +{ +#ifndef OFFLINE_DECODER + // Force initialisation of mapping tables. + EfexCellMapping dummyEfexMapping(0,0,0,0,0,0); + if (!dummyEfexMapping.getDetectorRegion().getValidity()) { + std::cerr << "L1CaloBsDecoderRun3::ctor: unexpected invalid eFEX mapping!?" << std::endl; + } + JfexCellMapping dummyJfexMapping(0,1,0,0); // Processor number 1-4 + if (!dummyJfexMapping.getDetectorRegion().getValidity()) { + std::cerr << "L1CaloBsDecoderRun3::ctor: unexpected invalid jFEX mapping!?" << std::endl; + } +#endif +} + +#ifndef OFFLINE_DECODER +/*! + * Decode eFEX input fibre data. + * For the EM layer we have up to 10 supercell values per tower + * though there is still just a single hadronic value. + * It seemed overkill to have an RDO per supercell, so instead + * the main RDO "Value" is set to the sum of 10 EM supercells + * with the individual supercell Ets saved in a separate vector. + * These are always stored in 1441 order (PS, F0-F3, M0-M3, BK) + * even in regions where some supercells are missing. + * The flag field is used for status/error bits. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeEfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + const size_t fragmentSize = end - beg; + + // The data block is optimised for production by firmware with the length + // in a trailer at the end. The SWROD concatenates a number of such blocks + // (removing the 2 word header used to fill the ROD fragment header). + // So we need to find the end and work backwards. + + size_t numTowers = tower.size(); + size_t index = fragmentSize; + + // Loop looking backwards for eFEX processor input data blocks. + while ( index > 0 ) { + if ( index < 4 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexData: remaining block size " << index + << " is too small for the eFEX FPGA trailer" << std::endl; + return; + } + const uint32_t ctrlTrailer2 = payload[--index]; + const uint32_t ctrlTrailer1 = payload[--index]; + const uint32_t fpgaTrailer2 = payload[--index]; + const uint32_t fpgaTrailer1 = payload[--index]; + + const uint32_t ctrlErrors = ctrlTrailer2 & 0x3f; + const uint32_t efexNumber = (ctrlTrailer1 >> 12) & 0xf; + const uint32_t shelfNumber = (ctrlTrailer1 >> 16) & 0x1; // Just use 1 bit from 4 bit field + const size_t payloadSize = (ctrlTrailer1 & 0xfff) - 2; + + if ( payloadSize > index ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexData: remaining eFEX block size " + << index << " is too small for the claimed payload size " + << payloadSize << std::endl; + return; + } + + if (efexNumber >= CrateDefs::numAtcaFexSlots() ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexData: invalid eFEX number " << efexNumber + << " (out of range 0-" << CrateDefs::numAtcaFexSlots()-1 << ")" << std::endl; + return; + } + + // We can now work forwards from the start of this block + // decoding each set of 8 words per input fibre channel. + // The payloadSize has had two trailer words subtracted. + index -= payloadSize; + size_t chanIndex = 0; + uint32_t chanErrorOR = 0; + std::bitset<49> chansWithError; + bool anyErrorBit = false; + while ( chanIndex < payloadSize ) { + if ( (payloadSize - chanIndex) < 8 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexData: crate " << shelfNumber << " efex " << efexNumber + << ": remaining eFEX block size " << (payloadSize - chanIndex) + << " is too small for one eFEX input fibre block (8)" << std::endl; + return; + } + const uint32_t chanNumber = payload[index+chanIndex+7] & 0xff; + const uint32_t chanErrors = this->decodeEfexDataChan ( &payload[index+chanIndex], efexNumber, shelfNumber, + ctrlErrors, tower, rodInfo ); + if ( chanErrors ) { + chanErrorOR |= ( chanErrors & 0x7 ); // Only three lowest bits get ORed + chansWithError[chanNumber] = 1; + anyErrorBit = true; + } + chanIndex += 8; + } + if ( anyErrorBit ) { + chanErrorOR |= 0x8; // Extra bit set if any of the three lower bits are set + } + const uint64_t fpgaErrorBits = ( (const uint64_t)(fpgaTrailer2 & 0x1ffff) << 32 ) | fpgaTrailer1; + const uint64_t chanErrorBits = chansWithError.to_ullong(); + const uint32_t fpgaErrorOR = ( fpgaTrailer2 >> 28 ) & 0xf; + if ( fpgaErrorBits != chanErrorBits || fpgaErrorOR != chanErrorOR ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexData: crate " << shelfNumber << " efex " << efexNumber + << ": mismatch between errors in FPGA trailer: " + << std::hex << fpgaErrorBits << " " << fpgaErrorOR + << " and those derived from channels: " << chanErrorBits << " " << chanErrorOR + << std::dec << std::endl; + } + } + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeEfexData: n.towers added=" + << tower.size() - numTowers << std::endl; + } +} + +/*! + * Decode the data from one eFEX input fibre (only ever one slice). + * \param payload payload vector starting at this 8 word block + * \param efexNumber number of this eFEX in its shelf + * \param shelfNumber shelf number + * \param errorMask global error bits set for this ROD fragment + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + * \return whether decoding succeeded (false to abort decoding) + */ +uint32_t +L1CaloBsDecoderRun3::decodeEfexDataChan( const uint32_t payload[], + const uint32_t efexNumber, const uint32_t shelfNumber, + const uint32_t errorMask, + std::list<L1CaloRdoEfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + // The EM and hadronic fibres have different encoding and cover + // different numbers of towers with more or less granularity. + // Channel numbers 0-39 are EM, 40-48 are hadronic. + const uint32_t chanNumber = ( payload[7] ) & 0xff; + const uint32_t fpgaNumber = ( payload[7] >> 8 ) & 0x3; + const uint32_t errorBits = ( payload[7] >> 28 ) & 0xf; + if ((int)chanNumber >= EfexDefs::numInputFibresPerFpga()) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexDataChan: invalid channel " << chanNumber + << " (out of range 0-" << EfexDefs::numInputFibresPerFpga()-1 << ")" << std::endl; + return 0xffffffff; + } + + // The EfexCellMapping returns global coordinates but the RDO objects + // expect local eta,phi within the module. So prepare these here. + // Find local eta,phi from global EfexCellMapping and module offset. + // There is no good way of handling the localEta. Below we add one + // so the barrel eta has core towers in localEta 1-16 with 0 and 17 + // being core towers on C and A sides. Overlap towers extend further + // and both localEta and localPhi may be negative. + int moduleEta = -24 + (efexNumber % 3) * 16; + int modulePhi = 2 + (shelfNumber * 32) + (efexNumber / 3) * 8; + + // The fibre packer decoder methods expect a vector of seven words. + std::vector<FibrePackerBase::myDataWord> encodedData; + for ( size_t i = 0; i < 7; i++ ) { + encodedData.push_back( payload[i] ); + } + const FibrePackerBase::InputDataFrameType frameType( FibrePackerBase::InputDataFrameType::Normal ); + + if (chanNumber < 40) { + // Each EM fibre has 10 supercells for each of two 0.1*0.1 trigger towers. + // The EfexLatomeFibrePacker returns the complete set of 20 supercells. + EfexLatomeFibrePacker packer; + std::vector<FibrePackerBase::myDataWord> cells = packer.getUnpackedData( encodedData, frameType ); + + // Create two tower RDOs, each containing half the supercells. + for ( size_t k = 0; k < 2; k++ ) { + std::vector<uint32_t> towerCells( 10, 0 ); + uint32_t towerSumEt( 0 ); + for (size_t i = 0; i < 10; i++) { + towerCells[i] = cells[i+k*10]; + towerSumEt += towerCells[i]; + } + // **FIXME** Fibre packer does not yet return quality bits! + // There should be one per middle layer supercell. + // We can OR those with error bits for this block. + // TEMP: for the moment we only have the block bits. + const uint32_t towerFlag = errorMask; + + if ( towerSumEt || towerFlag ) { + const uint32_t wordNumber = k * 10; // Only need the first of 10 supercells + EfexCellMapping mapping( shelfNumber, efexNumber, fpgaNumber, chanNumber, wordNumber ); + L1CaloDetectorRegion region = mapping.getDetectorRegion(); + EfexHardwareInfo hwInfo( mapping.getHardwareInfo() ); + if ( region.getValidity() && hwInfo.getValidity() ) { + int localEta = region.getEtaIndex() - moduleEta + 1; + int localPhi = region.getPhiIndex() - modulePhi; + + const uint32_t layer = 0; // EM + L1CaloRdoEfexTower newOne( shelfNumber, efexNumber, localEta, localPhi, layer, region ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoEfexTower& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tower ); + + rdo.setHardwareInfo( fpgaNumber, chanNumber, wordNumber, + hwInfo.getMpodNumber(), hwInfo.getFibreNumber() ); + rdo.setValue( towerSumEt ); + rdo.setFlag( towerFlag ); + rdo.setSupercells( towerCells ); + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeEfexDataChan: EM" + << ", shelf=" << shelfNumber << ", module=" << efexNumber + << std::hex << ", Et=0x" << towerSumEt << ", flag=0x" << towerFlag + << std::dec << std::endl; + } + } + } + } + } + + else { + // Each hadronic fibre has a 4*4 array of 16 towers. + EfexTrexFibrePacker packer; + std::vector<FibrePackerBase::myDataWord> towerVals = packer.getUnpackedData( encodedData, frameType ); + size_t numHadTowers = std::min(towerVals.size(),(size_t)16); + + // Create an RDO for each tower. + for ( size_t wordNumber = 0; wordNumber < numHadTowers; wordNumber++ ) { + // At least for the start of Run 3 the tower Et is still limited to 8 bits + // as in Runs 1 & 2. After the legacy CP/JEP system is decommissioned this + // may change to enable use of the full 10 bit field. + const uint32_t towerEt = ( towerVals[wordNumber] == 0x3fe ) ? 0 : towerVals[wordNumber] & 0xff; + const uint32_t invalid = ( towerVals[wordNumber] == 0x3fe ) ? 1 : 0; + const uint32_t towerFlag = errorMask | ( invalid << 31 ); + + if ( towerEt || towerFlag ) { + EfexCellMapping mapping( shelfNumber, efexNumber, fpgaNumber, chanNumber, wordNumber ); + L1CaloDetectorRegion region = mapping.getDetectorRegion(); + EfexHardwareInfo hwInfo( mapping.getHardwareInfo() ); + if ( region.getValidity() && hwInfo.getValidity() ) { + int localEta = region.getEtaIndex() - moduleEta + 1; + int localPhi = region.getPhiIndex() - modulePhi; + + const uint32_t layer = 1; // Hadronic + L1CaloRdoEfexTower newOne( shelfNumber, efexNumber, localEta, localPhi, layer, region ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoEfexTower& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tower ); + + rdo.setHardwareInfo( fpgaNumber, chanNumber, wordNumber, + hwInfo.getMpodNumber(), hwInfo.getFibreNumber() ); + rdo.setValue( towerEt ); + rdo.setFlag( towerFlag ); + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeEfexDataChan: Had" + << ", shelf=" << shelfNumber << ", module=" << efexNumber + << std::hex << ", Et=0x" << towerEt << ", flag=0x" << towerFlag + << std::dec << std::endl; + } + } + } + } + } + + return errorBits; +} +#endif // OFFLINE +/*! + * Decode eFEX TOBs and XTOBs. + * The RDO value encodes the cluster Et in the value field + * and the isolation bits and other information in the flag. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tob list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeEfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + const size_t fragmentSize = end - beg; + + // The data block is optimised for production by firmware as a set + // of nested blocks with the lengths at the end. So we need to find + // the end and work backwards. + + if ( fragmentSize < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: fragment size " << fragmentSize + << " is too small for the ROD trailer" << std::endl; + return; + } + + size_t index = fragmentSize; + const uint32_t rodTrailer2 = payload[--index]; + const uint32_t rodTrailer1 = payload[--index]; + + const uint32_t rodErrors = rodTrailer2 & 0x7f; + const size_t payloadSize = rodTrailer1 & 0xffff; + if ( (payloadSize + 2) != fragmentSize ) { + // Actual ROD fragment payload size does not match that claimed in the trailer. + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: payload size " << payloadSize + << " inconsistent with ROD fragment size " << fragmentSize << std::endl; + return; + } + + // Loop looking backwards for eFEX module blocks. + while ( index > 0 ) { + if ( index < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: remaining block size " << index + << " is too small for the eFEX trailer" << std::endl; + return; + } + size_t efexIndex = index; + const uint32_t efexTrailer2 = payload[--efexIndex]; + const uint32_t efexTrailer1 = payload[--efexIndex]; + const size_t efexBlockSize = efexTrailer1 & 0xfff; + const uint32_t efexNumber = (efexTrailer1 >> 12) & 0xf; + const uint32_t shelfNumber = (efexTrailer1 >> 16) & 0x1; // 4 bit field, but only 1 but used + //??const uint32_t procErrMap = (efexTrailer1 >> 20) & 0xf; // Currently unused + const uint32_t numSlices = (efexTrailer1 >> 24) & 0xf; + //??const uint32_t l1aSlice = (efexTrailer1 >> 28) & 0xf; // Currently unused + const uint32_t efexErrors = efexTrailer2 & 0x3f; + if ( efexBlockSize > index ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: eFEX block size " << efexBlockSize + << " exceeds remaining data size " << index + << " (shelf " << shelfNumber << " eFEX " << efexNumber << ")" << std::endl; + return; + } + // Update index to previous eFEX block (if any). + index = efexIndex - efexBlockSize; + + // Combine rod and efex error fields. + const uint32_t errorMask = efexErrors | (rodErrors << 6); + + // Loop looking backwards for eFEX processor blocks. + // There should be one block per slice per FPGA + // (except for errors giving corrective trailers). + while ( efexIndex > index ) { + if ( (efexIndex - index) < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: remaining eFEX block size " + << (efexIndex - index) + << " is too small for the eFEX slice trailer" << std::endl; + return; + } + size_t procIndex = efexIndex; + bool ret = this->decodeEfexTobSlice ( payload, procIndex, efexNumber, shelfNumber, + numSlices, errorMask, tob, rodInfo ); + if ( ! ret ) { + return; + } + efexIndex = procIndex; + } + } +} + +/*! + * Decode one eFEX FPGA block of TOBs and XTOBs for one slice. + * \param payload entire ROD fragment payload vector + * \param index just past end of block for this slice + * (this method updates it to the start of the block) + * \param efexNumber number of this eFEX in its shelf + * \param shelfNumber shelf number + * \param numSlices number of TOB slices read out in this event + * \param errorMask global error bits set for this ROD fragment + * \param tob list of RDO to be filled + * \return whether decoding succeeded (false to abort decoding + in which case the returned index is not valid) + */ +bool +L1CaloBsDecoderRun3::decodeEfexTobSlice( const uint32_t payload[], size_t& index, + const uint32_t efexNumber, const uint32_t shelfNumber, + const uint32_t numSlices, const uint32_t errorMask, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + if ( index < 1 ) { + return false; + } + + const uint32_t sliceTrailer = payload[index-1]; + const uint32_t corrective = (sliceTrailer >> 31) & 0x01; + const uint32_t tauInputErr = (sliceTrailer >> 30) & 0x01; + const uint32_t emInputErr = (sliceTrailer >> 29) & 0x01; + + if ( corrective ) { + // Corrective trailer: no data from this processor FPGA. + // Not much we can do so just report it and carry on. + size_t blockSize = sliceTrailer & 0xfff; + const uint32_t failingBCN = (sliceTrailer >> 12) & 0xfff; + const uint32_t fpgaNumber = (sliceTrailer >> 24) & 0x3; + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: corrective trailer from FPGA " + << fpgaNumber << " eFEX " << efexNumber << " shelf " << shelfNumber + << ", failing BCN " << failingBCN << ", input errors tau=" << tauInputErr + << ", em=" << emInputErr << ", trailer size " << blockSize << std::endl; + // corrective trailer blockSize doesn't include the trailer itself (length of one) + // and also doesn't include padding word (which occurs if the blockSize is even + // because the corrective trailer adds one more word and then a padding word is needed + blockSize += (blockSize%2 == 1) ? 1 : 2; + index -= blockSize; // Ought to be 2 + } + else { + // October 2021: New format implemented! K.Char in lowest 8 bits. + const uint32_t tobType = (sliceTrailer >> 8) & 0x1; + const uint32_t numTobs = (sliceTrailer >> 9) & 0x7; + const uint32_t numEmXtobs = (sliceTrailer >> 12) & 0x3f; + const uint32_t numTauXtobs = (sliceTrailer >> 18) & 0x3f; + const uint32_t sliceNum = (sliceTrailer >> 24) & 0x7; + const uint32_t safeMode = (sliceTrailer >> 27) & 0x1; + const uint32_t fpgaNumber = (sliceTrailer >> 28) & 0x3; + + // Work out how long this block should be. Each TOB is one word, + // each XTOB is two words. If the total is odd (including one word + // for the trailer) there should be an extra zero padding word. + // However in safe mode all the TOBs and XTOBs are suppressed + // and we only have the trailer and padding word. + size_t blockSize = (safeMode == 0) + ? (1 + numTobs + 2 * (numEmXtobs + numTauXtobs)) + : 2; + blockSize += (blockSize % 2); + + // Move index to start of this block. + index -= blockSize; + + if ( safeMode ) { + std::cerr << "L1CaloBsDecoderRun3::decodeEfexTobs: safe mode from FPGA " + << fpgaNumber << " eFEX " << efexNumber << " shelf " << shelfNumber + << ", missed " << numTobs << " TOBs, " << numEmXtobs << " EM XTOBs, " + << numTauXtobs << " Tau XTOBs" << std::endl; + } + else { + // Luxury, we can now work forwards inside the slice block! + // We expect first TOBs (if any) then EM xTOBs, then Tau xTOBs. + const size_t totalTobs = numTobs + numEmXtobs + numTauXtobs; + L1CaloRdoFexTob::TobType fexTobType = (tobType == 0) + ? L1CaloRdoFexTob::TobType::EM + : L1CaloRdoFexTob::TobType::Tau; + L1CaloRdoFexTob::TobSource fexTobSource = L1CaloRdoFexTob::TobSource::EfexTob; + size_t sliceIndex = index; + for (size_t iTOB = 0; iTOB < totalTobs; iTOB++) { + if (iTOB >= numTobs) { + fexTobSource = L1CaloRdoFexTob::TobSource::EfexXtob; + fexTobType = (iTOB < (numTobs + numEmXtobs)) + ? L1CaloRdoFexTob::TobType::EM + : L1CaloRdoFexTob::TobType::Tau; + } + this->decodeOneEfexTob ( &payload[sliceIndex], shelfNumber, efexNumber, fpgaNumber, + errorMask, numSlices, sliceNum, fexTobType, fexTobSource, + tob, rodInfo ); + // One or two words per TOB or xTOB. + sliceIndex += (fexTobSource == L1CaloRdoFexTob::TobSource::EfexTob) ? 1 : 2; + } + } + } + return true; +} +#ifndef OFFLINE_DECODER +/*! + * Decode jFEX input fibre data. + * We create one RDO per tower using the value field for its Et. + * The flag field is used for status/error bits. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeJfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoJfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + const size_t fragmentSize = end - beg; + + // The data block is optimised for production by firmware with the length + // in a trailer at the end. The SWROD concatenates a number of such blocks + // (removing the 2 word header used to fill the ROD fragment header). + // So we need to find the end and work backwards. + + size_t numTowers = tower.size(); + size_t index = fragmentSize; + + // Loop looking backwards for jFEX processor input data blocks. + while ( index > 0 ) { + if ( index < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexData: remaining block size " << index + << " is too small for the jFEX FPGA trailer" << std::endl; + return; + } + const uint32_t fpgaTrailer2 = payload[--index]; + const uint32_t fpgaTrailer1 = payload[--index]; + + const uint32_t fpgaErrors = fpgaTrailer2 & 0x3f; + const uint32_t jfexNumber = (fpgaTrailer1 >> 20) & 0x7; + const uint32_t fpgaNumber = (fpgaTrailer1 >> 18) & 0x3; + const size_t payloadSize = (fpgaTrailer1 & 0xffff); + + if ( payloadSize > index ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexData: remaining jFEX block size " + << index << " is too small for the claimed payload size " + << payloadSize << std::endl; + return; + } + + // We can now work forwards from the start of this block + // decoding each set of 8 words per input fibre channel. + index -= payloadSize; + size_t chanIndex = 0; + while ( chanIndex < payloadSize ) { + if ( (payloadSize - chanIndex) < 8 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexData: remaining jFEX block size " + << (payloadSize - chanIndex) + << " is too small for one jFEX input fibre block (8)" << std::endl; + return; + } + this->decodeJfexDataChan ( &payload[index+chanIndex], jfexNumber, fpgaNumber, + fpgaErrors, tower, rodInfo ); + chanIndex += 8; + } + } + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeJfexData: n.towers added=" + << tower.size() - numTowers << std::endl; + } +} + +/*! + * Decode the data from one jFEX input fibre (only ever one slice). + * \param payload payload vector starting at this 8 word block + * \param jfexNumber number of this jFEX in its shelf + * \param fpgaNumber FPGA number + * \param errorMask global error bits set for this ROD fragment + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + * \return whether decoding succeeded (false to abort decoding) + */ +uint32_t +L1CaloBsDecoderRun3::decodeJfexDataChan( const uint32_t payload[], + const uint32_t jfexNumber, const uint32_t fpgaNumber, + const uint32_t errorMask, + std::list<L1CaloRdoJfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + // **FIXME** Use 2 for jFEX shelf number as currently generated in mappings. + // **FIXME** Its logical crate ID in COOL should be 34 though. Rationalise?! + const uint32_t shelfNumber = 2; + + // The LATOME and TREX fibres have slightly different encoding. + // Towards the forward regions and especially FCAL they cover + // different numbers of towers with more or less granularity. + // Channels 0-23 & 36-59 are standard inputs, 24-35 are overlap. + // The HEC overlap minipod (AVR) is #3 of 1-5 in each FPGA. + const uint32_t chanNumber = ( payload[7] ) & 0xff; + const uint32_t errorBits = ( payload[7] >> 28 ) & 0xf; + + // **FIXME** Ignore overlap for the moment. + if (chanNumber >= 24 && // **FIXME** Remove this! + chanNumber < 36) { // **FIXME** Remove this! + return 0; // **FIXME** Remove this! + } // **FIXME** Remove this! + // **FIXME** Ignore end jFEX modules for now. + if (jfexNumber == 0 || // **FIXME** Remove this! + jfexNumber == 5) { // **FIXME** Remove this! + return 0; // **FIXME** Remove this! + } // **FIXME** Remove this! + + // The JfexCellMapping returns global coordinates but the RDO objects + // expect local eta within the module. So prepare this here. + // **FIXME** Not sure how to handle internal eta for FCAL (esp C side). + int moduleEta = -24 + (jfexNumber % 6) * 8; + + // The fibre packer decoder methods expect a vector of seven words. + std::vector<FibrePackerBase::myDataWord> encodedData; + for ( size_t i = 0; i < 7; i++ ) { + encodedData.push_back( payload[i] ); + } + const FibrePackerBase::InputDataFrameType frameType( FibrePackerBase::InputDataFrameType::Normal ); + + // **FIXME** Need mapping to decide when to use Latome vs TREX decoders. + // **FIXME** But they are identical apart from handling of saturation + // **FIXME** so for the moment always use Latome. + + bool trex = false; // **FIXME** + + // Unpack sixteen 0.1*0.1 trigger towers. + std::vector<FibrePackerBase::myDataWord> towers; + if (trex) { + JfexTrexFibrePacker packer; + towers = packer.getUnpackedData( encodedData, frameType ); + } + else { + // Unpack sixteen 0.1*0.1 trigger towers. + JfexLatomeFibrePacker packer; + towers = packer.getUnpackedData( encodedData, frameType ); + } + + // Create RDO per tower. + for ( size_t iTower = 0; iTower < towers.size(); iTower++ ) { + const uint32_t towerEt = towers[iTower]; + + if ( towerEt || errorMask ) { + // The JfexCellMapping needs the "unit number", ie U1-U4 not + // the FPGA "processor" number as in the readout. Additionally + // the readout uses 0-3 whereas JfexCellMapping expects 1-4. + // We also want the fibre number. The (FPGA,chan) JfexCellMapping + // constructor returns that (numbered with the whole module). + // NB per FPGA minipod (AVR) 1, 2, 4 & 5 are full input data. + // Minipod 3 is the overlap one, also used for TTC, IPbus, etc. + // The channel (MGT) numbering in the readout is 0-59 which are + // the direct fibres. But in firmware and mappings 0-59 are PMA + // loopback channels. The direct fibres are actually 60-119. + // But for the moment the PMA loopback inputs are not read out + // so, except for the mapping lookup, we use 0-59. + int unitNumber = JfexDefs::processorNumberToUnitNumber(fpgaNumber+1); + JfexHardwareInfo hwInfoFpga( JfexCellMapping( unitNumber, chanNumber+60 ).getHardwareInfo() ); + int mgtFibreNumber = hwInfoFpga.getFibreNumber(); + JfexCellMapping mapping( jfexNumber, unitNumber, mgtFibreNumber, iTower ); + L1CaloDetectorRegion region = mapping.getDetectorRegion(); + JfexHardwareInfo hwInfo( mapping.getHardwareInfo() ); + + if ( region.getValidity() && hwInfo.getValidity() ) { + int layer = (region.getLayer() == L1CaloDetectorRegion::Electromagnetic) ? 0 : 1; + int localEta = region.getEtaIndex() - moduleEta; + int localPhi = region.getPhiIndex(); + + L1CaloRdoJfexTower newOne( shelfNumber, jfexNumber, localEta, localPhi, layer, region ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoJfexTower& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tower ); + + rdo.setHardwareInfo( fpgaNumber, chanNumber, iTower, + hwInfo.getAvr(), hwInfo.getFibreNumber() ); + rdo.setValue( towerEt ); + rdo.setFlag( errorMask ); + + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeJfexDataChan: " + << "module=" << jfexNumber + << std::hex << ", Et=0x" << towerEt << ", flag=0x" << errorMask + << std::dec << std::endl; + } + //std::cout << "L1CaloBsDecoderRun3::addJfexTower: fpga=" << fpgaNumber + // << ", chan=" << chanNumber << ", word=" << iTower + // << ", unit=" << unitNumber + // << ", mpod=" << hwInfo.getAvr() + // << ", modFibre=" << hwInfo.getFibreNumber() + // << ", mgtFibre=" << mgtFibreNumber + // << ", regionEta=" << localEta + // << ", regionPhi=" << localPhi + // << ", layer=" << layer + // << ", phiFpga=" << (localPhi/16) + // << ", hwFpga=" << hwInfo.getFpgaNumber() + // << ", validity=" << region.getValidity() + // << ", Et=" << towerEt + // << ", errors=0x" << std::hex << errorMask << std::dec + // << std::endl; + } + } + } + + return errorBits; +} + +/*! + * Decode jFEX TOBs and XTOBs. + * The RDO value encodes the cluster Et in the value field + * and the isolation bits and other information in the flag. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tob list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeJfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoJfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + const size_t fragmentSize = end - beg; + + // The data block is optimised for production by firmware as a set + // of nested blocks with the lengths at the end. So we need to find + // the end and work backwards. + + if ( fragmentSize < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexTobs: fragment size " << fragmentSize + << " is too small for the ROD trailer" << std::endl; + return; + } + + size_t index = fragmentSize; + const uint32_t rodTrailer2 = payload[--index]; + const uint32_t rodTrailer1 = payload[--index]; + + const uint32_t rodErrors = rodTrailer2 & 0x7f; + const size_t payloadSize = rodTrailer1 & 0xffff; + if ( (payloadSize + 2) != fragmentSize ) { + // Actual ROD fragment payload size does not match that claimed in the trailer. + std::cerr << "L1CaloBsDecoderRun3::decodeJfexTobs: payload size " << payloadSize + << " inconsistent with ROD fragment size " << fragmentSize << std::endl; + return; + } + //??const uint32_t rodShelfNumber = (rodTrailer1 >> 18) & 0x3; + + // Loop looking backwards for jFEX FPGA blocks. + while ( index > 0 ) { + if ( index < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexTobs: remaining block size " << index + << " is too small for the jFEX trailer" << std::endl; + return; + } + size_t fpgaIndex = index; + const uint32_t fpgaTrailer2 = payload[--fpgaIndex]; + const uint32_t fpgaTrailer1 = payload[--fpgaIndex]; + const size_t fpgaBlockSize = fpgaTrailer1 & 0xffff; + const uint32_t jfexNumber = (fpgaTrailer1 >> 20) & 0x7; + const uint32_t fpgaNumber = (fpgaTrailer1 >> 18) & 0x3; + const uint32_t numSlices = (fpgaTrailer1 >> 24) & 0xf; + const uint32_t sliceNumber = (fpgaTrailer1 >> 28) & 0xf; + const uint32_t fpgaErrors = fpgaTrailer2 & 0x3f; + if ( fpgaBlockSize > index ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexTobs: jFEX FPGA block size " + << fpgaBlockSize << " exceeds remaining data size " << index + << " (jFEX " << jfexNumber << " FPGA " << fpgaNumber << ")" << std::endl; + return; + } + // Update index to previous jFEX FPGA block (if any). + index = fpgaIndex - fpgaBlockSize; + + // Combine rod and jfex error fields. + const uint32_t errorMask = fpgaErrors | (rodErrors << 6); + + bool ret = this->decodeJfexTobSlice ( payload, fpgaIndex, jfexNumber, fpgaNumber, + sliceNumber, numSlices, errorMask, tob, rodInfo ); + if ( ! ret ) { + return; + } + } +} + +/*! + * Decode one jFEX FPGA block of TOBs and XTOBs for one slice. + * \param payload entire ROD fragment payload vector + * \param index just past end of block for this slice + * (this method updates it to the start of the block) + * \param jfexNumber number of this jFEX in its shelf + * \param fpgaNumber FPGA number + * \param sliceNumber number of this readout slice + * \param numSlices total number of TOB slices read out in this event + * \param errorMask global error bits set for this ROD fragment + * \param tob list of RDO to be filled + * \return whether decoding succeeded (false to abort decoding + in which case the returned index is not valid) + */ +bool +L1CaloBsDecoderRun3::decodeJfexTobSlice( const uint32_t payload[], size_t& index, + const uint32_t jfexNumber, const uint32_t fpgaNumber, + const uint32_t sliceNumber, const uint32_t numSlices, + const uint32_t errorMask, + std::list<L1CaloRdoJfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + if ( index < 1 ) { + return false; + } + + const uint32_t countTrailer1 = payload[index-2]; + const uint32_t countTrailer2 = payload[index-1]; + + // **FIXME** Not sure if jFEX will send corrective trailers? + const uint32_t corrective = 0; + if ( corrective ) { + // **FIXME** To be implemented if required. + } + else { + const uint32_t safeMode = (countTrailer1 ) & 0x1; + const uint32_t numSJetTobs = (countTrailer1 >> 1) & 0xf; + const uint32_t numLJetTobs = (countTrailer1 >> 5) & 0xf; + const uint32_t numTauTobs = (countTrailer1 >> 9) & 0xf; + const uint32_t numElecTobs = (countTrailer1 >> 13) & 0xf; + const uint32_t numSJetXtobs = (countTrailer2 >> 1) & 0x3f; + const uint32_t numLJetXtobs = (countTrailer2 >> 7) & 0x3f; + const uint32_t numTauXtobs = (countTrailer2 >> 13) & 0x3f; + const uint32_t numElecXtobs = (countTrailer2 >> 19) & 0x3f; + + // Work out how long this block should be. For jFEX both TOBs + // and XTOBs are one word. Apart from the counts of small jets, + // large jets, taus and (forward) electrons there are two TOBs + // for SumEt and MissingEt. + // If the total is odd there should be an extra zero padding word. + // However in safe mode all the TOBs and XTOBs are suppressed + // and we only have the trailer with the counts. + + // For ease of later decoding, fill a vector with the end index + // of each type of TOB/XTOB word. + std::vector<size_t> end; + end.push_back( 0 ) ; + if ( !safeMode ) { + end.push_back( end.back() + numSJetTobs ); + end.push_back( end.back() + numLJetTobs ); + end.push_back( end.back() + numTauTobs ); + end.push_back( end.back() + numElecTobs ); + end.push_back( end.back() + 1 ); // SumEt + end.push_back( end.back() + 1 ); // MissEt + end.push_back( end.back() + numSJetXtobs ); + end.push_back( end.back() + numLJetXtobs ); + end.push_back( end.back() + numTauXtobs ); + end.push_back( end.back() + numElecXtobs ); + } + + size_t blockSize = end.back(); // End of TOBs/XTOBs + blockSize += (blockSize % 2); // Possible padding + blockSize += 2; // Two count words + + size_t numTobs = (safeMode) ? 0 : end[6]; // End index of MissEt TOBs + size_t totalTobs = (safeMode) ? 0 : end.back(); // End index of TOBs+XTOBs + + // Move index to start of this block. + index -= blockSize; + + // Luxury, we can now work forwards inside the slice block! + size_t sliceIndex = index; + L1CaloRdoFexTob::TobType fexTobType; + L1CaloRdoFexTob::TobSource fexTobSource; + for (size_t iTOB = 0; iTOB < totalTobs; iTOB++) { + // Small jets, taus and electron TOBs and XTOBs + // all have similar structure so extract common + // values and override for other TOB types. + // TOBs and xTOBs now have identical formats + // so first find the TOB type, then process + // the values afterwards. + const uint32_t tobWord = payload[sliceIndex++]; + uint32_t flagInfo = (tobWord >> 21) & 0xfff; + uint32_t etValue = (tobWord >> 10) & 0x7ff; + uint32_t tobPhi = (tobWord >> 1) & 0xf; + uint32_t tobEta = (tobWord >> 5) & 0x1f; + fexTobSource = (iTOB < numTobs) + ? L1CaloRdoFexTob::TobSource::JfexTob + : L1CaloRdoFexTob::TobSource::JfexXtob; + if (iTOB < end[1]) { + fexTobType = L1CaloRdoFexTob::TobType::SmallJet; + flagInfo = 0; // None defined + } else if (iTOB < end[2]) { + fexTobType = L1CaloRdoFexTob::TobType::LargeJet; + } else if (iTOB < end[3]) { + fexTobType = L1CaloRdoFexTob::TobType::Tau; + } else if (iTOB < end[4]) { + fexTobType = L1CaloRdoFexTob::TobType::EM; + } else if (iTOB < end[6]) { + fexTobType = L1CaloRdoFexTob::TobType::Energy; + } else if (iTOB < end[7]) { + fexTobType = L1CaloRdoFexTob::TobType::SmallJet; + } else if (iTOB < end[8]) { + fexTobType = L1CaloRdoFexTob::TobType::LargeJet; + flagInfo = (tobWord ) & 0x3ff; + etValue = (tobWord >> 10) & 0x1fff; + } else if (iTOB < end[9]) { + fexTobType = L1CaloRdoFexTob::TobType::Tau; + } else if (iTOB < end[10]) { + fexTobType = L1CaloRdoFexTob::TobType::EM; + } else { + // Padding word: skip. + continue; + } + + if (fexTobType == L1CaloRdoFexTob::TobType::SmallJet) { + flagInfo = 0; // None defined + } else if (fexTobType == L1CaloRdoFexTob::TobType::LargeJet) { + flagInfo = 0; // None defined + etValue = (tobWord >> 10) & 0x1fff; + } else if (fexTobType == L1CaloRdoFexTob::TobType::Energy) { + // **FIXME** SumEt and MissEt to be implemented! + flagInfo = (tobWord & 0x1) | (tobWord & 0x1000) >> 15; + etValue = (tobWord & 0x7ffffffe) >> 1; + tobPhi = 0; + tobEta = 0; + } + + // **FIXME** REMOVE THIS TEMPORARY HACK ASAP!! + // **FIXME** Ignore & fix wrong slice numbers. + uint32_t sliceNumberHacked = sliceNumber; + if (numSlices == 1 && sliceNumber == 2) { + sliceNumberHacked = 0; + } + //>>if ( sliceNumber >= numSlices ) { + if ( sliceNumberHacked >= numSlices ) { + std::cerr << "L1CaloBsDecoderRun3::decodeJfexTobs: TOB slice " << sliceNumber + << " exceeds number of slices " << numSlices << " in processor trailer" + << std::endl; + } + else if ( etValue || flagInfo ) { + // Should already be zero suppressed in the readout + // but no harm in making doubly sure. + // Add the error bits from the jFEX control FPGA (low 6 bits) + // and the error bits from the ROD output packet (low 7 bits) + // to the isolation bits (shifted up 16 bits). + // This probably needs more sophisticated treatment. + // **FIXME** Check use of tobEta and adjusted tobPhi by FPGA. + const uint32_t modulePhi = tobPhi + 16 * fpgaNumber; + const uint32_t flagMask = (flagInfo << 16) | errorMask; + const uint32_t shelfNumber = 0x22; // Hard code to P1 value (34) + L1CaloRdoJfexTob newOne( shelfNumber, jfexNumber, tobEta, modulePhi, + numSlices, fexTobType, fexTobSource ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoJfexTob& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tob ); + //>>rdo.setValue( etValue, sliceNumber ); + //>>rdo.setFlag( flagMask, sliceNumber ); + rdo.setValue( etValue, sliceNumberHacked ); + rdo.setFlag( flagMask, sliceNumberHacked ); + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeJfexTobSlice: tobType=" << fexTobType + << ", tobSource=" << fexTobSource << ", slice=" << sliceNumber + << ", shelf=" << shelfNumber << ", module=" << jfexNumber + << ", eta=" << tobEta << ", phi=" << modulePhi + << std::hex << ", Et=0x" << etValue << ", flag=0x" << flagMask + << std::dec << ", numSlices=" << numSlices << std::endl; + } + } + } + } + return true; +} + +/*! + * Decode gFEX input fibre data. + * We create one RDO per tower using the value field for its Et. + * The flag field is used for status/error bits. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeGfexData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoGfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + + // The data block consists of up to three sections, one per processor FPGA. + // Each block starts with a one word header that identifies the FPGA and has + // the block length and a few other fields. + + size_t numTowers = tower.size(); + + // Loop looking backwards for jFEX processor input data blocks. + while ( payload < end ) { + const uint32_t word = *payload++; + + // The first word must be the header. + + const uint32_t fpgaCode = ( word >> 28 ) & 0xf; + if ( fpgaCode < 0xa || fpgaCode > 0xc ) { + std::cerr << "L1CaloBsDecoderRun3::decodeGfexData: invalid FPGA code 0x" + << std::hex << fpgaCode << std::dec << std::endl; + return; + } + const uint32_t fpgaNumber = fpgaCode - 0xa; // A=0, B=1, C=2 + const uint32_t headerVer = ( word >> 24 ) & 0xf; + const uint32_t headerLen = ( word >> 22 ) & 0x3; + if ( headerVer > 1 || headerLen > 1 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeGfexData: header version " << headerVer + << " or length " << headerLen << " is not yet supported" << std::endl; + return; + } + const uint32_t truncatedFlag = (word >> 12) & 0x1; + if ( truncatedFlag ) { + std::cerr << "L1CaloBsDecoderRun3::decodeGfexData: WARNING data truncated" << std::endl; + } + const uint32_t numFpgaWords = word & 0xfff; + if ( ( numFpgaWords % 7 ) != 0 ) { + std::cerr << "L1CaloBsDecoderRun3::decodeGfexData: input data size " << numFpgaWords + << " is not a multiple of 7" << std::endl; + return; + } + const uint32_t numInputFibres = numFpgaWords / 7; + + // Loop over the input fibres and decode each block of 7 words. + // **FIXME** Not sure if gFEX data will have error flags? + // **FIXME** For the moment hard code to zero. + + const uint32_t fpgaErrors = 0; + + for ( size_t chanNumber = 0; chanNumber < numInputFibres; chanNumber++ ) { + // Ignore the spare fibres for the moment. + if ( chanNumber < 48 || chanNumber >= 52 ) { + this->decodeGfexDataChan ( payload, fpgaNumber, chanNumber, + fpgaErrors, tower, rodInfo ); + } + payload += 7; + } + } + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeGfexData: n.towers added=" + << tower.size() - numTowers << std::endl; + } +} + +/*! + * Decode the data from one gFEX input fibre (only ever one slice). + * \param payload payload vector starting at this 7 word block + * \param fpgaNumber FPGA number (0-2 for A-C) + * \param errorMask global error bits set for this ROD fragment + * \param tower list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + * \return whether decoding succeeded (false to abort decoding) + */ +uint32_t +L1CaloBsDecoderRun3::decodeGfexDataChan( const uint32_t payload[], + const uint32_t fpgaNumber, + const uint32_t chanNumber, + const uint32_t errorMask, + std::list<L1CaloRdoGfexTower>& tower, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + // **FIXME** Use 3 for gFEX shelf number as currently generated in mappings. + // **FIXME** Its logical crate ID in COOL should be 35 though. Rationalise?! + const uint32_t shelfNumber = 3; + const uint32_t gfexNumber = 0; // **CHECK** What is used in COOL/OKS? + + // Ignore the spare fibres for the moment. + if ( chanNumber >= 48 && chanNumber < 52 ) { + return 0; + } + + // Ignore FPGA C for the moment (different format). + if ( fpgaNumber >= 2 ) { + return 0; + } + + // The fibre packer decoder methods expect a vector of seven words. + std::vector<FibrePackerBase::myDataWord> encodedData; + for ( size_t i = 0; i < 7; i++ ) { + encodedData.push_back( payload[i] ); + } + const FibrePackerBase::InputDataFrameType frameType( FibrePackerBase::InputDataFrameType::Normal ); + + // **FIXME** Need mapping to decide when to use Latome vs TREX decoders. + // **FIXME** Temporarily assume the first 50 fibres are EM (central Latome) + // **FIXME** and second 50 are hadronic (TREX). Surely wrong!! + + // Unpack up to sixteen 0.1*0.1 trigger towers. + // NB central FPGAs only have eight per fibre + // but additionally have eight fine positions. + // For the moment we ignore the latter. + std::vector<FibrePackerBase::myDataWord> towers; + size_t numTowers = 8; + L1CaloDetectorRegion::LayerTypeEnum layerEnum = L1CaloDetectorRegion::Electromagnetic; + if ( chanNumber >= 50 ) { + GfexTrexFibrePacker packer; + towers = packer.getUnpackedData( encodedData, frameType ); + layerEnum = L1CaloDetectorRegion::Hadronic; + } + else { + // Unpack sixteen 0.1*0.1 trigger towers. + GfexLatomeCentralFibrePacker packer; + towers = packer.getUnpackedData( encodedData, frameType ); + } + + // Create RDO per tower. + for ( size_t iTower = 0; iTower < numTowers && iTower < towers.size(); iTower++ ) { + int towerEt = towers[iTower]; + + if ( towerEt || errorMask ) { + // **FIXME** Create temporary mapping information. Probably WRONG! + int moduleFibre = chanNumber + fpgaNumber * 100; + int localPhi = GfexDefs::phiBinFromFibreSeqNum( moduleFibre, iTower ); + int localEta = GfexDefs::etaBinFromFibreSeqNum( moduleFibre, iTower ); + + // **FIXME** Create L1CaloDetectorRegion (normally comes from mappings). + const bool valid(true); // **TEMP** + const double binWidth = 0.2; // **TEMP** OK for central region + L1CaloDetectorRegion region( L1CaloDetectorRegion::GFEX, layerEnum, valid, + localEta, localPhi, binWidth, binWidth, + (localEta * 0.1), (localPhi * 0.1) ); + + if ( region.getValidity() && localPhi >= 0 && localEta >= 0 ) { + int layer = (region.getLayer() == L1CaloDetectorRegion::Electromagnetic) ? 0 : 1; + + L1CaloRdoGfexTower newOne( shelfNumber, gfexNumber, localEta, localPhi, layer, region ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoGfexTower& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tower ); + + // **FIXME** Invent minipod number and fibre in minipod. + // **FIXME** Use chanNumber ignoring spare fibres. + int fibreInFpga = (chanNumber > 48) ? (chanNumber - 4) : chanNumber; + int moduleMinipod = (fibreInFpga / 12) + (fpgaNumber * 4); + int fibreInMinipod = fibreInFpga % 12; + rdo.setHardwareInfo( fpgaNumber, chanNumber, iTower, + moduleMinipod, fibreInMinipod ); + rdo.setValue( towerEt ); + rdo.setFlag( errorMask ); + + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeGfexDataChan: " + << "FPGA=" << fpgaNumber + << std::hex << ", Et=0x" << towerEt << ", flag=0x" << errorMask + << std::dec << std::endl; + } + //std::cout << "L1CaloBsDecoderRun3::decodeGfexDataChan: fpga=" << fpgaNumber + // << ", chan=" << chanNumber << ", word=" << iTower + // << ", mpod=" << moduleMinipod + // << ", mpodFibre=" << fibreInMinipod + // << ", regionEta=" << localEta + // << ", regionPhi=" << localPhi + // << ", layer=" << layer + // << ", Et=" << towerEt + // << ", errors=0x" << std::hex << errorMask << std::dec + // << std::endl; + } + } + } + + return 0; // No error bits yet +} + +/*! + * Decode gFEX TOBs. + * We create one RDO per TOB using the value field for its Et. + * The flag field is used for status/error bits. + * **FIXME** We also create one RDO for the energy TOBs. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param tob list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeGfexTobs( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoGfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + // **FIXME** To be implemented! +} + +/*! + * Decode Ph1Topo input data: these are TOBs from FEXes and MuCTPI. + * We create one RDO per TOB using the value field for its Et. + * The flag field is used for status/error bits. + * \param beg pointer to start of rod data payload + * \param end pointer to end of rod data payload + * \param etob list of eFEX TOB RDOs to be filled + * \param jtob list of jFEX TOB RDOs to be filled + * \param gtob list of gFEX TOB RDOs to be filled + * \param mtob list of muon TOB RDOs to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodePh1TopoData( const uint32_t* beg, const uint32_t* end, + std::list<L1CaloRdoEfexTob>& etob, + std::list<L1CaloRdoJfexTob>& jtob, + std::list<L1CaloRdoGfexTob>& gtob, + std::list<L1CaloRdoMuonTob>& mtob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t* payload( beg ); + const size_t fragmentSize = end - beg; + + // Fibre types of Ph1Topo inputs. These are defined in the VHDL at: + // https://gitlab.cern.ch/atlas-l1calo/l1topo/ph1topo/-/blob/master/src/infr/topo_mapping_pkg.vhd#L27 + // For our purposes we only need to distinguish different formats + // and ignore those that do not appear in the readout. + enum TopoInputFibreTypes { Unknown=0x00, IPB=0x01, TTC=0x02, EM1=0x03, EM2=0x04, + G1=0x05, G2=0x06, J1=0x07, J2=0x08, JF=0x09, JFXE=0x0a, + M0A=0x0b, M0C=0x0c, M1A=0x0d, M1C=0x0e, M2A=0x0f, M2C=0x10, + TAU1=0x11, TAU2=0x12, gJ1=0x13, gJ2=0x14 }; + + // The data block is optimised for production by firmware with the length + // in a trailer at the end. The SWROD concatenates a number of such blocks + // (removing the 2 word header used to fill the ROD fragment header). + // So we need to find the end and work backwards. + + //??size_t numTobs = tower.size(); + size_t index = fragmentSize; + + // Loop looking backwards for Ph1Topo processor input data blocks. + while ( index > 0 ) { + if ( index < 2 ) { + std::cerr << "L1CaloBsDecoderRun3::decodePh1TopoData: remaining block size " << index + << " is too small for the Ph1Topo FPGA trailer" << std::endl; + return; + } + const uint32_t fpgaTrailer2 = payload[--index]; + const uint32_t fpgaTrailer1 = payload[--index]; + + const uint32_t fpgaErrors = fpgaTrailer2 & 0x3f; + //??const uint32_t sliceNum = (fpgaTrailer1 >> 28) & 0xf; + //??const uint32_t numSlices = (fpgaTrailer1 >> 24) & 0xf; + const uint32_t sliceNum = 0; // **FIXME** Temporarily hard code until set properly + const uint32_t numSlices = 1; // **FIXME** Temporarily hard code until set properly + //>>const uint32_t topoNumber = (fpgaTrailer1 >> 22) & 0x3; + //>>const uint32_t fpgaNumber = (fpgaTrailer1 >> 21) & 0x1; + //>>const uint32_t fwType = (fpgaTrailer1 >> 19) & 0x3; // 0: Mult, 1: Algo, 2: Beta + const size_t payloadSize = (fpgaTrailer1 & 0xffff); + + if ( payloadSize > index ) { + std::cerr << "L1CaloBsDecoderRun3::decodePh1TopoData: remaining Ph1Topo block size " + << index << " is too small for the claimed payload size " + << payloadSize << std::endl; + return; + } + + // We can now work forwards from the start of this block + // decoding each set of 8 words per input fibre channel. + // In Ph1Topo the input data channels can be TOBs of many + // different types. The type is identifed in the 8th word. + // We then need the appropriate mapping to give us which + // P1 FEX crate and module number is the source. + // NB this may be incorrect for test rig setups. + index -= payloadSize; + size_t chanIndex = 0; + uint32_t fexShelf = 0; + uint32_t fexModule = 0; + uint32_t fexFpga = 0; + while ( chanIndex < payloadSize ) { + if ( (payloadSize - chanIndex) < 8 ) { + std::cerr << "L1CaloBsDecoderRun3::decodePh1TopoData: remaining Ph1Topo block size " + << (payloadSize - chanIndex) + << " is too small for one Ph1Topo input fibre block (8)" << std::endl; + return; + } + //>>const uint32_t chanNumber = ( payload[7] ) & 0xff; + const uint32_t fibreType = ( payload[7] >> 8 ) & 0x1f; + //>>const uint32_t errorBits = ( payload[7] >> 30 ) & 0x3; + + L1CaloRdoFexTob::TobType tobType(L1CaloRdoFexTob::TobType::Invalid); + L1CaloRdoFexTob::TobSource tobSource(L1CaloRdoFexTob::TobSource::Ph1Topo); + + // Most fibres have up to six TOBs, but jFEX fibres have seven. + size_t numTobs = 6; + if (fibreType == TopoInputFibreTypes::J1 || fibreType == TopoInputFibreTypes::JF || + fibreType == TopoInputFibreTypes::J2 || fibreType == TopoInputFibreTypes::JFXE) { + numTobs = 7; + } + + for (size_t iTob = 0; iTob < numTobs; iTob++) { + // TOBs from eFEX. + if (fibreType == TopoInputFibreTypes::EM1 || fibreType == TopoInputFibreTypes::TAU1 || + fibreType == TopoInputFibreTypes::EM2 || fibreType == TopoInputFibreTypes::TAU2) { + tobType = (fibreType <= TopoInputFibreTypes::EM2) // Assumes enum order! + ? L1CaloRdoFexTob::TobType::EM + : L1CaloRdoFexTob::TobType::Tau; + // Need mapping! + this->decodeOneEfexTob( &payload[index+chanIndex+iTob], fexShelf, fexModule, fexFpga, + fpgaErrors, numSlices, sliceNum, tobType, tobSource, + etob, rodInfo ); + } + // Fibres from jFEX. + /* + else if () { + } + */ + } + chanIndex += 8; + } + } + //??if ( m_verbosity > 0 ) + //??{ + //?? std::cout << "L1CaloBsDecoderRun3::decodeJfexData: n.towers added=" + //?? << tower.size() - numTowers << std::endl; + //??} +} +#endif // OFFLINE +/*! + * Decode word(s) for one eFEX TOB (or xTOB) and create one RDO. + * **FIXME** Document other parameters when stable! + * \param tob list of RDO to be filled + * \param rodInfo iterator to ROD information for this block + */ +void +L1CaloBsDecoderRun3::decodeOneEfexTob( const uint32_t word[], const uint32_t shelfNumber, + const uint32_t efexNumber, const uint32_t/* fpgaNumber*/, + const uint32_t errorMask, + const uint32_t numSlices, const uint32_t sliceNum, + L1CaloRdoFexTob::TobType tobType, + L1CaloRdoFexTob::TobSource tobSource, + std::list<L1CaloRdoEfexTob>& tob, + std::list<L1CaloRdoRodInfo>::const_iterator rodInfo ) +{ + const uint32_t tobWord = word[0]; + const uint32_t isolInfo = (tobWord ) & 0xffc000; + const uint32_t tobPhi = (tobWord >> 24) & 0x7; + const uint32_t tobEta = (tobWord >> 27) & 0x7; + const uint32_t tobFpga = (tobWord >> 30) & 0x3; + + uint32_t etValue(0); + if (tobSource == L1CaloRdoFexTob::TobSource::EfexXtob) { + // XTOB: Et from second TOB word. + etValue = word[1] & 0xffff; + } + else { // EfexTob or Ph1Topo + // TOB: Et from single TOB word. + etValue = tobWord & 0xfff; + } + + if ( sliceNum >= numSlices ) { + ERS_ERROR("L1CaloBsDecoderRun3::decodeOneEfexTob: TOB slice " << sliceNum + << " exceeds number of slices " << numSlices << " in processor trailer"); + } + else if ( etValue ) { + // Should already be zero suppressed in the readout + // but no harm in making doubly sure. + // Add the error bits from the eFEX control FPGA (low 6 bits) + // and the error bits from the ROD output packet (low 7 bits) + // to the isolation bits (which have the low 14 bits unused). + // This probably needs more sophisticated treatment. + // The internal eta within one FPGA is in the range 0-5 + // where 1-4 are the standard values and 0 & 5 are only + // used at the extreme eta, ie |eta| > 2.4. + // To get an eta within the module we use the range 0-17 + // where 0 & 17 are only used at the extremes. + const uint32_t moduleEta = 1 + (tobEta - 1) + 4 * tobFpga; + const uint32_t flagMask = isolInfo | errorMask; + L1CaloRdoEfexTob newOne( shelfNumber, efexNumber, moduleEta, tobPhi, + numSlices, tobType, tobSource ); + newOne.setRodInfo( rodInfo ); + L1CaloRdoEfexTob& rdo = L1CaloBsDecoderUtil::findRdo( newOne, tob ); + rdo.setValue( etValue, sliceNum ); + rdo.setFlag( flagMask, sliceNum ); +#ifdef OFFLINE_DECODER + // store the raw words - used in offline to construct the EDM objects + rdo.setWord0( word[0], sliceNum ); + if(tobSource == L1CaloRdoFexTob::TobSource::EfexXtob) rdo.setWord1(word[1], sliceNum); +#endif + if ( m_verbosity > 0 ) + { + std::cout << "L1CaloBsDecoderRun3::decodeOneEfexTob: tobType=" << tobType + << ", tobSource=" << tobSource << ", slice=" << sliceNum + << ", shelf=" << shelfNumber << ", module=" << efexNumber + << ", eta=" << moduleEta << ", phi=" << tobPhi + << std::hex << ", Et=0x" << etValue << ", flag=0x" << flagMask + << std::dec << ", numSlices=" << numSlices << std::endl; + } + } +} diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderUtil.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderUtil.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d86bf9747e616bb638fa0607ef1f67492eeb1c0a --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloBsDecoderUtil.cxx @@ -0,0 +1,80 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include <iostream> +#include <iomanip> + +#include "eformat/ROBFragment.h" + +#include "bytestreamDecoder/L1CaloBsDecoderUtil.h" +#include "bytestreamDecoder/L1CaloRdoRodInfo.h" + +/*! + * \class L1CaloBsDecoderUtil + * Utility methods for bytestream decoder classes for runs 1, 2 and 3. + */ + +L1CaloBsDecoderUtil::L1CaloBsDecoderUtil() +{ +} + +void +L1CaloBsDecoderUtil::decodeRodInfo( const eformat::ROBFragment<const uint32_t*>* rod, + std::list<L1CaloRdoRodInfo>& dat ) +{ + // Create the basic object + int sourceId = rod->rod_source_id() & 0xffffff; + int runNumber = rod->rod_run_no(); + int bcNumber = rod->rod_bc_id(); + int triggerType = rod->rod_lvl1_trigger_type(); + int detEventType = rod->rod_detev_type(); + int version = rod->rod_version(); +//<< // **FIXME** Hack for testing Run 3: set rod version +//<< version |= 0x1004; // **FIXME** Remove this! + L1CaloRdoRodInfo rdo( sourceId>>16, sourceId&0xffff, runNumber, bcNumber, triggerType, detEventType, version ); + + // Add in the Level-1 ID and size + rdo.setLvl1Id( rod->rod_lvl1_id() ); + rdo.setSize( rod->rod_ndata() ); + + // Add in the status words + const uint32_t* status; + rod->rod_status(status); + int stat = *status++; + rdo.setStatus1( stat ); + stat = *status; + rdo.setStatus2( stat ); + + // Now add in the module status words in the payload, if not RoI or CTP + bool hasModuleStatus = true; + if ( (sourceId>>16) > 0x74 ) + hasModuleStatus = false; + if ( (sourceId>>16) == 0x73 ) + hasModuleStatus = false; + + if ( hasModuleStatus ) + { + const uint32_t* it_data; + rod->rod_data( it_data ); + const uint32_t ndata = rod->rod_ndata(); + ++it_data; + for ( uint32_t i = 1; i < ndata; ++i, ++it_data ) { + if ( ((*it_data)>>28) == 0xd ) + { + int idat = (*it_data)&0xfff; + int module = idat>>8; + int modStat = rdo.getModuleStatus( module ); + rdo.setModuleStatus( module, modStat | (idat&0xff) ); + } + if ( ((*it_data)>>28) == 0xf ) + { + int idat = (*it_data)&0x1ff; + int module = idat>>8; + int modStat = rdo.getModuleStatus( module ); + rdo.setModuleStatus( module, modStat | (idat&0xff) ); + } + } + } + + dat.push_back( rdo ); +} diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdo.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdo.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c66ee885ed9917fb788c676414bd728a4bb0022b --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdo.cxx @@ -0,0 +1,290 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include "bytestreamDecoder/L1CaloRdo.h" + +#include "bytestreamDecoder/L1CaloRdoRodInfo.h" + +#include <iostream> + +/*! + * \class L1CaloRdo + Abstract base class for L1Calo "Raw Data Objects" (RDOs). + Each RDO represents some information associated with an eta,phi,layer + for the complete set of readout time slices (bunch crossings). + Different coordinates imply different RDOs, but all timeslices for + the same coordinate are always kept in the same RDO. + For each RDO there is an integer value and flag per timeslice. + What this means is up to the subclasses. + The value may be an Et of towers or jet elements, threshold + multiplicities or hit bits indicating which thresholds were passed. + Sometimes the value is bit coded to pack two values into one word. + The flags are generally error bits, but in some cases other values + are bit packed into the flags. + Where either field is bit packed the subclass should have specific + methods to decode them. + */ + +L1CaloRdo::L1CaloRdo( int crate, int module, int eta, int phi, + int layer, int numSlices ) +: m_crate( crate ) +, m_module( module ) +, m_eta( eta ) +, m_phi( phi ) +, m_layer( layer ) +, m_vals( numSlices, 0 ) +, m_flags( numSlices, 0 ) +, m_l1aPos( numSlices/2 ) +#ifdef OFFLINE_DECODER +, m_word0s(numSlices,0),m_word1s(numSlices,0) +#endif +{ + // std::cout << "Object Created" << std::endl; +} + +L1CaloRdo::~L1CaloRdo( ) +{ + // std::cout << "Object Deleted" << std::endl; +} + +bool +operator<( const L1CaloRdo& lhs, const L1CaloRdo& rhs ) +{ + if ( lhs.getCrate() < rhs.getCrate() ) + return true; + if ( lhs.getCrate() > rhs.getCrate() ) + return false; + if ( lhs.getModule() < rhs.getModule() ) + return true; + if ( lhs.getModule() > rhs.getModule() ) + return false; + if ( lhs.getEta() < rhs.getEta() ) + return true; + if ( lhs.getEta() > rhs.getEta() ) + return false; + if ( lhs.getPhi() < rhs.getPhi() ) + return true; + if ( lhs.getPhi() > rhs.getPhi() ) + return false; + if ( lhs.getLayer() < rhs.getLayer() ) + return true; + return false; +} + +int +L1CaloRdo::getCrate( ) const +{ + return m_crate; +} + +int +L1CaloRdo::getModule( ) const +{ + return m_module; +} + +int +L1CaloRdo::getEta( ) const +{ + return m_eta; +} + +int +L1CaloRdo::getPhi( ) const +{ + return m_phi; +} + +int +L1CaloRdo::getLayer( ) const +{ + return m_layer; +} + +int +L1CaloRdo::getValue( size_t slice ) const +{ + if ( slice < m_vals.size() ) + return m_vals[slice]; + return 0; +} + +int +L1CaloRdo::getValue( ) const +{ + return getValue( getL1aPos() ); +} + +int +L1CaloRdo::getFlag( size_t slice ) const +{ + if ( slice < m_flags.size() ) + return m_flags[slice]; + return 0; +} + +int +L1CaloRdo::getFlag( ) const +{ + return getFlag( getL1aPos() ); +} + +int +L1CaloRdo::getL1aPos( ) const +{ + return m_l1aPos; +} +#ifndef OFFLINE_DECODER +L1CaloDetectorRegion +L1CaloRdo::getRegion( ) const +{ + return m_region; +} +#endif + +const std::list<L1CaloRdoRodInfo>::const_iterator& +L1CaloRdo::getRodInfo( ) const +{ + return m_rodInfo; +} + +int +L1CaloRdo::getModuleStatus( ) const +{ + return m_rodInfo->getModuleStatus( getModule() ); +} + +bool +L1CaloRdo::getModuleErrorGlinkParity( ) const +{ + return getModuleStatus( ) & 0x01 ; +} + +bool +L1CaloRdo::getModuleErrorGlinkProtocol( ) const +{ + return getModuleStatus( ) & 0x02 ; +} + +bool +L1CaloRdo::getModuleErrorBcnMismatch( ) const +{ + return getModuleStatus( ) & 0x04 ; +} + +bool +L1CaloRdo::getModuleErrorFifoOverflow( ) const +{ + return getModuleStatus( ) & 0x08 ; +} + +bool +L1CaloRdo::getModuleErrorSpecific( ) const +{ + return getModuleStatus( ) & 0x10 ; +} + +bool +L1CaloRdo::getModuleErrorUnused( ) const +{ + return getModuleStatus( ) & 0x20 ; +} + +bool +L1CaloRdo::getModuleErrorGlinkTimeout( ) const +{ + return getModuleStatus( ) & 0x40 ; +} + +bool +L1CaloRdo::getModuleErrorGlinkDown( ) const +{ + return getModuleStatus( ) & 0x80 ; +} + +size_t +L1CaloRdo::numSlices( ) const +{ + return m_vals.size(); +} + +bool +L1CaloRdo::sameDatum( const L1CaloRdo& rhs ) const +{ + if ( m_crate != rhs.m_crate ) + return false; + if ( m_module != rhs.m_module ) + return false; + if ( m_eta != rhs.m_eta ) + return false; + if ( m_phi != rhs.m_phi ) + return false; + if ( m_layer != rhs.m_layer ) + return false; + return true; +} + +void +L1CaloRdo::setValue( int val, size_t slice ) +{ + if ( slice < m_vals.size() ) + m_vals[slice] = val; +} + +void +L1CaloRdo::setValue( int val ) +{ + setValue( val, getL1aPos() ); +} + +void +L1CaloRdo::setFlag( int flag, size_t slice ) +{ + if ( slice < m_flags.size() ) + m_flags[slice] = flag; +} + +void +L1CaloRdo::setFlag( int flag ) +{ + setFlag( flag, getL1aPos() ); +} + +void +L1CaloRdo::setRodInfo( std::list<L1CaloRdoRodInfo>::const_iterator& rodInfo ) +{ + m_rodInfo = rodInfo; +} + +void +L1CaloRdo::info( ) const +{ + std::cout << "RDO Object Type: " << getType() + << " Crate: " << getCrate() + << " Module: " << getModule() + << " Eta: " << getEta() + << " Phi: " << getPhi() + << " Layer: " << getLayer() << std::endl; + std::cout << " Values: " << std::hex; + for ( size_t i = 0 ; i < m_vals.size() ; ++i ) + std::cout << getValue(i) << " "; + std::cout << " Flags: "; + for ( size_t i = 0 ; i < m_flags.size() ; ++i ) + std::cout << getFlag(i) << " "; + std::cout << std::endl << std::dec; + infoSpecific(); +} + +void +L1CaloRdo::infoSpecific( ) const +{ +} + +#ifndef OFFLINE_DECODER +void +L1CaloRdo::setRegion( const L1CaloDetectorRegion& region ) +{ + m_region = region; +} +#endif + diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoEfexTob.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoEfexTob.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7608ff56f56c208364f27839741d4db0c9db969c --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoEfexTob.cxx @@ -0,0 +1,89 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include "bytestreamDecoder/L1CaloRdoEfexTob.h" + +#include <iomanip> +#include <iostream> + +//>>#include "channelMappings/CpmTowerMapping.h" // Use Efex mapping!! + +/*! + \class L1CaloRdoEfexTob + L1CaloRdoFexTob subclass for eFEX EM/Tau TOBs or XTOBs. + These may come from eFEX or Ph1Topo inputs. + The available flags are different in these two cases. + The RDO object has two 32 bit words: value and flag. + The TOBs and XTOBs differ mainly in the number of bits + used for the Et value (12 vs 16). We store the Et in + the value word and everything else in the flag keeping + all the fields in their location in the hardware format. + + The id, fibre and tobSeq values were used in Run 2 for topo + and for distinguishing different sources of the same TOB + (eg here from FEX readout vs Ph1Topo readout perhaps). + */ + + +L1CaloRdoEfexTob::L1CaloRdoEfexTob( int crate, int module, int eta, int phi, int numSlices, + TobType tobType, TobSource source, int id, int fibre, int tobSeq ) +: L1CaloRdoFexTob( crate, module, eta, phi, numSlices, tobType, source, id, fibre, tobSeq ) +{ + //??CpmTowerMapping map( crate, module, eta, phi ); + //??setRegion( map.getL1CaloDetectorRegion() ); +} + +void +L1CaloRdoEfexTob::infoSpecific( ) const +{ + std::cout << " Isolation: " << std::hex; + for ( size_t s = 0 ; s < numSlices() ; ++s ) + { + std::cout << std::setw(2) << getIsol( s ) << " "; + } + std::cout << std::dec << std::endl; +} + +/* +bool +L1CaloRdoEfexTob::getOverflow( size_t slice ) const +{ + return ( getFlag( slice ) & 0x10 ); // **FIXME** +} + +bool +L1CaloRdoEfexTob::getOverflow( ) const +{ + return getOverflow( getL1aPos() ); +} +*/ + +/*! + Return 12 bit (TOB) or 16 bit (XTOB) cluster Et (from the RDO value). + */ +int +L1CaloRdoEfexTob::getClusterEt( size_t slice ) const +{ + return ( getValue( slice ) & 0xffff ); +} + +int +L1CaloRdoEfexTob::getClusterEt( ) const +{ + return getClusterEt( getL1aPos() ); +} + +/*! + Return three 2 bit isolation hits (packed into bits 18-23 of the RDO flag). + */ +int +L1CaloRdoEfexTob::getIsol( size_t slice ) const +{ + return ( ( getFlag( slice ) >> 18 ) & 0x3f ); +} + +int +L1CaloRdoEfexTob::getIsol( ) const +{ + return getIsol( getL1aPos() ); +} diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoFexTob.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoFexTob.cxx new file mode 100644 index 0000000000000000000000000000000000000000..544105f590db1d95b7b0e2180844598a16a798f5 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoFexTob.cxx @@ -0,0 +1,216 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include "bytestreamDecoder/L1CaloRdoFexTob.h" + +#include <iostream> +#include <sstream> + +/*! + \class L1CaloRdoFexTob + L1CaloRdo subclass for FEX Trigger Objects (TOBs and XTOBs). + These may come from FEX or phase 1 Topo readout. + */ + + +L1CaloRdoFexTob::L1CaloRdoFexTob( int crate, int module, int eta, int phi, int numSlices, + L1CaloRdoFexTob::TobType tobType, L1CaloRdoFexTob::TobSource source, + int id, int fibre, int tobSeq ) +: L1CaloRdo( crate, module, eta, phi, 0, numSlices ) +, m_tobType( tobType ) +, m_source( source ) +, m_id( id ) +, m_fibre( fibre ) +, m_tobSeq( tobSeq ) +{ +} + +std::string +L1CaloRdoFexTob::getType( ) const +{ + return getTobTypeString() + std::string( "Tob:" ) + getTobSourceString(); +} + +bool +L1CaloRdoFexTob::sameDatum( const L1CaloRdoFexTob& rhs ) const +{ + if ( m_tobType != rhs.m_tobType ) + return false; + if ( m_source != rhs.m_source ) + return false; + if ( m_id != rhs.m_id ) + return false; + return L1CaloRdo::sameDatum( rhs ); +} + +L1CaloRdoFexTob::TobType +L1CaloRdoFexTob::getTobType( ) const +{ + return m_tobType; +} + +std::string +L1CaloRdoFexTob::getTobTypeString( ) const +{ + if ( getIsEM() ) { return "EM"; } + else if ( getIsTau() ) { return "Tau"; } + else if ( getIsLargeJet() ) { return "LJet"; } + else if ( getIsSmallJet() ) { return "SJet"; } + else if ( getIsEnergy() ) { return "Energy"; } + else if ( getIsMuon() ) { return "Muon"; } + else { return ""; } +} + +int +L1CaloRdoFexTob::getTobID( ) const +{ + return m_id; +} + +bool +L1CaloRdoFexTob::getIsEM( ) const +{ + return ( getTobType() == TobType::EM ); +} + +bool +L1CaloRdoFexTob::getIsTau( ) const +{ + return ( getTobType() == TobType::Tau ); +} + +bool +L1CaloRdoFexTob::getIsJet( ) const +{ + return ( getTobType() == TobType::LargeJet || + getTobType() == TobType::SmallJet ); +} + +bool +L1CaloRdoFexTob::getIsLargeJet( ) const +{ + return ( getTobType() == TobType::LargeJet ); +} + +bool +L1CaloRdoFexTob::getIsSmallJet( ) const +{ + return ( getTobType() == TobType::SmallJet ); +} + +bool +L1CaloRdoFexTob::getIsEnergy( ) const +{ + return ( getTobType() == TobType::Energy ); +} + +bool +L1CaloRdoFexTob::getIsMuon( ) const +{ + return ( getTobType() == TobType::Muon ); +} + +L1CaloRdoFexTob::TobSource +L1CaloRdoFexTob::getTobSource( ) const +{ + return m_source; +} + +std::string +L1CaloRdoFexTob::getTobSourceString( ) const +{ + if ( getIsTob() ) { return "Tob"; } + else if ( getIsXtob() ) { return "Xtob"; } + else if ( getIsTopo() ) { std::ostringstream s; s << "Topo" << m_id; return s.str(); } + else { return ""; } +} + +bool +L1CaloRdoFexTob::getIsTob( ) const +{ + return ( getTobSource() == TobSource::EfexTob || + getTobSource() == TobSource::JfexTob || + getTobSource() == TobSource::GfexTob || + getTobSource() == TobSource::Ph1Topo ); +} + +bool +L1CaloRdoFexTob::getIsXtob( ) const +{ + return ( getTobSource() == TobSource::EfexXtob || + getTobSource() == TobSource::JfexXtob || + getTobSource() == TobSource::GfexXtob ); +} + +bool +L1CaloRdoFexTob::getIsEfex( ) const +{ + return ( getTobSource() == TobSource::EfexTob || + getTobSource() == TobSource::EfexXtob ); +} + +bool +L1CaloRdoFexTob::getIsJfex( ) const +{ + return ( getTobSource() == TobSource::JfexTob || + getTobSource() == TobSource::JfexXtob ); +} + +bool +L1CaloRdoFexTob::getIsGfex( ) const +{ + return ( getTobSource() == TobSource::GfexTob || + getTobSource() == TobSource::GfexXtob ); +} + +bool +L1CaloRdoFexTob::getIsTopo( ) const +{ + return ( getTobSource() == TobSource::Ph1Topo ); +} + +int +L1CaloRdoFexTob::getFibre( ) const +{ + return m_fibre; +} + +int +L1CaloRdoFexTob::getTobSeq( ) const +{ + return m_tobSeq; +} + +bool +operator<( const L1CaloRdoFexTob& lhs, const L1CaloRdoFexTob& rhs ) +{ + if ( lhs.getCrate() < rhs.getCrate() ) + return true; + if ( lhs.getCrate() > rhs.getCrate() ) + return false; + if ( lhs.getModule() < rhs.getModule() ) + return true; + if ( lhs.getModule() > rhs.getModule() ) + return false; + if ( lhs.getEta() < rhs.getEta() ) + return true; + if ( lhs.getEta() > rhs.getEta() ) + return false; + if ( lhs.getPhi() < rhs.getPhi() ) + return true; + if ( lhs.getPhi() > rhs.getPhi() ) + return false; + if ( lhs.getLayer() < rhs.getLayer() ) + return true; + if ( lhs.getTobType() < rhs.getTobType() ) + return true; + if ( lhs.getTobType() > rhs.getTobType() ) + return false; + if ( lhs.getTobSource( ) < rhs.getTobSource( ) ) + return true; + if ( lhs.getTobSource( ) > rhs.getTobSource( ) ) + return false; + if ( lhs.getTobID( ) < rhs.getTobID( ) ) + return true; + return false; +} diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoRodInfo.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoRodInfo.cxx new file mode 100644 index 0000000000000000000000000000000000000000..6c795282ff1fb676da04a702fc49609f808321fd --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/bytestreamDecoder/src/L1CaloRdoRodInfo.cxx @@ -0,0 +1,231 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ +#include "bytestreamDecoder/L1CaloRdoRodInfo.h" + +#include <iostream> + +// Class mainly intended to hold information from the ROD fragment header. +// It also contains error codes from the L1Calo ROD fragment status block +// for the legacy Run 1 and Run 2 system. +// Many of these are no longer relevant for Run 3 FEX/Topo ROD fragments. +// But it still seems worth having a common ROD info class and other error +// bits from these might be added? + +L1CaloRdoRodInfo::L1CaloRdoRodInfo( int system, int rod, int run, int bcnum, int triggerType, int detType, int version ) +: m_system( system ) +, m_rod( rod ) +, m_run( run ) +, m_bcnum( bcnum ) +, m_triggerType( triggerType ) +, m_detType( detType ) +, m_lvl1( 0 ) +, m_size( 0 ) +, m_status1( 0 ) +, m_status2 ( 0 ) +, m_version( version ) +, m_status( 16, 0 ) +{ +} + +std::string +L1CaloRdoRodInfo::getType( ) const +{ + return std::string( "RodInfo" ); +} + +void +L1CaloRdoRodInfo::info( ) const +{ + std::cout << "RDO Object Type: " << getType(); + std::cout << std::hex; + std::cout << " System Id: " << getSystemId(); + std::cout << " ROD Id: " << getRodId() << std::endl << " "; + std::cout << " Run Num: " << getRunNumber(); + std::cout << " Level-1 Id: " << getLevel1Id(); + std::cout << " Bc Number: " << getBcNumber(); + std::cout << " Trig Type: " << getTriggerType(); + std::cout << " Step Number: " << getStepNumber(); + std::cout << " Data Size: " << getDataSize(); + std::cout << " Minor Ver: " << getMinorVersion(); + std::cout << std::endl; + std::cout << std::dec; +} + +int +L1CaloRdoRodInfo::getMinorVersion( ) const +{ + return ( m_version & 0xffff ); +} + +bool +L1CaloRdoRodInfo::getIsRun1( ) const +{ + return ( (unsigned int)this->getMinorVersion() < s_minRun2Version ); +} + +bool +L1CaloRdoRodInfo::getIsRun2( ) const +{ + return ( this->getIsRun3() || (unsigned int)this->getMinorVersion() >= s_minRun2Version ); +} + +bool +L1CaloRdoRodInfo::getIsRun3( ) const +{ + // Run3 fragments have some of bits 12-15 set. + return ( ( this->getRodId() & 0xf000 ) != 0 ); +} + +int +L1CaloRdoRodInfo::getSystemId( ) const +{ + return m_system; +} + +int +L1CaloRdoRodInfo::getRodId( ) const +{ + return m_rod; +} + +int +L1CaloRdoRodInfo::getSourceId( ) const +{ + return ( (m_system<<16) | m_rod ); +} + +int +L1CaloRdoRodInfo::getRunNumber( ) const +{ + return m_run; +} + +int +L1CaloRdoRodInfo::getBcNumber( ) const +{ + return m_bcnum; +} + +int +L1CaloRdoRodInfo::getTriggerType( ) const +{ + return m_triggerType; +} + +int +L1CaloRdoRodInfo::getLevel1Id( ) const +{ + return m_lvl1; +} + +int +L1CaloRdoRodInfo::getDataSize( ) const +{ + return m_size; +} + +int +L1CaloRdoRodInfo::getStepNumber( ) const +{ + return ( (m_detType>>4) & 0xfff); +} + +int +L1CaloRdoRodInfo::getOrbitCount( ) const +{ + return ( (m_detType>>16) & 0xffff); +} + +bool +L1CaloRdoRodInfo::getBcnumMismatch( ) const +{ + return ( m_status1 & 0x1 ); +} + +bool +L1CaloRdoRodInfo::getGlinkTimeout( ) const +{ + return ( m_status1 & 0x4 ); +} + +bool +L1CaloRdoRodInfo::getDataTransportError( ) const +{ + return ( m_status1 & 0x8 ); +} + +bool +L1CaloRdoRodInfo::getRodOverflow( ) const +{ + return ( m_status1 & 0x10 ); +} + +bool +L1CaloRdoRodInfo::getModuleLinkError( ) const +{ + return ( m_status1 & 0x10000 ); +} + +bool +L1CaloRdoRodInfo::getCmmParityError( ) const +{ + return ( m_status1 & 0x20000 ); +} + +bool +L1CaloRdoRodInfo::getGlinkError( ) const +{ + return ( m_status1 & 0x40000 ); +} + +bool +L1CaloRdoRodInfo::getRoiOverflow( ) const +{ + return ( m_status2 & 0x2 ); +} + +bool +L1CaloRdoRodInfo::getTriggerTypeTimeout( ) const +{ + return ( m_status2 & 0x10000 ); +} + +int +L1CaloRdoRodInfo::getModuleStatus( const int module ) const +{ + if ( (module < 0) || (module > 15) ) + return 0; + return m_status[module]; +} + +void +L1CaloRdoRodInfo::setLvl1Id( int lvl1 ) +{ + m_lvl1 = lvl1; +} + +void +L1CaloRdoRodInfo::setSize( int size ) +{ + m_size = size; +} + +void +L1CaloRdoRodInfo::setStatus1( int status ) +{ + m_status1 = status; +} + +void +L1CaloRdoRodInfo::setStatus2( int status ) +{ + m_status2 = status; +} + +void +L1CaloRdoRodInfo::setModuleStatus( int module, int status ) +{ + m_status[module] = status; +} + diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/components/L1CaloFEXByteStream_entries.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/components/L1CaloFEXByteStream_entries.cxx index ed5888a4cf4439abe7ec9eb6e43f6957c13f6f22..e23c196d0c27aa2cbcd08c3bc369fb0e974d486e 100644 --- a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/components/L1CaloFEXByteStream_entries.cxx +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/components/L1CaloFEXByteStream_entries.cxx @@ -1,3 +1,5 @@ #include "../jFexByteStreamTool.h" +#include "../eFexByteStreamTool.h" DECLARE_COMPONENT( jFexByteStreamTool ) +DECLARE_COMPONENT( eFexByteStreamTool ) diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.cxx b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..71ce6c95195ccc516137e027bf15fbfd7a44c437 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.cxx @@ -0,0 +1,158 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ + +//*************************************************************************** +// eFexByteStreamTool - description +// ------------------- +// begin : 11 05 2022 +// email : will@cern.ch +// ***************************************************************************/ + +#include "eFexByteStreamTool.h" +#include "CxxUtils/span.h" +#include "eformat/SourceIdentifier.h" +#include "eformat/Status.h" + +#include "xAODTrigger/eFexEMRoIAuxContainer.h" +#include "xAODTrigger/eFexTauRoIAuxContainer.h" +#include "bytestreamDecoder/L1CaloRdoEfexTob.h" +#include "bytestreamDecoder/L1CaloBsDecoderUtil.h" +#include "bytestreamDecoder/L1CaloRdoRodInfo.h" +#include "bytestreamDecoder/L1CaloBsDecoderRun3.h" + +using ROBF = OFFLINE_FRAGMENTS_NAMESPACE::ROBFragment; +using WROBF = OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment; + +namespace { + // Helper method for creating the containers + template<typename T, typename W> + StatusCode addContainer( + std::map <std::pair<L1CaloRdoFexTob::TobSource, bool>, SG::WriteHandle<T>> &map, + L1CaloRdoFexTob::TobSource source, bool inTime, const SG::WriteHandleKey <T> &handle, + const EventContext &ctx) { + if (!handle.empty()) { + return map.emplace(std::make_pair(source, inTime), + SG::WriteHandle<T>(handle, ctx)).first->second.record( + std::make_unique<T>(), std::make_unique<W>()); + } + return StatusCode::SUCCESS; + } +} + +eFexByteStreamTool::eFexByteStreamTool(const std::string& type, + const std::string& name, + const IInterface* parent) + : base_class(type, name, parent) {} + +StatusCode eFexByteStreamTool::initialize() { + + std::vector<SG::VarHandleKey*> writeKeys = { + &m_eEMTOBWriteKey, &m_eTAUTOBWriteKey, &m_eEMxTOBWriteKey, &m_eTAUxTOBWriteKey }; + std::vector<SG::VarHandleKey*> readKeys = { + &m_eEMTOBReadKey, &m_eTAUTOBReadKey, &m_eEMxTOBReadKey, &m_eTAUxTOBReadKey }; + std::vector<SG::VarHandleKey*> sliceKeys = { + &m_eEMTOBSliceKey, &m_eTAUTOBSliceKey, &m_eEMxTOBSliceKey, &m_eTAUxTOBSliceKey }; + + auto previousMode = ConversionMode::Undefined; + for(size_t i=0;i<writeKeys.size();i++) { + auto mode = getConversionMode(*readKeys.at(i),*writeKeys.at(i),msg()); + ATH_CHECK(mode!=ConversionMode::Undefined); + if(i!=0) ATH_CHECK(mode==previousMode); // if this fails, user is trying to mix encoding and decoding + previousMode = mode; + ATH_CHECK(writeKeys.at(i)->initialize(mode==ConversionMode::Decoding)); + ATH_CHECK(readKeys.at(i)->initialize(mode==ConversionMode::Encoding)); + if(mode==ConversionMode::Decoding && m_multiSlice) { + ATH_CHECK(sliceKeys.at(i)->assign(!writeKeys.at(i)->empty() ? writeKeys.at(i)->key()+"OutOfTime" : "")); + } + ATH_CHECK(sliceKeys.at(i)->initialize(!sliceKeys.at(i)->empty())); + } + + return StatusCode::SUCCESS; +} + +// BS->xAOD conversion +StatusCode eFexByteStreamTool::convertFromBS(const std::vector<const ROBF*>& vrobf, const EventContext& ctx) const { + + + // indices are source (tob or xtob) and bool for in/out of time + std::map<std::pair<L1CaloRdoFexTob::TobSource,bool>,SG::WriteHandle<xAOD::eFexEMRoIContainer>> eContainers; + std::map<std::pair<L1CaloRdoFexTob::TobSource,bool>,SG::WriteHandle<xAOD::eFexTauRoIContainer>> tContainers; + + ATH_CHECK( StatusCode(addContainer<xAOD::eFexEMRoIContainer,xAOD::eFexEMRoIAuxContainer>(eContainers,L1CaloRdoFexTob::TobSource::EfexTob,false,m_eEMTOBWriteKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexEMRoIContainer,xAOD::eFexEMRoIAuxContainer>(eContainers,L1CaloRdoFexTob::TobSource::EfexXtob,false,m_eEMxTOBWriteKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexEMRoIContainer,xAOD::eFexEMRoIAuxContainer>(eContainers,L1CaloRdoFexTob::TobSource::EfexTob,true,m_eEMTOBSliceKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexEMRoIContainer,xAOD::eFexEMRoIAuxContainer>(eContainers,L1CaloRdoFexTob::TobSource::EfexXtob,true,m_eEMxTOBSliceKey,ctx)) ); + + ATH_CHECK( StatusCode(addContainer<xAOD::eFexTauRoIContainer,xAOD::eFexTauRoIAuxContainer>(tContainers,L1CaloRdoFexTob::TobSource::EfexTob,false,m_eTAUTOBWriteKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexTauRoIContainer,xAOD::eFexTauRoIAuxContainer>(tContainers,L1CaloRdoFexTob::TobSource::EfexXtob,false,m_eTAUxTOBWriteKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexTauRoIContainer,xAOD::eFexTauRoIAuxContainer>(tContainers,L1CaloRdoFexTob::TobSource::EfexTob,true,m_eTAUTOBSliceKey,ctx)) ); + ATH_CHECK( StatusCode(addContainer<xAOD::eFexTauRoIContainer,xAOD::eFexTauRoIAuxContainer>(tContainers,L1CaloRdoFexTob::TobSource::EfexXtob,true,m_eTAUxTOBSliceKey,ctx)) ); + + std::list<L1CaloRdoRodInfo> rodInfos; + std::list<L1CaloRdoEfexTob> efexTobs; + L1CaloBsDecoderRun3 decoder; + for (const ROBF* rob : vrobf) { + // Iterate over ROD words and decode + ATH_MSG_DEBUG("Decoding " << rob->rod_ndata() << " ROD words from ROB 0x" << std::hex << rob->rob_source_id() << std::dec); + L1CaloBsDecoderUtil::decodeRodInfo( rob, rodInfos ); + + if(rob->rod_ndata()==0) continue; + + CxxUtils::span data{rob->rod_data(), rob->rod_ndata()}; + auto lastRod = rodInfos.end(); lastRod--; + decoder.decodeEfexTobs( data.begin(), data.end(), efexTobs, lastRod ); + + for(const L1CaloRdoEfexTob& tob : efexTobs) { + // loop over slices ... create separate tobs for each, where there's a word defined + for(size_t slice=0;slice < tob.numSlices(); slice++) { + if (!m_multiSlice && int(slice) != tob.getL1aPos()) continue; // ignore out-of-time slices if multiSlice option = false + if (tob.getWord0(slice)==0) continue; // this tob isn't in this slice + if(tob.getTobType() == L1CaloRdoFexTob::TobType::EM) { + auto cont = eContainers.find({tob.getTobSource(),int(slice)!=tob.getL1aPos()}); + if(cont == eContainers.end()) continue; // not writing this tob collection + cont->second->push_back( std::make_unique<xAOD::eFexEMRoI>() ); + if(tob.getTobSource() == L1CaloRdoFexTob::TobSource::EfexTob) { + cont->second->back()->initialize(tob.getModule(),tob.getCrate(),tob.getWord0(slice)); + } else { + cont->second->back()->initialize(tob.getWord0(slice),tob.getWord1(slice)); + } + } else if (tob.getTobType() == L1CaloRdoFexTob::TobType::Tau) { + auto cont = tContainers.find({tob.getTobSource(),int(slice)!=tob.getL1aPos()}); + if(cont == tContainers.end()) continue; // not writing this tob collection + cont->second->push_back( std::make_unique<xAOD::eFexTauRoI>() ); + if(tob.getTobSource() == L1CaloRdoFexTob::TobSource::EfexTob) { + cont->second->back()->initialize(tob.getModule(),tob.getCrate(),tob.getWord0(slice)); + } else { + cont->second->back()->initialize(tob.getWord0(slice),tob.getWord1(slice)); + } + } + } // timeslice loop + } // tob loop + } // fragment loop + + if(msgLevel(MSG::DEBUG)) { + std::stringstream msg; + for (auto&[k, v]: eContainers) { + msg << v->size() << " " << (k.second ? "out-of-time " : "in-time ") << "eg" << + (k.first == L1CaloRdoFexTob::TobSource::EfexTob ? "" : "x") << "TOB, "; + } + for (auto&[k, v]: tContainers) { + msg << v->size() << " " << (k.second ? "out-of-time " : "in-time ") << "tau" << + (k.first == L1CaloRdoFexTob::TobSource::EfexTob ? "" : "x") << "TOB, "; + } + ATH_MSG_DEBUG("Decoded: " << msg.str()); + } + // this is how to print the in-time em xTOBs: +// for(const auto& tob : *(eContainers[{L1CaloRdoFexTob::TobSource::EfexXtob,false}])) { +// std::cout << tob->eFexNumber() << "." << tob->fpga() << ": " << tob->fpgaEta() << " " << tob->fpgaPhi() << " " << tob->et() << " " << tob->etXTOB() << " " << tob->etTOB() << " " << std::endl; +// } + + return StatusCode::SUCCESS; +} + +StatusCode eFexByteStreamTool::convertToBS(std::vector<OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment*>& , const EventContext& ) { + // not supported yet + return StatusCode::FAILURE; +} + diff --git a/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.h b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.h new file mode 100644 index 0000000000000000000000000000000000000000..516d3a1b112f6ceb1eed0222c03e2b0b03bacff5 --- /dev/null +++ b/Trigger/TrigT1/L1CaloFEX/L1CaloFEXByteStream/src/eFexByteStreamTool.h @@ -0,0 +1,95 @@ +/* + Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration +*/ + +//*************************************************************************** +// eFexByteStreamTool - description +// ------------------- +// begin : 11 05 2022 +// email : will@cern.ch +// ***************************************************************************/ + +#ifndef EFEXBYTESTREAMTOOL_H +#define EFEXBYTESTREAMTOOL_H + +// Trigger includes +#include "TrigT1ResultByteStream/IL1TriggerByteStreamTool.h" + +// Athena includes +#include "AthenaBaseComps/AthAlgTool.h" + +#include "xAODTrigger/eFexEMRoIContainer.h" +#include "xAODTrigger/eFexTauRoIContainer.h" + +#include "bytestreamDecoder/L1CaloRdoEfexTob.h" + +// Gaudi includes +#include "Gaudi/Property.h" + +/** @class eFEXRoIByteStreamTool + * @brief Implementation of a tool for L1 RoI conversion from BS to xAOD and from xAOD to BS + * (IL1TriggerByteStreamTool interface) + **/ +class eFexByteStreamTool : public extends<AthAlgTool, IL1TriggerByteStreamTool> { + public: + eFexByteStreamTool(const std::string& type, const std::string& name, const IInterface* parent); + virtual ~eFexByteStreamTool() override = default; + + // ------------------------- IAlgTool methods -------------------------------- + virtual StatusCode initialize() override; + + // ------------------------- IL1TriggerByteStreamTool methods ---------------------- + /// BS->xAOD conversion + virtual StatusCode convertFromBS(const std::vector<const OFFLINE_FRAGMENTS_NAMESPACE::ROBFragment*>& vrobf, const EventContext& eventContext)const override; + + /// xAOD->BS conversion + virtual StatusCode convertToBS(std::vector<OFFLINE_FRAGMENTS_NAMESPACE_WRITE::ROBFragment*>& vrobf, const EventContext& eventContext) override; + + /// Declare ROB IDs for conversion + virtual const std::vector<uint32_t>& robIds() const override { + return m_robIds.value(); + } + + private: + + + // ------------------------- Properties -------------------------------------- + // ROBIDs property required by the interface + Gaudi::Property<std::vector<uint32_t>> m_robIds {this, "ROBIDs", {}, "List of ROB IDs required for conversion to/from xAOD RoI"}; + + // Write handle keys for the L1Calo EDMs for BS->xAOD mode of operation + + + // Is multi-slice data decoding required? + Gaudi::Property<bool> m_multiSlice { + this, "multiSlice", true, "Decode data from all time slices (false = just from triggered BC)"}; + + // Only write keys should be set to non-empty string in python configuration if the tool is in BS->xAOD mode of operation + // Note that these are the keys for the triggered time slice. In multi-slice mode data from crossings + // before or after this will be stored in separate containers whose keys have the suffix "OutOfTime" + SG::WriteHandleKey<xAOD::eFexEMRoIContainer> m_eEMTOBWriteKey { + this, "eEMTOBContainerWriteKey", "", "Write handle key to eEM TOB (RoI) container for conversion from ByteStream"}; + SG::WriteHandleKey<xAOD::eFexEMRoIContainer> m_eEMxTOBWriteKey { + this, "eEMxTOBContainerWriteKey", "", "Write handle key to eEM xTOB container for conversion from ByteStream"}; + SG::WriteHandleKey<xAOD::eFexTauRoIContainer> m_eTAUTOBWriteKey { + this, "eTAUTOBContainerWriteKey", "", "Write handle key to eTAU TOB (RoI) container for conversion from ByteStream"}; + SG::WriteHandleKey<xAOD::eFexTauRoIContainer> m_eTAUxTOBWriteKey { + this, "eTAUxTOBContainerWriteKey", "", "Write handle key to eTAU xTOB container for conversion from ByteStream"}; + // Only read keys should be set to non-empty string in python configuration if the tool is in xAOD->BS mode of operation + SG::ReadHandleKey<xAOD::eFexEMRoIContainer> m_eEMTOBReadKey { + this, "eEMTOBContainerReadKey", "", "Read handle key to eEM TOB (RoI) container for conversion from ByteStream"}; + SG::ReadHandleKey<xAOD::eFexEMRoIContainer> m_eEMxTOBReadKey { + this, "eEMxTOBContainerReadKey", "", "Read handle key to eEM xTOB container for conversion from ByteStream"}; + SG::ReadHandleKey<xAOD::eFexTauRoIContainer> m_eTAUTOBReadKey { + this, "eTAUTOBContainerReadKey", "", "Read handle key to eTAU TOB (RoI) container for conversion from ByteStream"}; + SG::ReadHandleKey<xAOD::eFexTauRoIContainer> m_eTAUxTOBReadKey { + this, "eTAUxTOBContainerReadKey", "", "Read handle key to eTAU xTOB container for conversion from ByteStream"}; + // Additional keys for multi-slice TOB and xTOB readout (values to be defined in initialize based on the keys specified above) + SG::WriteHandleKey<xAOD::eFexEMRoIContainer> m_eEMTOBSliceKey; + SG::WriteHandleKey<xAOD::eFexEMRoIContainer> m_eEMxTOBSliceKey; + SG::WriteHandleKey<xAOD::eFexTauRoIContainer> m_eTAUTOBSliceKey; + SG::WriteHandleKey<xAOD::eFexTauRoIContainer> m_eTAUxTOBSliceKey; + +}; + +#endif // EFEXBYTESTREAMTOOL_H diff --git a/Trigger/TrigT1/TrigT1ResultByteStream/python/TrigT1ResultByteStreamConfig.py b/Trigger/TrigT1/TrigT1ResultByteStream/python/TrigT1ResultByteStreamConfig.py index 3793cafc6e7bbfa4b4f21537b147c01cab9d7f37..ae9cd0aa44add12e67ef4ea407be83d78d14bf56 100644 --- a/Trigger/TrigT1/TrigT1ResultByteStream/python/TrigT1ResultByteStreamConfig.py +++ b/Trigger/TrigT1/TrigT1ResultByteStream/python/TrigT1ResultByteStreamConfig.py @@ -191,16 +191,16 @@ def L1TriggerByteStreamEncoderCfg(flags): if __name__ == '__main__': from AthenaConfiguration.AllConfigFlags import ConfigFlags as flags from AthenaCommon.Logging import logging - from AthenaCommon.Constants import DEBUG + from AthenaCommon.Constants import DEBUG,WARNING import sys log = logging.getLogger('TrigT1ResultByteStreamConfig') log.setLevel(DEBUG) if len(sys.argv) != 4: - log.error('usage: python -m TrigT1ResultByteStream.TrigT1ResultByteStreamConfig subsystem file') + log.error('usage: python -m TrigT1ResultByteStream.TrigT1ResultByteStreamConfig subsystem file nevents') sys.exit(1) - supportedSubsystems = ['jFex'] + supportedSubsystems = ['jFex','eFex'] subsystem = sys.argv[1] filename = sys.argv[2] events = int(sys.argv[3]) @@ -208,7 +208,7 @@ if __name__ == '__main__': log.error(f'subsystem "{subsystem}" not one of supported subsystems: {supportedSubsystems}') sys.exit(1) - flags.Exec.OutputLevel = DEBUG + flags.Exec.OutputLevel = WARNING flags.Exec.MaxEvents = events flags.Input.Files = [filename] flags.Concurrency.NumThreads = 1 @@ -240,8 +240,21 @@ if __name__ == '__main__': outputEDM += addEDM('xAOD::jFexSumETRoIContainer', jFexTool.jTERoIContainerWriteKey.Path) outputEDM += addEDM('xAOD::jFexMETRoIContainer' , jFexTool.jXERoIContainerWriteKey.Path) + if subsystem=='eFex': + from L1CaloFEXByteStream.L1CaloFEXByteStreamConfig import eFexByteStreamToolCfg + eFexTool = eFexByteStreamToolCfg('eFexBSDecoder', flags) + decoderTools += [eFexTool] + # TOB containers + outputEDM += addEDM('xAOD::eFexEMRoIContainer', eFexTool.eEMTOBContainerWriteKey.Path) + outputEDM += addEDM('xAOD::eFexTauRoIContainer', eFexTool.eTAUTOBContainerWriteKey.Path) + # xTOB containers + outputEDM += addEDM('xAOD::eFexEMRoIContainer', eFexTool.eEMxTOBContainerWriteKey.Path) + outputEDM += addEDM('xAOD::eFexTauRoIContainer', eFexTool.eTAUxTOBContainerWriteKey.Path) + + + decoderAlg = CompFactory.L1TriggerByteStreamDecoderAlg(name="L1TriggerByteStreamDecoder", - DecoderTools=decoderTools) + DecoderTools=decoderTools, OutputLevel=DEBUG) acc.addEventAlgo(decoderAlg, sequenceName='AthAlgSeq') from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg