From 0497349124c4441e1eace700b09f9ca52d73d1a9 Mon Sep 17 00:00:00 2001 From: Alexandru Bercuci <abercuci@niham.nipne.ro> Date: Wed, 15 Jan 2025 15:30:15 +0200 Subject: [PATCH] 2D online unpacker. Updated versions for the algo and the FASP message. Template implementation --- algo/detectors/trd2d/ReadoutConfig.cxx | 14 +- algo/detectors/trd2d/ReadoutConfig.h | 8 +- algo/detectors/trd2d/Unpack.cxx | 31 ++- algo/detectors/trd2d/UnpackMS.cxx | 283 +++++++++++++++++++++++-- algo/detectors/trd2d/UnpackMS.h | 189 +++++++++++++---- 5 files changed, 452 insertions(+), 73 deletions(-) diff --git a/algo/detectors/trd2d/ReadoutConfig.cxx b/algo/detectors/trd2d/ReadoutConfig.cxx index 3aaa79b1c2..3258171b02 100644 --- a/algo/detectors/trd2d/ReadoutConfig.cxx +++ b/algo/detectors/trd2d/ReadoutConfig.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2023-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Volker Friese, Dominik Smith [committer] */ + Authors: Volker Friese, Dominik Smith [committer], Alex Bercuci */ #include "ReadoutConfig.h" @@ -73,10 +73,10 @@ namespace cbm::algo::trd2d // Invert to obtain component map (equipId) -> (module iq, crob id) for (auto& entry : map) { uint16_t mod_id = entry.first; - for (uint8_t crob_id = 0; crob_id < NCROBMOD; crob_id++) { - uint16_t eq_id = entry.second[crob_id]; + for (uint8_t rob_id = 0; rob_id < NCROBMOD; rob_id++) { + uint16_t eq_id = entry.second[rob_id]; if (!eq_id) continue; - fReadoutMap[eq_id] = {mod_id, crob_id}; + fReadoutMap[eq_id] = {mod_id, rob_id}; } } } @@ -146,8 +146,8 @@ namespace cbm::algo::trd2d uint16_t equipmentId = comp.first; auto value = comp.second; uint16_t moduleId = value.moduleId; - uint16_t crobId = value.crobId; - ss << "Equipment " << equipmentId << " Module " << moduleId << " Crob " << crobId << "\n"; + uint16_t robId = value.robId; + ss << "Equipment " << equipmentId << " Module " << moduleId << " Rob " << robId << "\n"; } ss << "\n"; diff --git a/algo/detectors/trd2d/ReadoutConfig.h b/algo/detectors/trd2d/ReadoutConfig.h index 9d43ea1e5f..4631e1c0a5 100644 --- a/algo/detectors/trd2d/ReadoutConfig.h +++ b/algo/detectors/trd2d/ReadoutConfig.h @@ -1,6 +1,6 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2023-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Volker Friese, Dominik Smith [committer] */ + Authors: Volker Friese, Dominik Smith [committer], Alex Bercuci */ #pragma once @@ -34,11 +34,11 @@ namespace cbm::algo::trd2d public: struct CompMapping { uint16_t moduleId; - uint8_t crobId; + uint8_t robId; CBM_YAML_FORMAT(YAML::Flow); CBM_YAML_PROPERTIES(yaml::Property(&CompMapping::moduleId, "moduleId", "Module ID"), - yaml::Property(&CompMapping::crobId, "crobId", "CROB ID")); + yaml::Property(&CompMapping::robId, "crobId", "CROB ID")); }; struct ChanMapping { diff --git a/algo/detectors/trd2d/Unpack.cxx b/algo/detectors/trd2d/Unpack.cxx index ef628a177d..c8a1ee8034 100644 --- a/algo/detectors/trd2d/Unpack.cxx +++ b/algo/detectors/trd2d/Unpack.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main +/* Copyright (C) 2024-2025 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main SPDX-License-Identifier: GPL-3.0-only - Authors: Felix Weiglhofer [committer], Dominik Smith */ + Authors: Felix Weiglhofer [committer], Dominik Smith, Alex Bercuci */ #include "Unpack.h" @@ -11,7 +11,14 @@ using fles::Subsystem; Unpack::Unpack(const ReadoutConfig& readout) : fReadout(readout) { - constexpr u8 SystemVersion = 0x02; + /** Register the algorithm versions available for TRD2D. For the moment (25.01.14), there is + * no distinction between ALGO and MESSAGE version. The following mapping is + * assumed: + * eMessageVersion::kMessLegacy - refers to the version WITH digi buffering + * eMessageVersion::kMess24 - refers to the version WITHOUT digi buffering + */ + constexpr std::array<u8, int(eMessageVersion::kMessNoDef)> AlgoVersion = {(u8) eMessageVersion::kMessLegacy, + (u8) eMessageVersion::kMess24}; // Create one algorithm per component for TRD and configure it with parameters auto equipIdsTrd2d = fReadout.GetEquipmentIds(); @@ -35,11 +42,23 @@ Unpack::Unpack(const ReadoutConfig& readout) : fReadout(readout) auto comppars = fReadout.CompMap(equip); par.fSystemTimeOffset = fReadout.GetSystemTimeOffset(); par.fModId = comppars.moduleId; - par.fCrobId = comppars.crobId; + par.fRobId = comppars.robId; par.fAsicParams.push_back(asicPar); } - auto algo = std::make_unique<UnpackMS>(par); - fAlgos[{equip, SystemVersion}] = std::move(algo); + // build all algorithms defined for data unpacking ! Why ?? (AB 25.01.15) + std::unique_ptr<UnpackMSBase<CbmTrdDigi, UnpackMonitorData, UnpackAuxData>> algo; + for (auto ver : AlgoVersion) { + switch (ver) { + case (int) eMessageVersion::kMessLegacy: + algo = std::make_unique<UnpackMS<(u8) eMessageVersion::kMessLegacy>>(par); + break; + case (int) eMessageVersion::kMess24: + algo = std::make_unique<UnpackMS<(u8) eMessageVersion::kMess24>>(par); + break; + } + // register algorithm + fAlgos[{equip, ver}] = std::move(algo); + } L_(debug) << "--- Configured equipment " << equip << " with " << numAsics << " asics"; } diff --git a/algo/detectors/trd2d/UnpackMS.cxx b/algo/detectors/trd2d/UnpackMS.cxx index 714f4c2e81..9bdf5b40d3 100644 --- a/algo/detectors/trd2d/UnpackMS.cxx +++ b/algo/detectors/trd2d/UnpackMS.cxx @@ -8,6 +8,7 @@ #include <algorithm> #include <cassert> +#include <sstream> #include <vector> using std::unique_ptr; @@ -15,23 +16,135 @@ using std::unique_ptr; namespace cbm::algo::trd2d { // ---- Fasp message constructor ---------------------------------------- - FaspMessage::FaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t rob, uint8_t asic) + FaspMessage::FaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t r, uint8_t asic, uint8_t e) : ch(c) - , type(typ) + , type(eMessageType::kNone) , tlab(t) , data(d) - , crob(rob) + , rob(r) + , elink(e) , fasp(asic) { + if (typ == uint8_t(eMessageType::kData)) + type = eMessageType::kData; + else if (typ == uint8_t(eMessageType::kEpoch)) + type = eMessageType::kEpoch; } - UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {} - UnpackMS::~UnpackMS() = default; + std::string FaspMessage::print() const + { + std::stringstream ss; + switch (type) { + case eMessageType::kData: + ss << " DATA : rob=" << rob << (elink ? 'u' : 'd') << " fasp_id=" << fasp << " [" << getFaspIdMod() + << "] ch_id=" << ch << " tclk=" << tlab << " data=" << data << std::endl; + break; + case eMessageType::kEpoch: + ss << " EPOCH: eq_id=" << rob << (elink ? 'u' : 'd') << " ch_id=" << ch << " epoch=" << epoch << std::endl; + break; + default: ss << " MTYPE: unknown" << std::endl; break; + } + + return ss.str(); + } + + // ---- Data type descriptor --------------------------------------------- + template<std::uint8_t mess_ver> + eMessageType FaspMessage::getType(uint32_t) + { + return eMessageType::kNone; + } + + template<> + eMessageType FaspMessage::getType<uint8_t(eMessageVersion::kMess24)>(uint32_t w) + { + /** Search the data type descriptor in a FASP word. Starting with message version kMess24*/ + + if ((w >> 31) & 0x1) return eMessageType::kEpoch; + return eMessageType::kData; + } + + // ---- Unpacking a DATA WORD --------------------------------------------- + template<std::uint8_t mess_ver> + void FaspMessage::readDW(uint32_t) + { + return; + } + + template<> + void FaspMessage::readDW<uint8_t(eMessageVersion::kMess24)>(uint32_t w) + { + /** Data Word unpacking starting with message version kMess24*/ + + uint8_t shift(0); + uint16_t adc_data = (w >> shift) & 0x3fff; + // TODO This data format version delivers the ADC value as bit_sgn + 13 significant bits + // TODO The CbmTrdDigi supports digi data with only 12bits unsigned. The first tests will + // TODO convert the measurement to the old format leaving the implementation of the new storage to // TODO later time. (AB 14.06.2024) + uint16_t sign = adc_data >> 13; // sign + int value_i; + if (!sign) + value_i = adc_data; + else + value_i = (-1) * ((adc_data ^ 0xffff) & 0x1fff); + // convert to 12bit unsigned + data = (value_i + 0x1fff) >> 2; + shift += uint8_t(eMessageLength::kMessData); + tlab = (w >> shift) & 0x7f; + shift += uint8_t(eMessageLength::kMessTlab); + ch = (w >> shift) & 0xf; + shift += uint8_t(eMessageLength::kMessCh); + fasp = ((w >> shift) & 0x3f); + shift += uint8_t(eMessageLength::kMessFasp); + type = eMessageType((w >> shift) & 0x1); + shift += uint8_t(eMessageLength::kMessType); + + // if (VERBOSE >= 2) { + // printf("v06.24Mess_readDW[%x] signed charge = %+d\n", w, value_i); + // print(); + // } + return; + } + + // ---- Unpacking an EPOCH WORD --------------------------------------------- + template<std::uint8_t mess_ver> + void FaspMessage::readEW(uint32_t) + { + return; + } + + template<> + void FaspMessage::readEW<uint8_t(eMessageVersion::kMess24)>(uint32_t w) + { + /** Epoch Word unpacking starting with message version kMess24*/ + + uint8_t shift(0); + epoch = (w >> shift) & 0x1fffff; + shift += uint8_t(eMessageLength::kMessEpoch); + ch = (w >> shift) & 0xf; + shift += uint8_t(eMessageLength::kMessCh); + fasp = (w >> shift) & 0x3f; + shift += uint8_t(eMessageLength::kMessFasp); + type = eMessageType((w >> shift) & 0x1); + shift += uint8_t(eMessageLength::kMessType); + + // if (VERBOSE >= 2) { + // printf("v06.24Mess_readEW[%x]\n", w); + // print(); + // } + return; + } // ---- Algorithm execution --------------------------------------------- - UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, - const uint64_t tTimeslice) const + template<> + typename UnpackMS<uint8_t(eMessageVersion::kMessLegacy)>::Result_t + UnpackMS<uint8_t(eMessageVersion::kMessLegacy)>::operator()(const uint8_t* msContent, + const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const { + /** Implementation of TRD2D unpacking for the 2021 - 2022 mCBM data taking + * The algorithm implements digi buffering due to a "feature" on the ADC read-out + */ // --- Output data Result_t result = {}; @@ -44,12 +157,12 @@ namespace cbm::algo::trd2d uint64_t time = uint64_t((msDescr.idx - tTimeslice - fParams.fSystemTimeOffset) / 12.5); // Get parameters for current eq id. - const uint8_t crob_id = fParams.fCrobId; + const uint8_t crob_id = fParams.fRobId; // Get the number of complete words in the input MS buffer. const uint32_t nwords = msDescr.size / 4; - // We have 32 bit spadic frames in this readout version + // We have 32 bit FASP frames in this readout version const uint32_t* wd = reinterpret_cast<const uint32_t*>(msContent); unsigned char lFaspOld(0xff); @@ -90,16 +203,16 @@ namespace cbm::algo::trd2d ctx.fMonitor.fNumSelfTriggeredData++; data &= 0x1fff; } - vMess.emplace_back(ch_id, kData, slice, data >> 1, crob_id, lFaspOld); + vMess.emplace_back(ch_id, (uint8_t) eMessageType::kData, slice, data >> 1, crob_id, lFaspOld); } std::get<0>(result) = FinalizeComponent(ctx); //TO DO: Original (non-algo) version calls this after MS loop!! std::get<1>(result) = ctx.fMonitor; return result; } - //_________________________________________________________________________________ - bool UnpackMS::pushDigis(std::vector<FaspMessage> messes, const uint64_t time, MsContext& ctx) const + template<uint8_t sys_ver> + bool UnpackMS<sys_ver>::pushDigis(std::vector<FaspMessage> messes, const uint64_t time, MsContext& ctx) const { const uint16_t mod_id = fParams.fModId; const UnpackAsicPar& asicPar = fParams.fAsicParams[messes[0].fasp]; @@ -167,7 +280,8 @@ namespace cbm::algo::trd2d return true; } - std::vector<CbmTrdDigi> UnpackMS::FinalizeComponent(MsContext& ctx) const + template<uint8_t sys_ver> + std::vector<CbmTrdDigi> UnpackMS<sys_ver>::FinalizeComponent(MsContext& ctx) const { std::vector<CbmTrdDigi> outputDigis; @@ -196,4 +310,147 @@ namespace cbm::algo::trd2d } return outputDigis; } + + // ------------- Specialization kMess24 -------------------------------- + typename UnpackMS<uint8_t(eMessageVersion::kMess24)>::Result_t + UnpackMS<uint8_t(eMessageVersion::kMess24)>::operator()(const uint8_t* msContent, + const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const + { + /** Implementation of TRD2D unpacking for the 2024 - PRESENT mCBM data taking + * The algorithm implements the new message format starting with version kMess24 + */ + + constexpr uint8_t m24 = + uint8_t(eMessageVersion::kMess24); // message versions compatible with the current algo specialization + Result_t result = {}; + MsContext ctx = {}; + + // define time wrt start of time slice in TRD/FASP clks [80 MHz]. Contains: + // - relative offset of the MS wrt the TS + // - FASP epoch offset for current CROB + // - TRD2D system offset wrt to experiment time + uint64_t time = uint64_t((msDescr.idx - tTimeslice - fParams.fSystemTimeOffset) * fAsicClockFreq); + + // Get the number of complete words in the input MS buffer. + const uint32_t nwords = msDescr.size / 4; + + // We have 32 bit FASP frames in this readout version + const uint32_t* wd = reinterpret_cast<const uint32_t*>(msContent); + + unsigned char lFaspOld(0xff); + std::vector<FaspMessage> vMess; + for (uint32_t j = 0; j < nwords; j++, wd++) { + uint32_t w = *wd; + // Select the appropriate conversion type of the word according to + // the current message version and type + switch (FaspMessage::getType<m24>(w)) { + case eMessageType::kData: ctx.fMess.readDW<m24>(w); break; + case eMessageType::kEpoch: ctx.fMess.readEW<m24>(w); break; + default: break; // no way to reach this line + } + ctx.fMess.rob = fParams.fRobId; + + // PROCESS EPOCH MESSAGES + if (ctx.fMess.type == eMessageType::kEpoch) { + if (ctx.fMess.ch == 0) { // check word integrity + // clear buffer + if (vMess.size()) { + pushDigis(vMess, time, ctx); + } + vMess.clear(); + + lFaspOld = 0xff; + time += FASP_EPOCH_LENGTH; + } + else { + L_(error) << "FASP message[Epoch] with wrong signature."; + ctx.fMonitor.fNumErrEndBitSet++; + } + continue; + } + + // PROCESS DATA MESSAGES + // clear buffer when switching to other FASP + if (ctx.fMess.fasp != lFaspOld) { + if (vMess.size()) pushDigis(vMess, time, ctx); + vMess.clear(); + lFaspOld = ctx.fMess.fasp; + } + if (ctx.fMess.data & 0x1) { // kept for backward compatibility TODO + ctx.fMonitor.fNumErrEndBitSet++; + continue; + } + if (ctx.fMess.data & 0x2000) { // kept for backward compatibility TODO + ctx.fMonitor.fNumSelfTriggeredData++; + ctx.fMess.data &= 0x1fff; + } + vMess.emplace_back(ctx.fMess); + } + + // combine all digis from this ROB + std::vector<CbmTrdDigi> outputDigis; + for (uint16_t ipad(0); ipad < NFASPMOD * NFASPCH; ipad++) { + if (!ctx.fRobDigi[ipad].size()) continue; + for (auto id : ctx.fRobDigi[ipad]) + outputDigis.emplace_back(std::move(id)); + } + std::get<0>(result) = outputDigis; + std::get<1>(result) = ctx.fMonitor; + + return result; + } + bool UnpackMS<uint8_t(eMessageVersion::kMess24)>::pushDigis(std::vector<FaspMessage> messes, const uint64_t time, + MsContext& ctx) const + { + const uint16_t mod_id = fParams.fModId; + const UnpackAsicPar& asicPar = fParams.fAsicParams[messes[0].fasp]; + const uint64_t tdaqOffset = asicPar.fChanParams[messes[0].ch].fDaqOffset; + + for (auto imess : messes) { + const int32_t pad = std::abs(asicPar.fChanParams[imess.ch].fPadAddress); + const bool hasPairingR = bool(asicPar.fChanParams[imess.ch].fPadAddress > 0); + const uint64_t lTime = time + tdaqOffset + imess.tlab; + const uint16_t lchR = hasPairingR ? imess.data : 0; + const uint16_t lchT = hasPairingR ? 0 : imess.data; + std::vector<CbmTrdDigi>& digiBuffer = ctx.fRobDigi[pad]; + + // init pad position in array and build digi for message + if (digiBuffer.size() == 0) { + digiBuffer.emplace_back(pad, lchT, lchR, lTime); + digiBuffer.back().SetAddressModule(mod_id); + continue; + } + + // check if last digi has both R/T message components. + // Update if not and is within time window + auto id = digiBuffer.back(); // Should always be valid here. + // No need to extra check + double r, t; + int32_t dt; + const int32_t dtime = id.GetTimeDAQ() - lTime; + bool use(false); + if (abs(dtime) < 5) { // test message part of (last) digi + r = id.GetCharge(t, dt); + if (lchR && r < 0.1) { // set R charge on an empty slot + id.SetCharge(t, lchR, -dtime); + use = true; + } + else if (lchT && t < 0.1) { // set T charge on an empty slot + id.SetCharge(lchT, r, +dtime); + id.SetTimeDAQ(uint64_t(id.GetTimeDAQ() - dtime)); + use = true; + } + } + + // build digi for message when update failed + if (!use) { + digiBuffer.emplace_back(pad, lchT, lchR, lTime); + digiBuffer.back().SetAddressModule(mod_id); + } + } + messes.clear(); + + return true; + } } // namespace cbm::algo::trd2d diff --git a/algo/detectors/trd2d/UnpackMS.h b/algo/detectors/trd2d/UnpackMS.h index 18a498eecc..6491945e0e 100644 --- a/algo/detectors/trd2d/UnpackMS.h +++ b/algo/detectors/trd2d/UnpackMS.h @@ -4,7 +4,6 @@ #pragma once #include "CbmTrdDigi.h" -#include "CbmTrdRawMessageSpadic.h" #include "MicrosliceDescriptor.hpp" #include "UnpackMSBase.h" @@ -21,30 +20,102 @@ namespace cbm::algo::trd2d { + enum class eMessageLength : int + { + kMessCh = 4, + kMessType = 1, + kMessTlab = 7, + kMessData = 14, + kMessFasp = 6, + kMessEpoch = 21 + }; + + enum class eMessageVersion : uint8_t + { + kMessLegacy = 2, /// unpacker version for 2-board FASPRO+GETS HW + kMess24 = 3, /// unpacker version for 1-board FASPRO HW first used 18.06.24 (mCBM) + kMessNoDef /// default unpacker version + }; - enum FaspMessageType + enum class eMessageType : int { - kEpoch = 0, - kData + kData = 0, + kEpoch = 1, + kNone }; /** @brief Data structure for unpacking the FASP word */ + // Constants + /** @brief Bytes per FASP frame stored in the microslices (32 bits words) + * - DATA WORD - for legacy version + * ffff.ffdd dddd.dddd dddd.tttt ttta.cccc + * f - FASP id + * d - ADC signal + * t - time label inside epoch + * a - word type (1) + * c - channel id + * - EPOCH WORD - + * ffff.fftt tttt.tttt tttt.tttt ttta.cccc + * f - FASP id + * t - epoch index + * a - word type (0) + * c - channel id + * ===================================================== + * - DATA WORD - for 06.2024 version + * afff.fffc ccct.tttt ttdd.dddd dddd.dddd + * f - FASP id + * d - ADC signal + * t - time label inside epoch + * a - word type (1) + * c - channel id + * - EPOCH WORD - + * afff.fffc ccct.tttt tttt tttt tttt.tttt + * a - word type (0) + * f - FASP id + * t - epoch index + * c - channel id + */ struct FaspMessage { - FaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t rob, uint8_t asic); - uint8_t ch = 0; ///< ch id in the FASP - uint8_t type = 0; ///< message type 0 = epoch, 1 = data (not used for the moment) - uint8_t tlab = 0; ///< time of the digi inside the epoch - uint16_t data = 0; ///< ADC value - uint32_t epoch = 0; ///< epoch id (not used for the moment) - uint32_t mod = 0; ///< full module address according to CbmTrdAddress - uint8_t crob = 0; ///< CROB id in the module - uint8_t fasp = 0; ///< FASP id in the module + FaspMessage() = default; + FaspMessage(const FaspMessage&) = default; + FaspMessage(uint8_t c, uint8_t typ, uint8_t t, uint16_t d, uint8_t rob, uint8_t asic, uint8_t e = 0); + int getFaspIdMod() const { return fasp + rob * NFASPCROB; } + + /** \brief Implementation of message type descriptor according to message version + * \param w the message word + */ + template<std::uint8_t mess_ver> + static eMessageType getType(uint32_t w); + + std::string print() const; + + /** \brief Read DATA WORD and store the content locally + * \param w the message word + */ + template<std::uint8_t mess_ver> + void readDW(uint32_t w); + + /** \brief Read EPOCH WORD and store the content locally + * \param w the message word + */ + template<std::uint8_t mess_ver> + void readEW(uint32_t w); + + uint8_t ch = 0; ///< ch id in the FASP + eMessageType type = eMessageType::kNone; ///< message type 0 = data, 1 = epoch (not used for the moment) + uint8_t tlab = 0; ///< time of the digi inside the epoch + uint16_t data = 0; ///< ADC value + uint32_t epoch = 0; ///< epoch id (not used for the moment) + uint32_t mod = 0; ///< full module address according to CbmTrdAddress + uint8_t rob = 0; ///< ROB id in the module + uint8_t elink = 0; ///< optical link for read-out unit (up or down, starting with kMess24) + uint8_t fasp = 0; ///< FASP id in the module }; /** @struct UnpackChannelPar ** @author Dominik Smith <d.smith@gsi.de> ** @since 31 January 2023 - ** @brief TRD Unpacking parameters for one Asic channel + ** @brief TRD2D Unpacking parameters for one Asic channel **/ struct UnpackChannelPar { int32_t fPadAddress; ///< Pad address for channel @@ -55,7 +126,7 @@ namespace cbm::algo::trd2d /** @struct UnpackAsicPar ** @author Dominik Smith <d.smith@gsi.de> ** @since 31 January 2023 - ** @brief TRD Unpacking parameters for one Asic + ** @brief TRD2D Unpacking parameters for one Asic **/ struct UnpackAsicPar { std::vector<UnpackChannelPar> fChanParams; ///< Parameters for different channels @@ -64,12 +135,12 @@ namespace cbm::algo::trd2d /** @struct UnpackPar ** @author Dominik Smith <d.smith@gsi.de> ** @since 31 January 2023 - ** @brief Parameters required for the TRD unpacking (specific to one component) + ** @brief Parameters required for the TRD2D unpacking (specific to one component) **/ struct UnpackPar { int32_t fSystemTimeOffset = 0; ///< Time calibration parameter uint16_t fModId = 0; ///< Module ID of component - uint8_t fCrobId = 0; ///< CROB ID of component + uint8_t fRobId = 0; ///< ROB ID of component std::vector<UnpackAsicPar> fAsicParams = {}; ///< Parameters for each ASIC }; @@ -77,7 +148,7 @@ namespace cbm::algo::trd2d /** @struct UnpackMoni ** @author Dominik Smith <d.smith@gsi.de> ** @since 31 January 2023 - ** @brief Monitoring data for TRD unpacking + ** @brief Monitoring data for TRD2D unpacking **/ struct UnpackMonitorData { uint32_t fNumSelfTriggeredData = 0; ///< word fulfills data & 0x2000 @@ -101,27 +172,26 @@ namespace cbm::algo::trd2d /** @struct UnpackAux ** @author Dominik Smith <d.smith@gsi.de> ** @since 24 May 2024 - ** @brief Auxiliary data for BMON unpacking + ** @brief Auxiliary data for unpacking **/ struct UnpackAuxData { ///// TO BE FILLED }; /** @class UnpackMS - ** @author Dominik Smith <d.smith@gsi.de> + ** @author Dominik Smith <d.smith@gsi.de>, Alex Bercuci <abercuci@niham.nipne.ro> ** @since 31 January 2023 - ** @brief Unpack algorithm for TRD + ** @brief Unpack algorithm for TRD2D **/ + template<std::uint8_t sys_ve> class UnpackMS : public UnpackMSBase<CbmTrdDigi, UnpackMonitorData, UnpackAuxData> { public: /** @brief Construct from parameters **/ - UnpackMS(const UnpackPar& pars); - + UnpackMS(const UnpackPar& /*pars*/) {} /** @brief Destructor **/ - ~UnpackMS() override; - + ~UnpackMS() override = default; /** @brief Algorithm execution ** @param msContent Microslice payload @@ -152,25 +222,58 @@ namespace cbm::algo::trd2d /** @brief Finalize component (e.g. copy from temp buffers) */ std::vector<CbmTrdDigi> FinalizeComponent(MsContext& ctx) const; - - // Constants - /** @brief Bytes per FASP frame stored in the microslices (32 bits words) - * - DATA WORD - - * ffff.ffdd dddd.dddd dddd.tttt ttta.cccc - * f - FASP id - * d - ADC signal - * t - time label inside epoch - * a - word type (1) - * c - channel id - * - EPOCH WORD - - * ffff.fftt tttt.tttt tttt.tttt ttta.cccc - * f - FASP id - * t - epoch index - * a - word type (0) - * c - channel id - */ - static const std::uint8_t fBytesPerWord = 4; }; + /** @class UnpackMS<kMess24> + ** @author Alex Bercuci <abercuci@niham.nipne.ro> + ** @since 15 January 2025 + ** @brief Unpack algorithm specialization for TRD2D based on one-layered FEB design introduced in + * 06.2024 (https://indico.gsi.de/event/20885/attachments/49263/72236/TRD2D-CDR-FEB.pdf) + * - using different message format (\see \enum eMessageVersion) starting with kMess24 + * - remove time buffering of digi per channel as a new 32 channels ADC is used in the FEE + **/ + template<> + class UnpackMS<uint8_t(eMessageVersion::kMess24)> : + public UnpackMSBase<CbmTrdDigi, UnpackMonitorData, UnpackAuxData> { + + public: + /** @brief Construct from parameters **/ + UnpackMS(const UnpackPar& /*pars*/) {} + + /** @brief Destructor **/ + ~UnpackMS() override = default; + + + /** @brief Algorithm execution + ** @param msContent Microslice payload + ** @param msDescr Microslice descriptor + ** @param tTimeslice Unix start time of timeslice [ns] + ** @return TRD digi data + **/ + Result_t operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const override; + + /** @brief Set the parameter container + ** @param params Pointer to parameter container + **/ + void SetParams(std::unique_ptr<UnpackPar> params) { fParams = *(std::move(params)); } + + private: // Types + struct MsContext { + UnpackMonitorData fMonitor; ///< Container for monitoring data + std::array<std::vector<CbmTrdDigi>, NFASPMOD* NFASPCH> fRobDigi = { + {}}; ///> Buffered digi for each pad in one Epoch-ROB component + FaspMessage fMess; ///< encapsulation of the FASP message. + }; + + private: // members + bool pushDigis(std::vector<FaspMessage> messages, const uint64_t time, MsContext& ctx) const; + + UnpackPar fParams = {}; ///< Parameter container + + /** @brief Clock frequency of FASP in 0.08 GHz. HW also supports 0.04 GHz*/ + static constexpr float fAsicClockFreq = 0.08; + }; + } // namespace cbm::algo::trd2d -- GitLab