Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-05-26 01:15:39

0001 /****************************************************************************
0002  *
0003  * This is a part of HGCAL offline software.
0004  * Authors:
0005  *   Laurent Forthomme, CERN
0006  *
0007  ****************************************************************************/
0008 
0009 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0010 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0011 #include "FWCore/Utilities/interface/CRC16.h"
0012 
0013 #include "EventFilter/HGCalRawToDigi/interface/HGCalECONDEmulator.h"
0014 #include "EventFilter/HGCalRawToDigi/interface/HGCalFrameGenerator.h"
0015 #include "EventFilter/HGCalRawToDigi/interface/HGCalRawDataPackingTools.h"
0016 
0017 #include "CLHEP/Random/RandFlat.h"
0018 #include <iomanip>
0019 
0020 namespace hgcal {
0021   //--------------------------------------------------
0022   // bit-casting utilities
0023   //--------------------------------------------------
0024 
0025   template <typename T>
0026   void printWords(edm::MessageSender& os, const std::string& name, const std::vector<T> vec) {
0027     os << "Dump of the '" << name << "' words:\n";
0028     for (size_t i = 0; i < vec.size(); ++i)
0029       os << std::dec << std::setfill(' ') << std::setw(4) << i << ": 0x" << std::hex << std::setfill('0')
0030          << std::setw(sizeof(T) * 2) << vec.at(i) << "\n";
0031   }
0032 
0033   static std::vector<uint64_t> to64bit(const std::vector<uint32_t>& in) {
0034     std::vector<uint64_t> out;
0035     for (size_t i = 0; i < in.size(); i += 2) {
0036       uint64_t word1 = (i < in.size()) ? in.at(i) : 0ul, word2 = (i + 1 < in.size()) ? in.at(i + 1) : 0ul;
0037       out.emplace_back(((word2 & 0xffffffff) << 32) | (word1 & 0xffffffff));
0038     }
0039     return out;
0040   }
0041 
0042   static std::vector<uint64_t> to128bit(const std::vector<uint64_t>& in) {
0043     std::vector<uint64_t> out;
0044     for (size_t i = 0; i < in.size(); i += 2) {
0045       out.emplace_back(in.at(i));
0046       out.emplace_back((i + 1 < in.size()) ? in.at(i + 1) : 0u);
0047     }
0048     return out;
0049   }
0050 
0051   HGCalFrameGenerator::HGCalFrameGenerator(const edm::ParameterSet& iConfig) {
0052     const auto slink_config = iConfig.getParameter<edm::ParameterSet>("slinkParams");
0053 
0054     size_t econd_id = 0;
0055     std::vector<unsigned int> active_econds;
0056     for (const auto& econd : slink_config.getParameter<std::vector<edm::ParameterSet> >("ECONDs")) {
0057       // a bit of user input validation
0058       if (slink_config.getParameter<bool>("checkECONDsLimits")) {
0059         if (active_econds.size() > kMaxNumECONDs)
0060           throw cms::Exception("HGCalFrameGenerator")
0061               << "Too many active ECON-D set: " << active_econds.size() << " > " << kMaxNumECONDs << ".";
0062         if (econd_id >= kMaxNumECONDs)
0063           throw cms::Exception("HGCalFrameGenerator")
0064               << "Invalid ECON-D identifier: " << econd_id << " >= " << kMaxNumECONDs << ".";
0065       }
0066       if (econd.getParameter<bool>("active"))
0067         active_econds.emplace_back(econd_id);
0068 
0069       econd_params_.insert(std::make_pair(econd_id, econd::EmulatorParameters(econd)));
0070       ++econd_id;
0071     }
0072 
0073     slink_params_ = SlinkParameters{.active_econds = active_econds,
0074                                     .boe_marker = slink_config.getParameter<unsigned int>("boeMarker"),
0075                                     .eoe_marker = slink_config.getParameter<unsigned int>("eoeMarker"),
0076                                     .format_version = slink_config.getParameter<unsigned int>("formatVersion"),
0077                                     .num_capture_blocks = slink_config.getParameter<unsigned int>("numCaptureBlocks"),
0078                                     .store_header_trailer = slink_config.getParameter<bool>("storeHeaderTrailer")};
0079   }
0080 
0081   edm::ParameterSetDescription HGCalFrameGenerator::description() {
0082     edm::ParameterSetDescription desc;
0083 
0084     std::vector<edm::ParameterSet> econds_psets;
0085     for (size_t i = 0; i < 7; ++i)
0086       econds_psets.emplace_back();
0087 
0088     edm::ParameterSetDescription slink_desc;
0089     slink_desc.addVPSet("ECONDs", econd::EmulatorParameters::description(), econds_psets)
0090         ->setComment("list of active ECON-Ds in S-link");
0091     slink_desc.add<unsigned int>("boeMarker", 0x55);
0092     slink_desc.add<unsigned int>("eoeMarker", 0xaa);
0093     slink_desc.add<unsigned int>("formatVersion", 3);
0094     slink_desc.add<unsigned int>("numCaptureBlocks", 1)
0095         ->setComment("number of capture blocks to emulate per S-link payload");
0096     slink_desc.add<bool>("checkECONDsLimits", true)->setComment("check the maximal number of ECON-Ds per S-link");
0097     slink_desc.add<bool>("storeHeaderTrailer", true)->setComment("also store the S-link header and trailer words");
0098     desc.add<edm::ParameterSetDescription>("slinkParams", slink_desc);
0099 
0100     return desc;
0101   }
0102 
0103   void HGCalFrameGenerator::setRandomEngine(CLHEP::HepRandomEngine& rng) { rng_ = &rng; }
0104 
0105   void HGCalFrameGenerator::setEmulator(econd::Emulator& emul) { emul_ = &emul; }
0106 
0107   //--------------------------------------------------
0108   // emulation part
0109   //--------------------------------------------------
0110 
0111   HGCalFrameGenerator::HeaderBits HGCalFrameGenerator::generateStatusBits(unsigned int econd_id) const {
0112     if (!rng_)
0113       throw cms::Exception("HGCalFrameGenerator") << "Random number generator not initialised.";
0114     const auto& econd_params = econd_params_.at(econd_id);
0115     // first sample on header status bits
0116     return HeaderBits{
0117         .bitO = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitO,
0118         .bitB = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitB,
0119         .bitE = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitE,
0120         .bitT = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitT,
0121         .bitH = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitH,
0122         .bitS = CLHEP::RandFlat::shoot(rng_) < econd_params.error_prob.bitS,
0123     };
0124   }
0125 
0126   econd::ERxChannelEnable HGCalFrameGenerator::generateEnabledChannels(unsigned int econd_id) const {
0127     const auto& econd_params = econd_params_.at(econd_id);
0128     econd::ERxChannelEnable chmap(econd_params.num_channels_per_erx, false);
0129     for (size_t i = 0; i < chmap.size(); i++)
0130       // randomly choosing the channels to be shot at
0131       chmap[i] = CLHEP::RandFlat::shoot(rng_) <= econd_params.chan_surv_prob;
0132     return chmap;
0133   }
0134 
0135   std::vector<uint32_t> HGCalFrameGenerator::generateERxData(
0136       unsigned int econd_id,
0137       const econd::ERxInput& input_event,
0138       std::vector<econd::ERxChannelEnable>& enabled_channels) const {
0139     const auto& econd_params = econd_params_.at(econd_id);
0140     std::vector<uint32_t> erx_data;
0141     enabled_channels.clear();
0142     for (const auto& jt : input_event) {  // one per eRx
0143       const auto chmap =
0144           generateEnabledChannels(econd_id);  // generate a list of probable channels to be filled with emulated content
0145 
0146       // insert eRx header (common mode, channels map, ...)
0147       uint8_t stat = 0b111 /*possibly emulate*/, hamming_check = 0;
0148       bool bit_e = false;  // did unmasked stat error bits associated with the eRx cause the sub-packet to be supressed?
0149       auto erx_header = econd::eRxSubPacketHeader(stat, hamming_check, bit_e, jt.second.cm0, jt.second.cm1, chmap);
0150       erx_data.insert(erx_data.end(), erx_header.begin(), erx_header.end());
0151       if (jt.second.adc.size() < econd_params.num_channels_per_erx) {
0152         edm::LogError("HGCalFrameGenerator:generateERxData")
0153             << "Data multiplicity too low (" << jt.second.adc.size() << ") to emulate "
0154             << econd_params.num_channels_per_erx << " ECON-D channel(s).";
0155         continue;
0156       }
0157       // insert eRx payloads (integrating all readout channels)
0158       const auto erx_chan_data = econd::produceERxData(chmap,
0159                                                        jt.second,
0160                                                        true,  // passZS
0161                                                        true,  // passZSm1
0162                                                        true,  // hasToA
0163                                                        econd_params.characterisation_mode);
0164       erx_data.insert(erx_data.end(), erx_chan_data.begin(), erx_chan_data.end());
0165       enabled_channels.emplace_back(chmap);
0166     }
0167     LogDebug("HGCalFrameGenerator").log([&erx_data](auto& log) { printWords(log, "erx", erx_data); });
0168     return erx_data;
0169   }
0170 
0171   uint32_t HGCalFrameGenerator::computeCRC(const std::vector<uint32_t>& event_header) const {
0172     uint32_t crc = 0x12345678;  //TODO: implement 32-bit Bluetooth CRC in the future
0173     return crc;
0174   }
0175 
0176   //--------------------------------------------------
0177   // payload creation utilities
0178   //--------------------------------------------------
0179 
0180   std::vector<uint64_t> HGCalFrameGenerator::produceECONEvent(unsigned int econd_id, unsigned int cb_id) const {
0181     if (!emul_)
0182       throw cms::Exception("HGCalFrameGenerator")
0183           << "ECON-D emulator was not properly set to the frame generator. Please ensure you are calling the "
0184              "HGCalFrameGenerator::setEmulator method.";
0185 
0186     std::vector<uint64_t> econd_event;
0187 
0188     const auto event = emul_->next();
0189     const auto& econd_params = econd_params_.at(econd_id);
0190     auto header_bits = generateStatusBits(econd_id);
0191     std::vector<econd::ERxChannelEnable> enabled_ch_per_erx;
0192     auto erx_payload = generateERxData(econd_id, event.second, enabled_ch_per_erx);
0193 
0194     // ECON-D event content was just created, now prepend packet header
0195     const uint8_t hamming = 0, rr = 0;
0196     auto econd_header =
0197         econd::eventPacketHeader(econd_params.header_marker,
0198                                  erx_payload.size() + 1 /*CRC*/,
0199                                  econd_params.passthrough_mode,
0200                                  econd_params.expected_mode,
0201                                  // HGCROC Event reco status across all active eRx E-B-O:
0202                                  (header_bits.bitH & 0x1) << 1 | (header_bits.bitT & 0x1),  // HDR/TRL numbers
0203                                  (header_bits.bitE & 0x1) << 2 | (header_bits.bitB & 0x1) << 1 |
0204                                      (header_bits.bitO & 0x1),  // Event/BX/Orbit numbers
0205                                  econd_params.matching_ebo_numbers,
0206                                  econd_params.bo_truncated,
0207                                  hamming,                   // Hamming for event header
0208                                  std::get<1>(event.first),  // BX
0209                                  std::get<0>(event.first),  // event id (L1A)
0210                                  std::get<2>(event.first),  // orbit
0211                                  header_bits.bitS,          // OR of "Stat" bits for all active eRx
0212                                  rr);
0213     LogDebug("HGCalFrameGenerator").log([&econd_header](auto& log) { printWords(log, "econ-d header", econd_header); });
0214     auto econd_header_64bit = to64bit(econd_header);
0215     econd_event.insert(econd_event.end(), econd_header_64bit.begin(), econd_header_64bit.end());
0216     LogDebug("HGCalFrameGenerator") << econd_header.size()
0217                                     << " word(s) of event packet header prepend. New size of ECON frame: "
0218                                     << econd_event.size();
0219     const auto erx_payload_64bit = to64bit(erx_payload);
0220     econd_event.insert(econd_event.end(), erx_payload_64bit.begin(), erx_payload_64bit.end());
0221     LogDebug("HGCalFrameGenerator") << erx_payload.size() << " word(s) of eRx payloads inserted.";
0222 
0223     std::vector<uint64_t> econd_footer;
0224     if (econd_params.add_econd_crc)
0225       econd_footer.emplace_back(computeCRC(econd_header));
0226     if (econd_params.add_idle_word) {
0227       const uint8_t buffer_status = 0, error_status = 0, reset_request = 0;
0228       econd_footer.emplace_back(
0229           econd::buildIdleWord(buffer_status, error_status, reset_request, econd_params.programmable_pattern));
0230     }
0231     econd_event.insert(econd_event.end(), econd_footer.begin(), econd_footer.end());
0232     // bookkeeping of last event + metadata
0233     last_slink_emul_info_.captureBlockEmulatedInfo(cb_id).addECONDEmulatedInfo(
0234         econd_id,
0235         HGCalECONDEmulatorInfo(header_bits.bitO,
0236                                header_bits.bitT,
0237                                header_bits.bitE,
0238                                header_bits.bitT,
0239                                header_bits.bitH,
0240                                header_bits.bitS,
0241                                enabled_ch_per_erx));
0242     last_emul_event_ = event;
0243     return econd_event;
0244   }
0245 
0246   std::vector<uint64_t> HGCalFrameGenerator::produceCaptureBlockEvent(unsigned int cb_id) const {
0247     std::vector<uint64_t> cb_event;
0248     // build all ECON-Ds payloads and add them to the capture block payload
0249     std::vector<backend::ECONDPacketStatus> econd_statuses(kMaxNumECONDs, backend::ECONDPacketStatus::InactiveECOND);
0250     for (const auto& econd : econd_params_) {  // for each ECON-D payload to be emulated
0251       const auto& econd_id = econd.first;
0252       if (!econd.second.active)
0253         continue;  // status is already inactive
0254       econd_statuses[econd_id] =
0255           backend::ECONDPacketStatus::Normal;  //TODO: also implement/emulate other ECON-D packet issues
0256       const auto econd_payload = produceECONEvent(econd_id, cb_id);
0257       cb_event.insert(cb_event.end(), econd_payload.begin(), econd_payload.end());
0258     }
0259     const auto& eid = last_emul_event_.first;
0260     const uint64_t event_id = std::get<0>(eid), bx_id = std::get<1>(eid), orbit_id = std::get<2>(eid);
0261     // prepend the header to the capture block payload
0262     const auto l1a_header = to64bit(backend::buildCaptureBlockHeader(bx_id, event_id, orbit_id, econd_statuses));
0263     LogDebug("HGCalFrameGenerator").log([&l1a_header](auto& log) { printWords(log, "l1a", l1a_header); });
0264     cb_event.insert(cb_event.begin(), l1a_header.begin(), l1a_header.end());
0265     return to128bit(cb_event);
0266   }
0267 
0268   std::vector<uint64_t> HGCalFrameGenerator::produceSlinkEvent(unsigned int fed_id) const {
0269     last_slink_emul_info_.clear();  // clear the metadata of the latest emulated S-link payload
0270 
0271     std::vector<uint64_t> slink_event;  // prepare the output S-link payload (will be "converted" to 128-bit)
0272     for (unsigned int cb_id = 0; cb_id < slink_params_.num_capture_blocks; ++cb_id) {
0273       const auto cb_payload = produceCaptureBlockEvent(cb_id);
0274       slink_event.insert(slink_event.end(),
0275                          cb_payload.begin(),
0276                          cb_payload.end());  // concatenate the capture block to the full S-link payload
0277     }
0278 
0279     if (slink_params_.store_header_trailer) {
0280       // build the S-link header words
0281       const uint32_t content_id = backend::buildSlinkContentId(backend::SlinkEmulationFlag::Subsystem, 0, 0);
0282       const auto& eid = last_emul_event_.first;
0283       const uint64_t event_id = std::get<0>(eid), bx_id = std::get<1>(eid), orbit_id = std::get<2>(eid);
0284       const auto slink_header = to128bit(to64bit(backend::buildSlinkHeader(
0285           slink_params_.boe_marker, slink_params_.format_version, event_id, content_id, fed_id)));
0286       slink_event.insert(slink_event.begin(), slink_header.begin(), slink_header.end());  // prepend S-link header
0287 
0288       // build the S-link trailer words
0289       const bool fed_crc_err = false, slinkrocket_crc_err = false, source_id_err = false, sync_lost = false,
0290                  fragment_trunc = false;
0291       const uint16_t status =
0292           backend::buildSlinkRocketStatus(fed_crc_err, slinkrocket_crc_err, source_id_err, sync_lost, fragment_trunc);
0293 
0294       const uint16_t daq_crc = 0, crc = 0;
0295       const uint32_t event_length = slink_event.size() - slink_header.size() - 1;
0296       const auto slink_trailer = to128bit(to64bit(
0297           backend::buildSlinkTrailer(slink_params_.eoe_marker, daq_crc, event_length, bx_id, orbit_id, crc, status)));
0298       slink_event.insert(slink_event.end(), slink_trailer.begin(), slink_trailer.end());  // append S-link trailer
0299 
0300       LogDebug("HGCalFrameGenerator").log([&slink_header, &slink_trailer](auto& log) {
0301         printWords(log, "slink header", slink_header);
0302         log << "\n";
0303         printWords(log, "slink trailer", slink_trailer);
0304       });
0305     }
0306 
0307     return to128bit(slink_event);
0308   }
0309 }  // namespace hgcal