diff --git a/Event/DigiEvent/include/Event/UTDigit.h b/Event/DigiEvent/include/Event/UTDigit.h index 1bdbcf05e4e82a53ecba60a1241ea126092329ed..0e4da0afbb24f425ad37994d047fed5385698714 100644 --- a/Event/DigiEvent/include/Event/UTDigit.h +++ b/Event/DigiEvent/include/Event/UTDigit.h @@ -34,7 +34,8 @@ namespace LHCb { * Upstream tracker digitization class * * @author Xuhao Yuan (based on code by Andy Beiter and Matthew Needham) - * + * @author Wojciech Krupa + */ class UTDigit final : public KeyedObject<LHCb::Detector::UT::ChannelID> { @@ -75,6 +76,26 @@ namespace LHCb { digit.m_daqID = 0; } + /// constructor for NZS events with mcm parameters + UTDigit( LHCb::Detector::UT::ChannelID utchannelID, float charge, unsigned int utdaqID, int asic, signed int mcmVal, + unsigned int mcmStrip ) + : KeyedObject<LHCb::Detector::UT::ChannelID>{utchannelID} + , m_depositedCharge( charge ) + , m_daqID( utdaqID ) + , m_asicNum( asic ) + , m_mcmVal( mcmVal ) + , m_mcmStrip( mcmStrip ) { + assert( utchannelID.isUT() && "Non-UT channelID" ); + } + + /// constructor for simplified NZS events with mcm parameters but without daqid and asic_num + UTDigit( LHCb::Detector::UT::ChannelID utchannelID, float charge, signed int mcmVal, unsigned int mcmStrip ) + : KeyedObject<LHCb::Detector::UT::ChannelID>{utchannelID} + , m_depositedCharge( charge ) + , m_mcmVal( mcmVal ) + , m_mcmStrip( mcmStrip ) { + assert( utchannelID.isUT() && "Non-UT channelID" ); + } // Move assignment operator UTDigit& operator=( UTDigit&& other ) noexcept = default; @@ -109,6 +130,27 @@ namespace LHCb { /// short cut for strip [[nodiscard]] unsigned int strip() const { return channelID().strip(); }; + /// short cut for stave + [[nodiscard]] unsigned int stave() const { return channelID().stave(); }; + + /// short cut for module + [[nodiscard]] unsigned int module() const { return channelID().module(); }; + + /// short cut for face + [[nodiscard]] unsigned int face() const { return channelID().face(); }; + + /// short cut for side + [[nodiscard]] unsigned int side() const { return channelID().side(); }; + + /// short cut for asic_num + [[nodiscard]] unsigned int asic() const { return m_asicNum; }; + + /// short cut for mcm + [[nodiscard]] signed int mcmVal() const { return m_mcmVal; }; + + /// short cut for mcmStrip + [[nodiscard]] unsigned int mcmStrip() const { return m_mcmStrip; }; + /// Print the unique sector name [[nodiscard]] std::string sectorName() const; @@ -135,6 +177,9 @@ namespace LHCb { private: float m_depositedCharge{0.0}; ///< charge deposited on strip unsigned int m_daqID{0}; + int m_asicNum{-1}; + signed int m_mcmVal{0}; + unsigned int m_mcmStrip{0}; }; // class UTDigit diff --git a/UT/UTDAQ/CMakeLists.txt b/UT/UTDAQ/CMakeLists.txt index c4da2965f3698c401a98e568e84458e6a612e844..27a53f60f41c98ac493938afe5b7b7e845cd0bc2 100644 --- a/UT/UTDAQ/CMakeLists.txt +++ b/UT/UTDAQ/CMakeLists.txt @@ -38,9 +38,10 @@ gaudi_add_module(UTDAQ src/component/UTRawBankMonitor.cpp src/component/UTReadoutTool.cpp src/component/UTRawBankToUTDigitsAlg.cpp + src/component/UTRawBankToUTNZSDigitsAlg.cpp LINK LHCb::DAQEventLib - LHCb::DAQUtilsLib + LHCb::DAQUtilsLib LHCb::DetDescLib LHCb::DigiEvent LHCb::LHCbKernel @@ -53,3 +54,12 @@ gaudi_add_module(UTDAQ ) gaudi_add_tests(QMTest) + +if(NOT USE_DD4HEP) + set_property( + TEST + UTDAQ.ut_decoding_nzs + PROPERTY + DISABLED TRUE + ) +endif() diff --git a/UT/UTDAQ/src/component/UTRawBankToUTNZSDigitsAlg.cpp b/UT/UTDAQ/src/component/UTRawBankToUTNZSDigitsAlg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..218c6226abb68f285827b1d74e03f2a91dd6fbd2 --- /dev/null +++ b/UT/UTDAQ/src/component/UTRawBankToUTNZSDigitsAlg.cpp @@ -0,0 +1,210 @@ +/*****************************************************************************\ +* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/** @class UTRawBankToUTNZSDigitsAlg + * + * Algorithm to decode UTNZSRawBank to UTDigits (TELL40 NZS decoder) + * Implementation file for class : UTNZSRawBankToUTDigitsAlg + * + * - \b BankLocation: Location of UTRawBank + * - \b OutputDigitData: Location of output UTDigit + * + * @author Wojciech Krupa (based on code by Xuhao Yuan, wokrupa@cern.ch) + * @date 2021-04-19 / 2023-05-25 / 2024-03-13 + */ + +#include "Event/RawBank.h" +#include "Event/RawEvent.h" +#include "Event/UTDigit.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/UTDAQBoard.h" +#include "Kernel/UTDAQDefinitions.h" +#include "Kernel/UTDAQID.h" +#include "Kernel/UTNZSDecoder.h" +#include "LHCbAlgs/Transformer.h" +#include "UTDAQ/UTDAQHelper.h" +#include <algorithm> +#include <bitset> +#include <cstdint> +#include <iomanip> +#include <string> +#include <vector> + +typedef std::vector<std::pair<unsigned int, int>> BanksTypes; + +class UTRawBankToUTNZSDigitsAlg : public LHCb::Algorithm::Transformer<LHCb::UTDigits( const LHCb::RawBank::View& )> { + +public: + /// Standard constructor + UTRawBankToUTNZSDigitsAlg( const std::string& name, ISvcLocator* pSvcLocator ) + : Transformer{name, pSvcLocator, {{"UTBank", {}}}, {{"OutputDigitData", LHCb::UTDigitLocation::UTDigits}}} {} + + LHCb::UTDigits operator()( const LHCb::RawBank::View& ) const override; ///< Algorithm execution + + // Counters for decoded banks + mutable Gaudi::Accumulators::Counter<> m_validHeaders{this, "#Nb Valid UT banks"}; + mutable Gaudi::Accumulators::Counter<> m_validDigits{this, "#Nb Valid UT digits"}; + mutable Gaudi::Accumulators::Counter<> m_invalidBanks{this, "#Nb Invalid banks"}; + mutable Gaudi::Accumulators::Counter<> m_errors_insert{this, "#Error - failure during inserting hit in TES"}; + +private: + // The readout tool for handling DAQID and ChannelID + ToolHandle<IUTReadoutTool> readoutTool{this, "ReadoutTool", "UTReadoutTool"}; +}; + +// Declaration of the Algorithm Factory +DECLARE_COMPONENT( UTRawBankToUTNZSDigitsAlg ) + +using namespace LHCb; + +//============================================================================= +// Main execution +//============================================================================= + +LHCb::UTDigits UTRawBankToUTNZSDigitsAlg::operator()( const LHCb::RawBank::View& banks ) const { + + LHCb::UTDigits digits; + + for ( const LHCb::RawBank* bank : banks ) { + + if ( msgLevel( MSG::DEBUG ) ) { + debug() << "------------------------- ------- UT RAW BANK ------------------------------- -----------" << endmsg; + debug() << "Bank: 0x" << std::uppercase << std::hex << bank->sourceID() << endmsg; + debug() << " Size: " << std::dec << bank->size() << "B" << endmsg; + debug() << " Type: " << bank->type() << endmsg; + debug() << " Version: " << bank->version() << endmsg; + debug() << "| | HEADER | LANE5 | LANE4 | LANE3 | LANE2 " + "| LANE1 | LANE0 |" + << endmsg; + // Dump raw UT bank + u_int8_t iw2 = 0; + unsigned int counter = 0; + for ( const u_int8_t* w = bank->begin<u_int8_t>(); w != bank->end<u_int8_t>(); ++w ) { + ++iw2; + if ( ( iw2 - 1 ) % 32 == 0 ) { + counter++; + if ( counter % 34 == 0 ) { + debug() << endmsg; + debug() << "---------------------------------------------------------------------------------------------" + "-------------------------------" + << endmsg; + } + debug() << endmsg; + debug() << "0x" << std::setfill( '0' ) << std::setw( 2 ) << std::hex << unsigned( iw2 ) << " "; + } + if ( ( iw2 - 1 ) % 4 == 0 ) debug() << " | "; + + debug() << std::uppercase << std::hex << std::setw( 2 ) << unsigned( *w ) << " "; + } + debug() << endmsg; + } + + auto decode = [&]( UTNZSDecoder<::UTDAQ::version::v5> decoder ) { + if ( msgLevel( MSG::DEBUG ) ) { + std::string bxid = std::bitset<12>( decoder.getBXID() ).to_string(); + std::string flag = std::bitset<4>( decoder.getflags() ).to_string(); + debug() << "Bxid: " << bxid << " " << std::dec << decoder.getBXID() << " Flag bits: " << flag << endmsg; + debug() << "Clusters in lanes from 0 to 5: "; + for ( unsigned int lane = 0; lane < 6; lane++ ) { debug() << decoder.nClusters( lane ) << " "; } + debug() << endmsg; + } + + // Decode NZS event + for ( const UTNZSWord& aWord : decoder.posRange() ) { + try { + + if ( msgLevel( MSG::DEBUG ) ) { + std::string binary = std::bitset<16>( aWord.value() ).to_string(); + debug() << "----------- DECODED DIGIT --------------------" << endmsg; + debug() << "Binary: " << binary << " Hex: " << std::hex << std::setfill( '0' ) << std::uppercase + << std::setw( 4 ) << aWord.value() << endmsg; + debug() << "Position in the bank: " << std::dec << aWord.pos() << endmsg; + debug() << "Lane: " << std::dec << aWord.lane() << " ASIC: " << aWord.asic_num() << endmsg; + } + // Decode left digit of the NZS Word + if ( aWord.not_nzs_header() ) { + ++m_validDigits; + UTDAQID daqidL = + UTDAQID( ( UTDAQID::BoardID )( LHCb::UTDAQ::boardIDfromSourceID( bank->sourceID() ) ), + ( UTDAQID::LaneID )( aWord.lane() ), ( UTDAQID::ChannelID )( aWord.channelIDL() & 0x1ff ) ); + + Detector::UT::ChannelID channelIDL = readoutTool->daqIDToChannelID( daqidL ); + + if ( msgLevel( MSG::DEBUG ) ) { + debug() << "Channel from data bank: " << std::dec << aWord.channelIDL() + << " ADC from data bank: " << static_cast<int16_t>( (int8_t)aWord.adcL() ) << endmsg; + debug() << daqidL << endmsg; + debug() << channelIDL << endmsg; + debug() << "Left Channel: " << aWord.channelIDL() + << " ADC: " << static_cast<int16_t>( (int8_t)aWord.adcL() ) << endmsg; + } + try { + digits.insert( new UTDigit( channelIDL, (int8_t)aWord.adcL(), daqidL.id(), aWord.asic_num(), + aWord.mcm_val(), aWord.mcm_ch() ), + channelIDL ); + } catch ( ... ) { + if ( msgLevel( MSG::DEBUG ) ) debug() << "Problem with inserting" << endmsg; + ++m_errors_insert; + continue; + } + } else { + if ( msgLevel( MSG::DEBUG ) ) debug() << "Skipping this digit" << endmsg; + } + // Decode right digit of the NZS Word + if ( aWord.not_nzs_header() ) { + ++m_validDigits; + UTDAQID daqidR = + UTDAQID( ( UTDAQID::BoardID )( LHCb::UTDAQ::boardIDfromSourceID( bank->sourceID() ) ), + ( UTDAQID::LaneID )( aWord.lane() ), ( UTDAQID::ChannelID )( aWord.channelIDR() & 0x1ff ) ); + + Detector::UT::ChannelID channelIDR = readoutTool->daqIDToChannelID( daqidR ); + + if ( msgLevel( MSG::DEBUG ) ) { + std::string binary = std::bitset<16>( aWord.value() ).to_string(); + debug() << "Channel from data bank: " << std::dec << aWord.channelIDR() + << " ADC from data bank: " << static_cast<int16_t>( (int8_t)aWord.adcR() ) << endmsg; + debug() << daqidR << endmsg; + debug() << channelIDR << endmsg; + debug() << "Right Channel: " << aWord.channelIDR() + << " ADC: " << static_cast<int16_t>( (int8_t)aWord.adcR() ) << endmsg; + } + try { + digits.insert( new UTDigit( channelIDR, (int8_t)aWord.adcR(), daqidR.id(), aWord.asic_num(), + aWord.mcm_val(), aWord.mcm_ch() ), + channelIDR ); + } catch ( ... ) { + if ( msgLevel( MSG::DEBUG ) ) debug() << "Problem with inserting" << endmsg; + ++m_errors_insert; + continue; + } + } else { + if ( msgLevel( MSG::DEBUG ) ) debug() << "Skipping this digit" << endmsg; + } + } catch ( const std::exception& ex ) { + if ( msgLevel( MSG::DEBUG ) ) debug() << ex.what() << endmsg; + } catch ( ... ) { + if ( msgLevel( MSG::DEBUG ) ) debug() << "Uknown type issue for: " << bank->sourceID() << endmsg; + } + } + }; + + if ( ::UTDAQ::version{bank->version()} == ::UTDAQ::version::v5 ) { + decode( UTNZSDecoder<::UTDAQ::version::v5>{*bank} ); + ++m_validHeaders; + } else { + debug() << "Wrong version of the RawBank" << endmsg; + ++m_invalidBanks; + } + } + return digits; +} + +//============================================================================= diff --git a/UT/UTDAQ/tests/options/ut_test_nzs.py b/UT/UTDAQ/tests/options/ut_test_nzs.py new file mode 100644 index 0000000000000000000000000000000000000000..bcbdb3a809b22567ac7aec09a348f35205610c0b --- /dev/null +++ b/UT/UTDAQ/tests/options/ut_test_nzs.py @@ -0,0 +1,50 @@ +############################################################################### +# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +############################################################################### +import logging +from Gaudi.Configuration import VERBOSE, DEBUG, INFO +from PRConfig.TestFileDB import test_file_db +from DDDB.CheckDD4Hep import UseDD4Hep +from PyConf.Algorithms import UTRawBankToUTNZSDigitsAlg + +from PyConf.application import (configure_input, configure, CompositeNode, + make_odin, default_raw_banks) + +# DD4HEP configuration +if UseDD4Hep: + from Configurables import LHCb__Det__LbDD4hep__DD4hepSvc as DD4hepSvc + dd4hepsvc = DD4hepSvc() + dd4hepsvc.GeometryVersion = "run3/trunk" + dd4hepsvc.GeometryMain = "LHCb.xml" + dd4hepsvc.DetectorList = ["/world", "UT"] + +# I/O configuration +options = test_file_db["ut-nzs-2024"].make_lbexec_options( + simulation=False, + python_logging_level=logging.INFO, + evt_max=100, + data_type='Upgrade', + geometry_version="run3/trunk", + conditions_version='master') + +# Setting up algorithms pipeline +configure_input(options) +odin = make_odin() +decoder_nzs = UTRawBankToUTNZSDigitsAlg( + name="UTRawToNZSDigits", + UTBank=default_raw_banks("UTNZS"), + OutputLevel=INFO) +decoder_nzs_err = UTRawBankToUTNZSDigitsAlg( + name="UTErrRawToNZSDigits", + UTBank=default_raw_banks("UTError"), + OutputLevel=INFO) + +top_node = CompositeNode("UT_NZSDecoding", [decoder_nzs, decoder_nzs_err]) +configure(options, top_node) diff --git a/UT/UTDAQ/tests/qmtest/ut_decoding_nzs.qmt b/UT/UTDAQ/tests/qmtest/ut_decoding_nzs.qmt new file mode 100644 index 0000000000000000000000000000000000000000..eb06a9bf802ddc62f47e913dd30db924c4b236af --- /dev/null +++ b/UT/UTDAQ/tests/qmtest/ut_decoding_nzs.qmt @@ -0,0 +1,25 @@ +<?xml version="1.0" ?><!DOCTYPE extension PUBLIC '-//QM/2.3/Extension//EN' 'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'> +<!-- + (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration + + This software is distributed under the terms of the GNU General Public + Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". + + In applying this licence, CERN does not waive the privileges and immunities + granted to it by virtue of its status as an Intergovernmental Organization + or submit itself to any jurisdiction. +--> +<extension class="GaudiTest.GaudiExeTest" kind="test"> + <argument name="program"><text>gaudirun.py</text></argument> + <argument name="unsupported_platforms"><set> + <text>detdesc</text> + </set></argument> + <argument name="args"><set> + <text>$UTDAQROOT/tests/options/ut_test_nzs.py</text> + </set></argument> + <argument name="validator"><text> +countErrorLines({"FATAL": 0, "ERROR": 0, "WARNING": 0}) + </text></argument> +</extension> + + diff --git a/UT/UTKernel/include/Kernel/UTDAQID.h b/UT/UTKernel/include/Kernel/UTDAQID.h index 84faa67d8f98cae7f734be9094d87fc42a10b777..21f75631bbd422286df54c2bf87104b6fd86c960 100644 --- a/UT/UTKernel/include/Kernel/UTDAQID.h +++ b/UT/UTKernel/include/Kernel/UTDAQID.h @@ -33,16 +33,14 @@ class UTDAQID final { template <masks m> [[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { - constexpr auto b = - __builtin_ctz( static_cast<unsigned int>( m ) ); // FIXME: C++20 replace __builtin_ctz with std::countr_zero + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); return ( i & static_cast<unsigned int>( m ) ) >> b; } template <masks m> [[nodiscard]] static constexpr unsigned int shift( unsigned int i ) { - constexpr auto b = - __builtin_ctz( static_cast<unsigned int>( m ) ); // FIXME: C++20 replace __builtin_ctz with std::countr_zero - auto v = ( i << static_cast<unsigned int>( b ) ); + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + auto v = ( i << static_cast<unsigned int>( b ) ); assert( extract<m>( v ) == i ); return v; } diff --git a/UT/UTKernel/include/Kernel/UTNZSDecoder.h b/UT/UTKernel/include/Kernel/UTNZSDecoder.h new file mode 100644 index 0000000000000000000000000000000000000000..e23a021b125fcc71ef8045b67267e5a24d8e248c --- /dev/null +++ b/UT/UTKernel/include/Kernel/UTNZSDecoder.h @@ -0,0 +1,175 @@ +/*****************************************************************************\ +* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ + +/** @class + * Implementation of UTRawBank decoder + * + * @author Wojciech Krupa (based on code by Xuhao Yuan, wokrupa@cern.ch) + * @date 2020-06-17 / 2023-06-08 + */ + +#pragma once +#include "Event/RawBank.h" +#include "Kernel/STLExtensions.h" +#include "SiDAQ/SiRawBankDecoder.h" +#include "SiDAQ/SiRawBufferWord.h" +#include "UTClusterWord.h" +#include "UTDAQ/UTHeaderWord.h" +#include "UTDAQ/UTNZSWord.h" +#include "UTDAQDefinitions.h" +#include <cassert> + +template <UTDAQ::version> +class UTNZSDecoder; + +template <> +class UTNZSDecoder<UTDAQ::version::v4> : public SiRawBankDecoder<UTClusterWord> { +public: + explicit UTNZSDecoder( const LHCb::RawBank& bank ) : SiRawBankDecoder<UTClusterWord>{bank.data()} { + assert( UTDAQ::version{bank.version()} == UTDAQ::version::v4 ); + } +}; + +template <> +class UTNZSDecoder<UTDAQ::version::v5> final { + enum class banksize { left_bit = 10, center_bit = 16, right_bit = 15 }; + + // Method which allow moving within lane by jump betweens 256b TELL40 lines. It returns 1, 16 (jump to next line), 17, + // 32, ... for N (numb of hits) = 1, 2, 3, ... + constexpr static unsigned int nskip( unsigned int digLane ) { + return static_cast<unsigned int>( banksize::center_bit ) * ( ( digLane + 1 ) / 2 - 1 ) + ( 1 - ( digLane % 2 ) ); + } + + constexpr static unsigned int get_digits( unsigned int input ) { + if ( input == 0x08 ) return 264; + if ( input == 0xC6 ) return 198; + if ( input == 0x84 ) return 132; + if ( input == 0x42 ) + return 66; + else + return 0; + } + + // Method for reading number of hits in each line for 64b (2x32b) event header. + constexpr static UTDAQ::digiVec make_digiVec( LHCb::span<const uint32_t, 2> header ) { + UTHeaderWord headerL{header[0]}, headerR{header[1]}; + return {get_digits( headerR.nClustersLane0() ), get_digits( headerR.nClustersLane1() ), + get_digits( headerR.nClustersLane2() ), get_digits( headerR.nClustersLane3() ), + get_digits( headerL.nClustersLane4() ), get_digits( headerL.nClustersLane5() )}; + } + + constexpr static UTDAQ::headerVec make_headerVec( LHCb::span<const uint32_t, 2> header ) { + UTHeaderWord headerL{header[0]}, headerR{header[1]}; + return {headerL.getFlags(), headerL.getBXID()}; + } + +public: + explicit UTNZSDecoder( const LHCb::RawBank& bank ) + : UTNZSDecoder{bank.range<uint16_t>().subspan( 4 ), bank.range<uint32_t>().first<2>()} { + assert( UTDAQ::version{bank.version()} == UTDAQ::version::v5 ); + if ( bank.type() == LHCb::RawBank::UT && + ( bank.size() / sizeof( unsigned int ) != ( ( nClusters() + 1 ) / 2 ) * 8 && + nClusters() != 0 ) ) { // TODO: add a dedicated UT DAQ StatusCode category, and + // throw a GaudiException with a value inside that + // category + std::string str_msg = "Error: unexpected UT RawBank size, expected: "; + str_msg.append( std::to_string( ( ( nClusters() + 1 ) / 2 ) * 8 ) ); + str_msg.append( " received: " + std::to_string( bank.size() / sizeof( unsigned int ) ) ); + const char* cc_msge = str_msg.c_str(); + throw std::runtime_error{cc_msge}; + } + } + + UTNZSDecoder( LHCb::span<const uint16_t> body, LHCb::span<const uint32_t, 2> header ) + : m_bank( body ), m_nDigits{make_digiVec( header )}, m_header{make_headerVec( header )} {} + + class pos_range final { // NON clustering iterator. + + private: + const LHCb::span<const uint16_t> m_bank; // Data Bank 16b in lane (max 2 hits) + UTDAQ::digiVec m_Digit; // Number of hits in each lane + struct Sentinel final {}; + + class Iterator final { + const LHCb::span<const uint16_t> m_bank; + UTDAQ::digiVec m_iterDigit; + unsigned int m_lane = UTDAQ::max_nlane; + unsigned int m_pos = 0; // Current position in lane + unsigned int m_maxpos = 0; // Max position in lane (determined by number of hits in lane) + unsigned int m_asic_num; // ASIC_num + 8b of NZS header + unsigned int m_nzs_header; // Remaining 16b of NZS header + unsigned int m_inlane_counter = 0; // + + public: + Iterator( LHCb::span<const uint16_t> bank, const UTDAQ::digiVec m_Digit ) : m_bank{bank}, m_iterDigit{m_Digit} { + auto PosLane = std::find_if( m_iterDigit.begin(), m_iterDigit.end(), + []( unsigned int& element ) { return element != 0; } ); + if ( PosLane != m_iterDigit.end() ) { + m_lane = std::distance( m_iterDigit.begin(), PosLane ); // first non-zero lane + m_pos = static_cast<unsigned int>( banksize::left_bit ) - 2 * m_lane; // initial pos (10, 8, 6, 4, 2, 0) + m_maxpos = m_pos + nskip( m_iterDigit[m_lane] ); // max posiotion (initial + distance distance depends on + } // number of hits ) + } + + // dereferencing + [[nodiscard]] constexpr UTNZSWord operator*() const { + return UTNZSWord{m_bank[m_pos], m_lane, m_asic_num, m_nzs_header, m_inlane_counter}; + } + + constexpr Iterator& operator++() { + + if ( m_inlane_counter % 66 == 0 ) m_nzs_header = m_bank[m_pos]; + if ( m_inlane_counter % 66 == 1 ) m_asic_num = m_bank[m_pos]; + m_inlane_counter++; + m_pos += ( ( m_pos % 2 ) ? static_cast<unsigned int>( banksize::right_bit ) + : 1u ); // if we read odd word we moving to the second in the line (1), otherwise jump + + if ( m_pos > m_maxpos ) { + m_inlane_counter = 0; // to next line + ++m_lane; // if we reach last hit, we jump to the next line + while ( m_lane < UTDAQ::max_nlane && m_iterDigit[m_lane] == 0 ) { + ++m_lane; + } // it there is no hits in lane, we jump to next one + if ( m_lane < UTDAQ::max_nlane ) { // if we didn't reach the end of lanes + m_pos = static_cast<unsigned int>( banksize::left_bit ) - 2 * m_lane; // changing initial and max position + m_maxpos = m_pos + nskip( m_iterDigit[m_lane] ); + } + } + + return *this; + } + + constexpr bool operator!=( Sentinel ) const { return m_lane < UTDAQ::max_nlane; } + }; + + public: + constexpr pos_range( LHCb::span<const uint16_t> bank, const UTDAQ::digiVec& ClusterVec ) + : m_bank{std::move( bank )}, m_Digit{ClusterVec} {} + [[nodiscard]] auto begin() const { return Iterator{m_bank, m_Digit}; } + [[nodiscard]] constexpr auto end() const { return Sentinel{}; } + }; + + [[nodiscard]] constexpr unsigned int nClusters() const { + static_assert( UTDAQ::max_nlane == 6 ); + return std::max( {m_nDigits[0], m_nDigits[1], m_nDigits[2], m_nDigits[3], m_nDigits[4], m_nDigits[5]} ); + } + [[nodiscard]] constexpr unsigned int nClusters( unsigned int laneID ) const { return m_nDigits[laneID]; } + [[nodiscard]] constexpr unsigned int getflags() const { return m_header[0]; } + [[nodiscard]] constexpr unsigned int getBXID() const { return m_header[1]; } + [[nodiscard]] auto posRange() const { + return pos_range{m_bank.first( ( ( nClusters() + 1 ) / 2 ) * 16 - 4 ), m_nDigits}; + } + +private: + LHCb::span<const uint16_t> m_bank; + UTDAQ::digiVec m_nDigits; + UTDAQ::headerVec m_header; +}; diff --git a/UT/UTKernel/include/UTDAQ/UTNZSWord.h b/UT/UTKernel/include/UTDAQ/UTNZSWord.h new file mode 100644 index 0000000000000000000000000000000000000000..f48aa8ed19289a6ba9ca630288df59481894c43d --- /dev/null +++ b/UT/UTKernel/include/UTDAQ/UTNZSWord.h @@ -0,0 +1,116 @@ +/*****************************************************************************\ +* (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +#pragma once +#include <iostream> +#include <string> + +/** @class UTNZSWord UTNZSWord.h "UTDAQ/UTNZSWord.h" + * + * Class for encapsulating header word in RAW data format + * for the UT Si detectors. + * + * @author Wojciech Krupa (based on code by Xuhao Yuan, wokrupa@cern.ch) + * @date 2020-06-17 / 2023-06-12 + */ + +class UTNZSWord final { + enum class mask { + strip = 0xFFE0, + adc = 0x1F, + adcL = 0xFF, + adcR = 0xFF00, + mcm_val = 0xFC0000, + mcm_ch = 0x03FC00, + mem_sep = 0x0003FE, + ev_par = 0x000001 + }; + + static constexpr unsigned int maxstrip = 512; + + template <mask m> + [[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = __builtin_ctz( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; + } + + template <mask m> + [[nodiscard]] static constexpr unsigned int shift( unsigned int i ) { + constexpr auto b = __builtin_ctz( static_cast<unsigned int>( m ) ); + auto v = ( i << static_cast<unsigned int>( b ) ); + assert( extract<m>( v ) == i ); + return v; + } + +public: + /** constructer with int + @param value + */ + explicit constexpr UTNZSWord( unsigned int value ) : m_value( value ) {} + + constexpr UTNZSWord( unsigned int value, unsigned int nlane ) : m_value( value ), m_lane( nlane ){}; + + constexpr UTNZSWord( unsigned int value, unsigned int nlane, unsigned int asic_num, unsigned int nzs_header, + unsigned int pos ) + : m_value( value ), m_lane( nlane ), m_asic_num( asic_num ), m_nzs_header( nzs_header ), m_pos( pos ){}; + + /** The actual value + @return valueW + */ + [[nodiscard]] constexpr unsigned int value() const { return m_value; }; + + [[nodiscard]] constexpr unsigned int channelIDL() const { + return maxstrip * m_lane + asic_num() % 4 * 128 + ( 127 - ( 2 * m_pos % 132 - 4 ) ); + } + [[nodiscard]] constexpr unsigned int channelIDR() const { + return maxstrip * m_lane + asic_num() % 4 * 128 + ( 127 - ( 2 * m_pos % 132 - 3 ) ); + } + [[nodiscard]] constexpr unsigned int adcL() const { return extract<mask::adcL>( m_value ); } + + [[nodiscard]] constexpr unsigned int adcR() const { return extract<mask::adcR>( m_value ); } + + [[nodiscard]] constexpr unsigned int pos() const { return m_pos; } + + [[nodiscard]] constexpr bool not_nzs_header() const { return m_pos % 66 > 1 ? 1 : 0; } + + [[nodiscard]] constexpr unsigned int asic_num() const { return extract<mask::adcR>( m_asic_num ); } + + [[nodiscard]] constexpr unsigned int nzs_header() const { + return ( extract<mask::adcL>( m_asic_num ) << 16 ) | m_nzs_header; + } + + [[nodiscard]] constexpr int mcm_val() const { // 2 complement for 6b since mcm_val may be negative + int extracted = extract<mask::mcm_val>( nzs_header() ); + return ( extracted & 0x20 ) ? ( extracted | 0xFFFFFFC0 ) : extracted; + } + [[nodiscard]] constexpr unsigned int mcm_ch() const { return extract<mask::mcm_ch>( nzs_header() ); } + + [[nodiscard]] constexpr unsigned int mem_sep() const { return extract<mask::mem_sep>( nzs_header() ); } + + [[nodiscard]] constexpr unsigned int ev_par() const { return extract<mask::ev_par>( nzs_header() ); } + + [[nodiscard]] constexpr unsigned int lane() const { return m_lane; } + + // Operator overloading for stringoutput + friend std::ostream& operator<<( std::ostream& s, const UTNZSWord& obj ) { return obj.fillStream( s ); } + + // Fill the ASCII output stream + std::ostream& fillStream( std::ostream& s ) const { + return s << "{ " + << " value:\t" << value() << " }\n"; + } + +private: + unsigned int m_value = 0; + unsigned int m_lane = 0; + unsigned int m_asic_num = 0; + unsigned int m_nzs_header = 0; + unsigned int m_pos = 0; +};