From d4e23dba14c93208e8e4ba498ef73d6d6c45e109 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 5 Apr 2019 09:17:24 +0200 Subject: [PATCH 1/2] Per-link superpages in MC->raw conversion, multiple link per RU --- .../ITSMFTReconstruction/ChipMappingITS.h | 8 +- .../ITSMFTReconstruction/PayLoadCont.h | 14 + .../ITSMFTReconstruction/RawPixelReader.h | 532 ++++++++---------- 3 files changed, 267 insertions(+), 287 deletions(-) diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h index 7d89a7974aebd..af6905e439e86 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h @@ -153,16 +153,16 @@ class ChipMappingITS int getNChipsPerCable(int ruType) { return NChipsPerCableSB[ruType]; } ///< get number cables on the RU served by a given RU type - int getNCablesOnRUType(int ruType) { return NCablesPerStaveSB[ruType]; } + int getNCablesOnRUType(int ruType) const { return NCablesPerStaveSB[ruType]; } ///< get pattern of lanes on the RU served by a given RU type - int getCablesOnRUType(int ruType) { return CablesOnStaveSB[ruType]; } + int getCablesOnRUType(int ruType) const { return CablesOnStaveSB[ruType]; } ///< get number of chips served by RU of given type (i.e. RU type for ITS) - int getNChipsOnRUType(int ruType) { return NChipsPerStaveSB[ruType]; } + int getNChipsOnRUType(int ruType) const { return NChipsPerStaveSB[ruType]; } ///< get RU type from the sequential ID of the RU - int getRUType(int ruID) + int getRUType(int ruID) const { ///< get the RU type corresponding to RU with secuential number ruID if (ruID > NStavesSB[IB] + NStavesSB[MB] - 1) { diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PayLoadCont.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PayLoadCont.h index 0d68b363e3ea4..9ce665090b6c0 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PayLoadCont.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PayLoadCont.h @@ -74,6 +74,13 @@ class PayLoadCont } } + ///< fill n bytes with given symbol w/o checking for the size + void fillFast(const uint8_t c, size_t n) + { + std::memset(mEnd, c, n); + mEnd += n; + } + ///< add n bytes to the buffer w/o checking for the size void addFast(const uint8_t* ptr, size_t n) { @@ -104,6 +111,13 @@ class PayLoadCont } } + ///< fill n bytes with given symbol + void fill(const uint8_t c, size_t n) + { + ensureFreeCapacity(n); + fillFast(c, n); + } + ///< add n bytes to the buffer, expand if needed. no check for overlap void add(const uint8_t* ptr, size_t n) { diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h index 4363cbca3d2cb..1f8e04b24f0b9 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h @@ -154,40 +154,12 @@ struct RawDecodingStat { ClassDefNV(RawDecodingStat, 1); }; -struct RUEncodeData { - // Scatter-gather list for the data of different cables data created during encoding, - // plust some aux data for needed for the encoding - std::array cableEntryInRaw{}; // entry of given cable data in raw buffer - std::array cableEndInRaw{}; // end of given cable data in raw buffer - std::array cableHWID{}; // HW id's of the cables - PayLoadCont buffer; // buffer to hold alpide converted data - - o2::InteractionRecord bcData; - - int id = -1; // secuential ID of the RU - int type = -1; // type of the RU (or stave it is serving) - int nChips = 0; // number of chips served by this RU - int nCables = 0; // number of cables served by this RU - // - int chipIDFirst2Conv = 0; // 1st chip whose data are still not converted to raw - - o2::ITSMFT::ChipInfo chInfo; // info on the chip currenly processed - - void clear() - { - std::fill_n(cableEndInRaw.begin(), nCables, 0); - nChips = 0; - nCables = 0; - id = type = -1; - chipIDFirst2Conv = 0; - } -}; - // support for the GBT single link data struct RULink { PayLoadCont data; // data buffer per link int lastPageSize = 0; // size of last added page = offset from the end to get to the RDH int nTriggers = 0; // number of triggers loaded (the last one might be incomplete) + uint32_t lanes = 0; // lanes served by this link }; struct RUDecodeData { @@ -319,109 +291,92 @@ class RawPixelReader : public PixelReader mRawBuffer.clear(); } + ///================================== Encoding methods ======================== + ///______________________________________________________________________ int digits2raw(const std::vector& digiVec, int from, int ndig, const o2::InteractionRecord& bcData, - PayLoadCont& sink, uint8_t ruSWMin = 0, uint8_t ruSWMax = 0xff) + uint8_t ruSWMin = 0, uint8_t ruSWMax = 0xff) { - // convert vector of digits to binary vector (no reset is applied) - constexpr uint16_t DummyChip = 0xffff; + // Convert ndig digits belonging to the same trigger to raw data + // The digits in the vector must be in increasing chipID order + // Return the number of pages in the link with smallest amount of pages + int nDigTot = digiVec.size(); assert(from < nDigTot); - int oldsize = sink.getSize(), last = (from + ndig <= nDigTot) ? from + ndig : nDigTot; - int curChipID = DummyChip; // currently processed SW chip id - - o2::ITSMFT::ChipPixelData chipData; // data of currently processed chip - - mRUEncode.clear(); - chipData.clear(); - chipData.setChipID(DummyChip); - int emptyRUID = 0; - bool ignore = false; + int last = (from + ndig <= nDigTot) ? from + ndig : nDigTot; + RUDecodeData* curRUDecode = nullptr; + ChipPixelData* curChipData = nullptr; + ChipInfo chInfo; + UShort_t curChipID = 0xffff; // currently processed SW chip id + mInteractionRecord = bcData; + ruSWMax = (ruSWMax < uint8_t(MAP.getNRUs())) ? ruSWMax : MAP.getNRUs() - 1; + + if (mNRUs < int(ruSWMax) - ruSWMin) { // book containers if needed + for (uint8_t ru = ruSWMin; ru <= ruSWMax; ru++) { + auto& ruData = getCreateRUDecode(ru); + int nLinks = 0; + for (int il = 0; il < MaxLinksPerRU; il++) { + nLinks += ruData.links[il] ? 1 : 0; + } + mNLinks += nLinks; + if (!nLinks) { + LOG(INFO) << "Imposing single link readout for RU " << int(ru); + ruData.links[0] = std::make_unique(); + ruData.links[0]->lanes = MAP.getCablesOnRUType(ruData.ruInfo->ruType); + mNLinks++; + } + } + } - mRUEncode.bcData = bcData; + // place digits into corresponding chip buffers for (int id = from; id < last; id++) { const auto& dig = digiVec[id]; - if (dig.getChipIndex() != curChipID) { // new chip data start - - // since a new chip data start, convert digits of previously processed chip to Alpide format in the temp. buffer - if (curChipID != DummyChip && !ignore) { - convertChipData(mRUEncode, chipData); + if (curChipID != dig.getChipIndex()) { + MAP.getChipInfoSW(dig.getChipIndex(), chInfo); + if (chInfo.ru < ruSWMin || chInfo.ru > ruSWMax) { // ignore this chip? + continue; } curChipID = dig.getChipIndex(); - MAP.getChipInfoSW(curChipID, mRUEncode.chInfo); - - if (mRUEncode.chInfo.ru != mRUEncode.id) { // new RU data starts, flush the previous one - if (mRUEncode.id != -1) { - for (; emptyRUID < mRUEncode.id; emptyRUID++) { // flush empty raw data for RUs w/o digits - if (emptyRUID >= ruSWMin && emptyRUID <= ruSWMax) { // do we want this RU? - flushEmptyRU(emptyRUID, mRUEncode.bcData, sink); - } - } - emptyRUID = mRUEncode.id + 1; // flushing of next empty RU will start from this one - if (!ignore) { - flushRUData(mRUEncode, sink); // flush already converted RU data - } - } - mRUEncode.id = mRUEncode.chInfo.ru; // and update with new RU identifier - mRUEncode.type = mRUEncode.chInfo.ruType; - mRUEncode.nChips = MAP.getNChipsOnRUType(mRUEncode.chInfo.ruType); - mRUEncode.nCables = MAP.getNCablesOnRUType(mRUEncode.chInfo.ruType); - ignore = mRUEncode.id < ruSWMin || mRUEncode.id > ruSWMax; - } - chipData.setChipID(mRUEncode.chInfo.chOnRU->chipOnModuleHW); // HW chip ID in module - } - if (!ignore) { // add only if the RU is not to be rejected - chipData.getData().emplace_back(&dig); // add new digit to the container + mCurRUDecodeID = chInfo.ru; + curRUDecode = &mRUDecodeVec[mCurRUDecodeID]; + curChipData = &curRUDecode->chipsData[curRUDecode->nChipsFired++]; + curChipData->setChipID(chInfo.chOnRU->id); // set ID within the RU } + curChipData->getData().emplace_back(&dig); // add new digit to the container } - if (!ignore) { - convertChipData(mRUEncode, chipData); // finish what is left in the buffer - } - for (; emptyRUID < mRUEncode.id; emptyRUID++) { // flush empty raw data for RUs w/o digits - if (emptyRUID >= ruSWMin && emptyRUID <= ruSWMax) { // do we want this RU? - flushEmptyRU(emptyRUID, mRUEncode.bcData, sink); + // convert digits to alpide data in the per-cable buffers + int minPages = 0xffffff; + for (mCurRUDecodeID = ruSWMin; mCurRUDecodeID <= int(ruSWMax); mCurRUDecodeID++) { + curRUDecode = &mRUDecodeVec[mCurRUDecodeID]; + uint16_t next2Proc = 0, nchTot = MAP.getNChipsOnRUType(curRUDecode->ruInfo->ruType); + for (int ich = 0; ich < curRUDecode->nChipsFired; ich++) { + auto& chipData = curRUDecode->chipsData[ich]; + convertEmptyChips(next2Proc, chipData.getChipID()); // if needed store EmptyChip flags + next2Proc = chipData.getChipID() + 1; + convertChip(chipData); + chipData.clear(); } - } - if (!ignore) { - flushRUData(mRUEncode, sink); - } - // flush empty raw data for RUs w/o digits - for (emptyRUID = mRUEncode.chInfo.ru + 1; emptyRUID < MAP.getNRUs(); emptyRUID++) { - if (emptyRUID >= ruSWMin && emptyRUID <= ruSWMax) { // do we want this RU? - flushEmptyRU(emptyRUID, mRUEncode.bcData, sink); + convertEmptyChips(next2Proc, nchTot); // if needed store EmptyChip flags + int minPageRU = fillRULinks(); // flush per-lane buffers to link buffers + if (minPageRU < minPages) { + minPages = minPageRU; } } - return last - from; - } - - //___________________________________________________________________________________ - void convertChipData(RUEncodeData& ruData, o2::ITSMFT::ChipPixelData& chData) - { - // convert digits of single chip within the RU to Alpide format, storing raw data in the - // internal buffer of the ruData - static o2::ITSMFT::ChipPixelData dummyData; - // add empty chip records for all digit-less chips with ID < currently processed chipID - convertEmptyChips(ruData, ruData.chInfo.chOnRU->id); - - convertChipDigits(ruData, chData); // convert previously filled chip data to raw - - chData.clear(); - ruData.chipIDFirst2Conv = ruData.chInfo.chOnRU->id + 1; // flag 1st unconverted chip + return minPages; } //___________________________________________________________________________________ - void convertChipDigits(RUEncodeData& ruData, o2::ITSMFT::ChipPixelData& pixData) + void convertChip(o2::ITSMFT::ChipPixelData& chipData) { - ///< convert digits of single chip to Alpide format - const auto& chip = *ruData.chInfo.chOnRU; + ///< convert digits of single chip to Alpide format. + auto& ruData = mRUDecodeVec[mCurRUDecodeID]; // current RU container + // fetch info of the chip with chipData->getChipID() ID within the RU + const auto& chip = *MAP.getChipOnRUInfo(ruData.ruInfo->ruType, chipData.getChipID()); ruData.cableHWID[chip.cableSW] = chip.cableHW; // register the cable HW ID - if (!chip.chipOnCable) { // is this a 1st chip served by this cable (i.e. master)? - ruData.cableEntryInRaw[chip.cableSW] = ruData.buffer.getSize(); // start of this cable data - } - auto& pixels = pixData.getData(); + auto& pixels = chipData.getData(); std::sort(pixels.begin(), pixels.end(), [](auto lhs, auto rhs) { if (lhs.getRow() < rhs.getRow()) @@ -430,194 +385,204 @@ class RawPixelReader : public PixelReader return false; return lhs.getCol() < rhs.getCol(); }); - ruData.buffer.ensureFreeCapacity(40 * (2 + pixels.size())); // make sure buffer has enough capacity - mCoder.encodeChip(ruData.buffer, pixData, chip.chipOnModuleHW, ruData.bcData.bc); - - ruData.cableEndInRaw[chip.cableSW] = ruData.buffer.getSize(); // current end of this cable data + ruData.cableData[chip.cableSW].ensureFreeCapacity(40 * (2 + pixels.size())); // make sure buffer has enough capacity + mCoder.encodeChip(ruData.cableData[chip.cableSW], chipData, chip.chipOnModuleHW, mInteractionRecord.bc); } //______________________________________________________ - void convertEmptyChips(RUEncodeData& ruData, int upto) + void convertEmptyChips(int fromChip, int uptoChip) { - // add empty chip markers - int chipIDSW = ruData.chipIDFirst2Conv; - int cableID = 0; - auto& buffer = ruData.buffer; - buffer.ensureFreeCapacity(ruData.nChips << 2); // we need at least 2 bytes per empty chip, here I ensure 4 - for (; chipIDSW < upto; chipIDSW++) { // flag chips w/o data - - const auto& chip = *MAP.getChipOnRUInfo(ruData.type, chipIDSW); - ruData.cableHWID[chip.cableSW] = chip.cableHW; // register the cable HW ID - if (!chip.chipOnCable) { // is this a 1st chip served by this cable (i.e. master)? - ruData.cableEntryInRaw[chip.cableSW] = buffer.getSize(); // start of this cable data - } - mCoder.addEmptyChip(buffer, chip.chipOnModuleHW, ruData.bcData.bc); - ruData.cableEndInRaw[chip.cableSW] = buffer.getSize(); // current end of this cable data + // add empty chip words to respective cable's buffers for all chips of the current RU container + auto& ruData = mRUDecodeVec[mCurRUDecodeID]; // current RU container + for (int chipIDSW = fromChip; chipIDSW < uptoChip; chipIDSW++) { // flag chips w/o data + const auto& chip = *MAP.getChipOnRUInfo(ruData.ruInfo->ruType, chipIDSW); + ruData.cableHWID[chip.cableSW] = chip.cableHW; // register the cable HW ID + ruData.cableData[chip.cableSW].ensureFreeCapacity(100); + mCoder.addEmptyChip(ruData.cableData[chip.cableSW], chip.chipOnModuleHW, mInteractionRecord.bc); } - ruData.chipIDFirst2Conv = upto; - } - - //_____________________________________________________________________________ - void flushEmptyRU(int ruID, const o2::InteractionRecord& bcData, PayLoadCont& sink) - { - // create raw data for empty RU - - mRUEncodeEmpty.type = MAP.getRUType(ruID); - mRUEncodeEmpty.id = ruID; - mRUEncodeEmpty.nChips = MAP.getNChipsOnRUType(mRUEncodeEmpty.type); - mRUEncodeEmpty.nCables = MAP.getNCablesOnRUType(mRUEncodeEmpty.type); - mRUEncodeEmpty.bcData = bcData; - mRUEncodeEmpty.chipIDFirst2Conv = 0; - if (mVerbose) { - LOG(INFO) << "Flushing empty RU#" << mRUEncodeEmpty.id - << " Orbit:" << mRUEncodeEmpty.bcData.orbit << " BC: " << mRUEncodeEmpty.bcData.bc; - } - convertEmptyChips(mRUEncodeEmpty, mRUEncodeEmpty.nChips); - flushRUData(mRUEncodeEmpty, sink); - } - - //_____________________________________________________________________________ - void printRDH(const o2::header::RAWDataHeader& h) - { - printf("RDH| Ver:%2u Hsz:%2u Blgt:%4u FEEId:0x%04x PBit:%u\n", - uint32_t(h.version), uint32_t(h.headerSize), uint32_t(h.blockLength), uint32_t(h.feeId), uint32_t(h.priority)); - printf("RDH|[CRU: Offs:%5u Msz:%4u LnkId:0x%02x Packet:%3u CRUId:0x%04x]\n", - uint32_t(h.offsetToNext), uint32_t(h.memorySize), uint32_t(h.linkID), uint32_t(h.packetCounter), uint32_t(h.cruID)); - printf("RDH| TrgOrb:%9u HBOrb:%9u TrgBC:%4u HBBC:%4u TrgType:%u\n", - uint32_t(h.triggerOrbit), uint32_t(h.heartbeatOrbit), uint32_t(h.triggerBC), uint32_t(h.heartbeatBC), - uint32_t(h.triggerType)); - printf("RDH| DetField:0x%05x Par:0x%04x Stop:0x%04x PageCnt:%5u\n", uint32_t(h.detectorField), uint32_t(h.par), uint32_t(h.stop), uint32_t(h.pageCnt)); } //___________________________________________________________________________________ - void flushRUData(RUEncodeData& ruData, PayLoadCont& sink) + int fillRULinks() { + // fill data of the RU to links buffer, return the number of pages in the link with smallest amount of pages constexpr uint8_t zero16[o2::ITSMFT::GBTPaddedWordLength] = { 0 }; // to speedup padding - if (ruData.id < 0) { - return; - } - convertEmptyChips(ruData, ruData.nChips); // fill empty chips up to the last chip of the RU - - // calculate number of GBT words needed to store this data (with 9 bytes per GBT word) - int nGBTWordsNeeded = 0; - for (int i = 0; i < ruData.nCables; i++) { - int start = ruData.cableEntryInRaw[i]; - int end = ruData.cableEndInRaw[i]; - int nb = end > start ? end - start : 0; - nGBTWordsNeeded += nb ? 1 + (nb - 1) / 9 : 0; - } - // - // prepare RDH - // 4x128 bit, represented as 8 64-bit words + const int dummyNPages = 0xffffff; // any large number + int minPages = dummyNPages; + auto& ruData = mRUDecodeVec[mCurRUDecodeID]; + ruData.nCables = ruData.ruInfo->nCables; o2::header::RAWDataHeader rdh; - rdh.headerSize = 0x40; // 4*128 bits; - rdh.feeId = MAP.RUSW2FEEId(ruData.id, 0); // write on link 0 always - rdh.triggerOrbit = rdh.heartbeatOrbit = ruData.bcData.orbit; - rdh.triggerBC = rdh.heartbeatBC = ruData.bcData.bc; - rdh.triggerType = o2::trigger::PhT; + rdh.triggerOrbit = rdh.heartbeatOrbit = mInteractionRecord.orbit; + rdh.triggerBC = rdh.heartbeatBC = mInteractionRecord.bc; + rdh.triggerType = o2::trigger::PhT; // ?? rdh.detectorField = MAP.getRUDetectorField(); - rdh.par = 0; - o2::ITSMFT::GBTDataHeader gbtHeader(0, MAP.getCablesOnRUType(ruData.type)); - o2::ITSMFT::GBTDataTrailer gbtTrailer; + rdh.blockLength = 0xffff; // ITS keeps this dummy - // max real payload words (accounting for GBT header and trailer) per packet - // Note: for this estimate we use GBTPaddedWordLength rather than the actual mGBTWordSize int maxGBTWordsPerPacket = (MaxGBTPacketBytes - rdh.headerSize) / o2::ITSMFT::GBTPaddedWordLength - 2; - if (sink.getFreeCapacity() < 2 * MaxGBTPacketBytes) { // make sure there is enough capacity - sink.expand(sink.getCapacity() + 10 * MaxGBTPacketBytes); - } - - rdh.blockLength = 0xffff; //MaxGBTPacketBytes; ITS does not fill it // total packet size: always use max size ? in bits ? - rdh.memorySize = rdh.headerSize + (nGBTWordsNeeded + 2) * mGBTWordSize; // update remaining size - if (rdh.memorySize > MaxGBTPacketBytes) { - rdh.memorySize = MaxGBTPacketBytes; - } - rdh.offsetToNext = mImposeMaxPage ? MaxGBTPacketBytes : rdh.memorySize; - rdh.stop = 0; + int nGBTW[MaxLinksPerRU] = { 0 }; + for (int il = 0; il < MaxLinksPerRU; il++) { + auto link = ruData.links[il].get(); + if (!link) { + continue; + } + int nGBTWordsNeeded = 0; + for (int icab = ruData.nCables; icab--;) { // calculate number of GBT words per link + if ((link->lanes & (0x1 << icab))) { + int nb = ruData.cableData[icab].getSize(); + nGBTWordsNeeded += nb ? 1 + (nb - 1) / 9 : 0; + } + } + // move data in padded GBT words from cable buffers to link buffers + rdh.feeId = MAP.RUSW2FEEId(ruData.ruInfo->idSW, il); // write on link 0 always + rdh.linkID = il; + rdh.pageCnt = 0; + rdh.stop = 0; + rdh.memorySize = rdh.headerSize + (nGBTWordsNeeded + 2) * mGBTWordSize; // update remaining size + if (rdh.memorySize > MaxGBTPacketBytes) { + rdh.memorySize = MaxGBTPacketBytes; + } + rdh.offsetToNext = mImposeMaxPage ? MaxGBTPacketBytes : rdh.memorySize; - sink.addFast(reinterpret_cast(&rdh), rdh.headerSize); // write RDH for current packet + link->data.ensureFreeCapacity(MaxGBTPacketBytes); + link->data.addFast(reinterpret_cast(&rdh), rdh.headerSize); // write RDH for current packet + link->nTriggers++; // acknowledge the page, note: here we count pages, not triggers + o2::ITSMFT::GBTDataHeader gbtHeader(0, link->lanes); + o2::ITSMFT::GBTDataTrailer gbtTrailer; // lanes will be set on closing the last page - gbtHeader.setPacketID(rdh.pageCnt); - sink.addFast(gbtHeader.getW8(), mGBTWordSize); // write GBT header for current packet + gbtHeader.setPacketID(rdh.pageCnt); + link->data.addFast(gbtHeader.getW8(), mGBTWordSize); // write GBT header for current packet + if (mVerbose) { + LOG(INFO) << "Filling RU data"; + printRDH(rdh); + gbtHeader.printX(mPadding128); + } - if (mVerbose) { - LOG(INFO) << "Flushing RU data"; - printRDH(rdh); - gbtHeader.printX(mPadding128); - } + // now loop over the lanes served by this link, writing each time at most 9 bytes, untill all lanes are copied + int nGBTWordsInPacket = 0; + do { + for (int icab = 0; icab < ruData.nCables; icab++) { + if ((link->lanes & (0x1 << icab))) { + auto& cableData = ruData.cableData[icab]; + int nb = cableData.getUnusedSize(); + if (!nb) { + continue; // write 80b word only if there is something to write + } + if (nb > 9) { + nb = 9; + } + int gbtWordStart = link->data.getSize(); // beginning of the current GBT word in the link + link->data.addFast(cableData.getPtr(), nb); // fill payload of cable + link->data.addFast(zero16, mGBTWordSize - nb); // fill the rest of the GBT word by 0 + link->data[gbtWordStart + 9] = MAP.getGBTHeaderRUType(ruData.ruInfo->ruType, ruData.cableHWID[icab]); // set cable flag + cableData.setPtr(cableData.getPtr() + nb); + nGBTWordsNeeded--; + if (mVerbose) { + ((GBTData*)(&link->data[gbtWordStart]))->printX(mPadding128); + } + if (++nGBTWordsInPacket == maxGBTWordsPerPacket) { // check if new GBT packet must be created + break; + } + } // storing data of single cable + } // loop over cables of this link - int nGBTWordsInPacket = 0; - do { - for (int icab = 0; icab < ruData.nCables; icab++) { - int &start(ruData.cableEntryInRaw[icab]), &end(ruData.cableEndInRaw[icab]), nb = end - start; - if (nb > 0) { // write 80b word only if there is something to write - if (nb > 9) { - nb = 9; + if (nGBTWordsNeeded && nGBTWordsInPacket >= maxGBTWordsPerPacket) { + // more data to write, write trailer and add new GBT packet + link->data.add(gbtTrailer.getW8(), mGBTWordSize); // write empty GBT trailer for current packet + if (mVerbose) { + gbtTrailer.printX(mPadding128); + } + rdh.pageCnt++; // flag new page + rdh.stop = nGBTWordsNeeded < maxGBTWordsPerPacket; // flag if this is the last packet of multi-packet + rdh.blockLength = 0xffff; // (nGBTWordsNeeded % maxGBTWordsPerPacket + 2) * mGBTWordSize; // record payload size + // update remaining size, using padded GBT words (as CRU writes) + rdh.memorySize = rdh.headerSize + (nGBTWordsNeeded + 2) * o2::ITSMFT::GBTPaddedWordLength; + if (rdh.memorySize > MaxGBTPacketBytes) { + rdh.memorySize = MaxGBTPacketBytes; } - int gbtWordStart = sink.getSize(); // beginning of the current GBT word in the sink - sink.add(ruData.buffer.data() + start, nb); - // fill the rest of the GBT word by 0 - sink.add(zero16, mGBTWordSize - nb); - sink[gbtWordStart + 9] = MAP.getGBTHeaderRUType(ruData.type, ruData.cableHWID[icab]); // set cable flag - start += nb; - nGBTWordsNeeded--; + rdh.offsetToNext = mImposeMaxPage ? MaxGBTPacketBytes : rdh.memorySize; + link->data.ensureFreeCapacity(MaxGBTPacketBytes); + link->data.addFast(reinterpret_cast(&rdh), rdh.headerSize); // write RDH for current packet + link->nTriggers++; // acknowledge the page, note: here we count pages, not triggers if (mVerbose) { - ((GBTData*)(sink.data() + gbtWordStart))->printX(mPadding128); + printRDH(rdh); } - if (++nGBTWordsInPacket == maxGBTWordsPerPacket) { // check if new GBT packet must be created - break; + gbtHeader.setPacketID(rdh.pageCnt); + link->data.addFast(gbtHeader.getW8(), mGBTWordSize); // write GBT header for current packet + if (mVerbose) { + gbtHeader.printX(mPadding128); } - } // storing data of single cable - } // loop over cables - - if (nGBTWordsNeeded && nGBTWordsInPacket >= maxGBTWordsPerPacket) { - // more data to write, write trailer and add new GBT packet - sink.add(gbtTrailer.getW8(), mGBTWordSize); // write GBT trailer for current packet - if (mVerbose) { - gbtTrailer.printX(mPadding128); - } - rdh.pageCnt++; // flag new page - rdh.stop = nGBTWordsNeeded < maxGBTWordsPerPacket; // flag if this is the last packet of multi-packet - rdh.blockLength = 0xffff; // (nGBTWordsNeeded % maxGBTWordsPerPacket + 2) * mGBTWordSize; // record payload size - // update remaining size, using padded GBT words (as CRU writes) - rdh.memorySize = rdh.headerSize + (nGBTWordsNeeded + 2) * o2::ITSMFT::GBTPaddedWordLength; - if (rdh.memorySize > MaxGBTPacketBytes) { - rdh.memorySize = MaxGBTPacketBytes; - } - rdh.offsetToNext = mImposeMaxPage ? MaxGBTPacketBytes : rdh.memorySize; - sink.add(reinterpret_cast(&rdh), rdh.headerSize); // write RDH for new packet - if (mVerbose) { - printRDH(rdh); + nGBTWordsInPacket = 0; // reset counter of words in the packet } - gbtHeader.setPacketID(rdh.pageCnt); - sink.addFast(gbtHeader.getW8(), mGBTWordSize); // write GBT header for current packet - if (mVerbose) { - gbtHeader.printX(mPadding128); - } - nGBTWordsInPacket = 0; // reset counter of words in the packet - } + } while (nGBTWordsNeeded); - } while (nGBTWordsNeeded); + gbtTrailer.setLanesStop(link->lanes); + gbtTrailer.setPacketState(0x1 << GBTDataTrailer::PacketDone); + link->data.addFast(gbtTrailer.getW8(), mGBTWordSize); // write GBT trailer for the last packet + if (mVerbose) { + gbtTrailer.printX(mPadding128); + } + // NOTE: here we don't pad the page to 8KB, will do this when flushing everything to the sink - gbtTrailer.setLanesStop(MAP.getCablesOnRUType(ruData.type)); - gbtTrailer.setPacketState(0x1 << GBTDataTrailer::PacketDone); + if (minPages > link->nTriggers) { + minPages = link->nTriggers; + } - sink.add(gbtTrailer.getW8(), mGBTWordSize); // write GBT trailer for the last packet - if (mVerbose) { - gbtTrailer.printX(mPadding128); - } + } // loop over links of RU + ruData.clearTrigger(); + ruData.nChipsFired = 0; + return minPages == dummyNPages ? 0 : minPages; + } - // at the moment the GBT page must be aligned to MaxGBTPacketBytes, fill by 0s - if (mImposeMaxPage) { - while (nGBTWordsInPacket++ < maxGBTWordsPerPacket) { - sink.add(zero16, mGBTWordSize); - if (mVerbose) { - ((GBTData*)zero16)->printX(mPadding128); - } + //___________________________________________________________________________________ + int flushSuperPages(int maxPages, PayLoadCont& sink) + { + // flush superpage (at most maxPages) of each link to the output, + // return total number of pages flushed + + int totPages = 0; + for (int ru = 0; ru < MAP.getNRUs(); ru++) { + auto* ruData = getRUDecode(ru); + if (!ruData) { + continue; } - } - ruData.clear(); + for (int il = 0; il < MaxLinksPerRU; il++) { + auto link = ruData->links[il].get(); + if (!link || link->data.isEmpty()) { + continue; + } + int nPages = 0; + sink.ensureFreeCapacity(maxPages * MaxGBTPacketBytes); + const auto* ptrIni = link->data.getPtr(); + while (nPages < maxPages && !link->data.isEmpty()) { + const auto ptr = link->data.getPtr(); + o2::header::RAWDataHeader* rdh = reinterpret_cast(ptr); + sink.addFast(ptr, rdh->memorySize); // copy header + payload + sink.fillFast(0, MaxGBTPacketBytes - rdh->memorySize); // complete with 0's till the end of the page + link->data.setPtr(ptr + rdh->memorySize); + link->nTriggers--; // here we count pages, not triggers + nPages++; + } + totPages += nPages; + link->data.moveUnusedToHead(); + } // loop over links + } // loop over RUs + return totPages; + } + + ///================================== Decoding methods ======================== + + //_____________________________________________________________________________ + void printRDH(const o2::header::RAWDataHeader& h) + { + printf("RDH| Ver:%2u Hsz:%2u Blgt:%4u FEEId:0x%04x PBit:%u\n", + uint32_t(h.version), uint32_t(h.headerSize), uint32_t(h.blockLength), uint32_t(h.feeId), uint32_t(h.priority)); + printf("RDH|[CRU: Offs:%5u Msz:%4u LnkId:0x%02x Packet:%3u CRUId:0x%04x]\n", + uint32_t(h.offsetToNext), uint32_t(h.memorySize), uint32_t(h.linkID), uint32_t(h.packetCounter), uint32_t(h.cruID)); + printf("RDH| TrgOrb:%9u HBOrb:%9u TrgBC:%4u HBBC:%4u TrgType:%u\n", + uint32_t(h.triggerOrbit), uint32_t(h.heartbeatOrbit), uint32_t(h.triggerBC), uint32_t(h.heartbeatBC), + uint32_t(h.triggerType)); + printf("RDH| DetField:0x%05x Par:0x%04x Stop:0x%04x PageCnt:%5u\n", uint32_t(h.detectorField), uint32_t(h.par), uint32_t(h.stop), uint32_t(h.pageCnt)); } //_____________________________________ @@ -645,11 +610,7 @@ class RawPixelReader : public PixelReader } int ruIDSW = getMapping().FEEId2RUSW(rdh->feeId); - if (mRUEntry[ruIDSW] < 0) { // do we see this RU for the 1st time - mRUEntry[ruIDSW] = mNRUs++; - mRUDecodeVec[mRUEntry[ruIDSW]].ruInfo = MAP.getRUInfoSW(ruIDSW); // info on the stave/RU - } - auto& ruDecode = mRUDecodeVec[mRUEntry[ruIDSW]]; + auto& ruDecode = getCreateRUDecode(ruIDSW); bool newTrigger = true; // check if we see new trigger auto link = ruDecode.links[rdh->linkID].get(); @@ -1044,10 +1005,7 @@ class RawPixelReader : public PixelReader #endif int ruIDSW = MAP.FEEId2RUSW(rdh->feeId); - if (mRUEntry[ruIDSW] < 0) { // do we see this RU for the 1st time - mRUEntry[ruIDSW] = mNRUs++; - mRUDecodeVec[mRUEntry[ruIDSW]].ruInfo = MAP.getRUInfoSW(ruIDSW); // info on the stave/RU - } + auto& ruDecode = getCreateRUDecode(ruIDSW); auto ruInfo = MAP.getRUInfoSW(ruIDSW); mInteractionRecord.bc = rdh->triggerBC; @@ -1058,7 +1016,7 @@ class RawPixelReader : public PixelReader mInteractionRecordHB.bc = rdh->heartbeatBC; mInteractionRecordHB.orbit = rdh->heartbeatOrbit; - auto& ruStat = mRUDecodeVec[mRUEntry[ruIDSW]].statistics; + auto& ruStat = ruDecode.statistics; ruStat.nPackets++; mDecodingStat.nRUsProcessed++; @@ -1399,15 +1357,24 @@ class RawPixelReader : public PixelReader return mRUEntry[ruSW] < 0 ? nullptr : &mRUDecodeVec[mRUEntry[ruSW]]; } + // get RU decode container for RU with given SW ID, if does not exist, create it + RUDecodeData& getCreateRUDecode(int ruSW) + { + assert(ruSW < MAP.getNRUs()); + if (mRUEntry[ruSW] < 0) { + mRUEntry[ruSW] = mNRUs++; + mRUDecodeVec[mRUEntry[ruSW]].ruInfo = MAP.getRUInfoSW(ruSW); // info on the stave/RU + LOG(INFO) << "Defining container for RU " << ruSW << " at slot " << mRUEntry[ruSW]; + } + return mRUDecodeVec[mRUEntry[ruSW]]; + } + private: std::ifstream mIOFile; Coder mCoder; Mapping MAP; int mVerbose = 0; //! verbosity level - RUEncodeData mRUEncode; //! buffer for the digits to convert - RUEncodeData mRUEncodeEmpty; //! placeholder for empty RU data - - int mCurRUDecodeID = -1; //! index of currently processed RUDecode container + int mCurRUDecodeID = -1; //! index of currently processed RUDecode container PayLoadCont mRawBuffer; //! buffer for binary raw data file IO @@ -1421,7 +1388,6 @@ class RawPixelReader : public PixelReader int mMinTriggersCached = 0; //! actual minimum (among different links) number of triggers to cache // statistics - //XXX std::array mRUDecodingStat; //! statics of decoding per FEE RawDecodingStat mDecodingStat; //! global decoding statistics TStopwatch mSWIO; //! timer for IO operations From e6a59c19659bf55a46c0c3cf0beed235e0e80efb Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 6 Apr 2019 20:11:34 +0200 Subject: [PATCH 2/2] Sample macro for MC->Raw conversion with multiple link/RU and certain CRU superpage size --- macro/run_digi2raw_its.C | 69 +++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/macro/run_digi2raw_its.C b/macro/run_digi2raw_its.C index 61607332df4b6..3fd1acb90a6fa 100644 --- a/macro/run_digi2raw_its.C +++ b/macro/run_digi2raw_its.C @@ -6,21 +6,23 @@ #include #include #include - +#include #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/GBTWord.h" #include "ITSMFTReconstruction/PayLoadCont.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTBase/Digit.h" -#include "ITSMFTReconstruction/RawPixelReader.h" #endif -void run_digi2raw_its(std::string outName = "rawits.bin", // name of the output binary file - std::string inpName = "itsdigits.root", // name of the input ITS digits - std::string digTreeName = "o2sim", // name of the digits tree - std::string digBranchName = "ITSDigit", // name of the digits branch - std::string rofRecName = "ITSDigitROF", // name of the ROF records tree and its branch - uint8_t ruSWMin = 0, uint8_t ruSWMax = 0xff // seq.ID of 1st and last RU (stave) to convert +#include "ITSMFTReconstruction/RawPixelReader.h" + +void run_digi2raw_its(std::string outName = "rawits.bin", // name of the output binary file + std::string inpName = "itsdigits.root", // name of the input ITS digits + std::string digTreeName = "o2sim", // name of the digits tree + std::string digBranchName = "ITSDigit", // name of the digits branch + std::string rofRecName = "ITSDigitROF", // name of the ROF records tree and its branch + uint8_t ruSWMin = 0, uint8_t ruSWMax = 0xff, // seq.ID of 1st and last RU (stave) to convert + uint8_t superPageSize = o2::ITSMFT::NCRUPagesPerSuperpage / 2 // CRU superpage size, max = 256 ) { TStopwatch swTot; @@ -66,7 +68,36 @@ void run_digi2raw_its(std::string outName = "rawits.bin", // name of the o2::ITSMFT::RawPixelReader rawReader; rawReader.setPadding128(true); + rawReader.setVerbosity(0); + //------------------------------------------------------------------------------->>>> + // just as an example, we require here that the IB staves are read via 3 links, + // while OB staves use only 1 link. + // Note, that if the RU container is not defined, it will be created automatically + // during encoding. + // If the links of the container are not defined, a single link readout will be assigned + const auto& mp = rawReader.getMapping(); + for (int ir = 0; ir < mp.getNRUs(); ir++) { + auto& ru = rawReader.getCreateRUDecode(ir); // create RU container + uint32_t lanes = mp.getCablesOnRUType(ru.ruInfo->ruType); // lanes patter of this RU + if (ru.ruInfo->layer < 3) { + for (int il = 0; il < 3; il++) { // create links + ru.links[il] = std::make_unique(); + ru.links[il]->lanes = lanes & ((0x1 << 3) - 1) << (3 * il); // each link will read 3 lanes==chips + LOG(INFO) << "RU " << std::setw(3) << ir << " on lr" << int(ru.ruInfo->layer) + << " : FEEId 0x" << std::hex << std::setfill('0') << std::setw(6) << mp.RUSW2FEEId(ir, il) + << " reads lanes " << std::bitset<9>(ru.links[il]->lanes); + } + } else { // note: we are not obliged to do this if only 1 link per RU is used + ru.links[0] = std::make_unique(); + ru.links[0]->lanes = lanes; // single link reads all lanes + LOG(INFO) << "RU " << std::setw(3) << ir << " on lr" << int(ru.ruInfo->layer) + << " : FEEId 0x" << std::hex << std::setfill('0') << std::setw(6) << mp.RUSW2FEEId(ir, 0) + << " reads lanes " << std::bitset<28>(ru.links[0]->lanes); + } + } + + //-------------------------------------------------------------------------------<<<< int lastTreeID = -1; long offs = 0, nEntProc = 0; for (int i = 0; i < rofTree.GetEntries(); i++) { @@ -93,13 +124,29 @@ void run_digi2raw_its(std::string outName = "rawits.bin", // name of the int digIndex = rofEntry.getIndex(); // needed ROF digits start from this one int maxDigIndex = digIndex + nDigROF; - rawReader.digits2raw(digiVec, rofEntry.getIndex(), nDigROF, rofRec.getBCData(), outBuffer, ruSWMin, ruSWMax); + int nPagesCached = rawReader.digits2raw(digiVec, rofEntry.getIndex(), nDigROF, rofRec.getBCData(), + ruSWMin, ruSWMax); - fwrite(outBuffer.data(), 1, outBuffer.getSize(), outFl); //write to file - outBuffer.clear(); + if (nPagesCached >= superPageSize) { + int nPagesFlushed = rawReader.flushSuperPages(superPageSize, outBuffer); + fwrite(outBuffer.data(), 1, outBuffer.getSize(), outFl); //write to file + outBuffer.clear(); + LOG(INFO) << "Flushed " << nPagesFlushed << " CRU pages"; + } } } // loop over multiple ROFvectors (in case of chaining) + // flush the rest + int flushed = 0; + do { + flushed = rawReader.flushSuperPages(o2::ITSMFT::NCRUPagesPerSuperpage, outBuffer); + fwrite(outBuffer.data(), 1, outBuffer.getSize(), outFl); //write to file + if (flushed) { + LOG(INFO) << "Flushed final " << flushed << " CRU pages"; + } + outBuffer.clear(); + } while (flushed); + fclose(outFl); // swTot.Stop();