Project
Loading...
Searching...
No Matches
GPUReconstructionConvert.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#ifdef GPUCA_O2_LIB
17#include "TPCBase/Sector.h"
19#include "TPCBase/Mapper.h"
20#endif
21
23#include "TPCFastTransform.h"
24#include "GPUTPCClusterData.h"
25#include "GPUO2DataTypes.h"
26#include "GPUDataTypes.h"
27#include "AliHLTTPCRawCluster.h"
28#include "GPUParam.h"
29#include "GPULogging.h"
30#include <algorithm>
31#include <vector>
32
33#include "clusterFinderDefs.h"
39#include "TPCBase/RDHUtils.h"
40#include "TPCBase/CRU.h"
42
43#include <oneapi/tbb.h>
44
45using namespace o2::gpu;
46using namespace o2::tpc;
47using namespace o2::tpc::constants;
48using namespace std::string_literals;
49
50void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr<GPUTPCClusterData[]>* clusters, uint32_t* nClusters, const TPCFastTransform* transform, int32_t continuousMaxTimeBin)
51{
52 memset(nClusters, 0, NSECTORS * sizeof(nClusters[0]));
53 uint32_t offset = 0;
54 for (uint32_t i = 0; i < NSECTORS; i++) {
55 uint32_t nClSector = 0;
56 for (int32_t j = 0; j < GPUCA_ROW_COUNT; j++) {
57 nClSector += native->nClusters[i][j];
58 }
59 nClusters[i] = nClSector;
60 clusters[i].reset(new GPUTPCClusterData[nClSector]);
61 nClSector = 0;
62 for (int32_t j = 0; j < GPUCA_ROW_COUNT; j++) {
63 for (uint32_t k = 0; k < native->nClusters[i][j]; k++) {
64 const auto& clin = native->clusters[i][j][k];
65 float x = 0, y = 0, z = 0;
66 if (continuousMaxTimeBin == 0) {
67 transform->Transform(i, j, clin.getPad(), clin.getTime(), x, y, z);
68 } else {
69 transform->TransformInTimeFrame(i, j, clin.getPad(), clin.getTime(), x, y, z, continuousMaxTimeBin);
70 }
71 auto& clout = clusters[i].get()[nClSector];
72 clout.x = x;
73 clout.y = y;
74 clout.z = z;
75 clout.row = j;
76 clout.amp = clin.qTot;
77 clout.flags = clin.getFlags();
78 clout.id = offset + k;
79 nClSector++;
80 }
81 native->clusterOffset[i][j] = offset;
82 offset += native->nClusters[i][j];
83 }
84 }
85}
86
87void GPUReconstructionConvert::ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess& native, std::unique_ptr<ClusterNative[]>& nativeBuffer, const AliHLTTPCRawCluster** rawClusters, uint32_t* nRawClusters)
88{
89 memset((void*)&native, 0, sizeof(native));
90 for (uint32_t i = 0; i < NSECTORS; i++) {
91 for (uint32_t j = 0; j < nRawClusters[i]; j++) {
92 native.nClusters[i][rawClusters[i][j].GetPadRow()]++;
93 }
94 native.nClustersTotal += nRawClusters[i];
95 }
96 nativeBuffer.reset(new ClusterNative[native.nClustersTotal]);
97 native.clustersLinear = nativeBuffer.get();
98 native.setOffsetPtrs();
99 for (uint32_t i = 0; i < NSECTORS; i++) {
100 for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) {
101 native.nClusters[i][j] = 0;
102 }
103 for (uint32_t j = 0; j < nRawClusters[i]; j++) {
104 const AliHLTTPCRawCluster& org = rawClusters[i][j];
105 int32_t row = org.GetPadRow();
106 ClusterNative& c = nativeBuffer[native.clusterOffset[i][row] + native.nClusters[i][row]++];
107 c.setTimeFlags(org.GetTime(), org.GetFlags());
108 c.setPad(org.GetPad());
109 c.setSigmaTime(CAMath::Sqrt(org.GetSigmaTime2()));
110 c.setSigmaPad(CAMath::Sqrt(org.GetSigmaPad2()));
111 c.qMax = org.GetQMax();
112 c.qTot = org.GetCharge();
113 }
114 }
115}
116
118{
119 float retVal = 0;
120 for (uint32_t i = 0; i < NSECTORS; i++) {
121 for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) {
122 for (uint32_t k = 0; k < native.nClusters[i][j]; k++) {
123 if (native.clusters[i][j][k].getTime() > retVal) {
124 retVal = native.clusters[i][j][k].getTime();
125 }
126 }
127 }
128 }
129 return ceil(retVal);
130}
131
133{
134 float retVal = 0;
135 for (uint32_t i = 0; i < NSECTORS; i++) {
136 for (uint32_t k = 0; k < digits.nTPCDigits[i]; k++) {
137 if (digits.tpcDigits[i][k].getTimeStamp() > retVal) {
138 retVal = digits.tpcDigits[i][k].getTimeStamp();
139 }
140 }
141 }
142 return ceil(retVal);
143}
144
146{
147 float retVal = 0;
148 for (uint32_t i = 0; i < NSECTORS; i++) {
149 int32_t firstHBF = zspages.sector[i].count[0] ? o2::raw::RDHUtils::getHeartBeatOrbit(*(const o2::header::RAWDataHeader*)zspages.sector[i].zsPtr[0][0]) : 0;
150 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
151 for (uint32_t k = 0; k < zspages.sector[i].count[j]; k++) {
152 const char* page = (const char*)zspages.sector[i].zsPtr[j][k];
153 for (uint32_t l = 0; l < zspages.sector[i].nZSPtr[j][k]; l++) {
156 int32_t nTimeBinSpan = hdr->nTimeBinSpan;
158 TPCZSHDRV2* hdr2 = (TPCZSHDRV2*)hdr;
159 if (hdr2->flags & TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8) {
160 nTimeBinSpan += 256;
161 }
162 }
163 uint32_t timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN + nTimeBinSpan;
164 if (timeBin > retVal) {
165 retVal = timeBin;
166 }
167 }
168 }
169 }
170 }
171 return ceil(retVal);
172}
173
174// ------------------------------------------------- TPC ZS -------------------------------------------------
175
176#ifdef GPUCA_TPC_GEOMETRY_O2
177namespace o2::gpu
178{
179namespace // anonymous
180{
181
182// ------------------------------------------------- TPC ZS General -------------------------------------------------
183
184typedef std::array<int64_t, TPCZSHDR::TPC_ZS_PAGE_SIZE / sizeof(int64_t)> zsPage;
185
186struct zsEncoder {
187 int32_t curRegion = 0, outputRegion = 0;
188 uint32_t encodeBits = 0;
189 uint32_t zsVersion = 0;
190 uint32_t iSector = 0;
191 o2::raw::RawFileWriter* raw = nullptr;
192 const o2::InteractionRecord* ir = nullptr;
193 const GPUParam* param = nullptr;
194 bool padding = false;
195 int32_t lastEndpoint = -2, lastTime = -1, lastRow = GPUCA_ROW_COUNT;
196 int32_t endpoint = 0, outputEndpoint = 0;
197 int64_t hbf = -1, nexthbf = 0;
198 zsPage* page = nullptr;
199 uint8_t* pagePtr = nullptr;
200 int32_t bcShiftInFirstHBF = 0;
201 int32_t firstTimebinInPage = -1;
202 float encodeBitsFactor = 0;
203 bool needAnotherPage = false;
204 uint32_t packetCounter = 0;
205 uint32_t pageCounter = 0;
206 void ZSfillEmpty(void* ptr, int32_t shift, uint32_t feeId, int32_t orbit, int32_t linkid);
207 static void ZSstreamOut(uint16_t* bufIn, uint32_t& lenIn, uint8_t* bufOut, uint32_t& lenOut, uint32_t nBits);
208 int64_t getHbf(int64_t timestamp) { return (timestamp * LHCBCPERTIMEBIN + bcShiftInFirstHBF) / o2::constants::lhc::LHCMaxBunches; }
209};
210
211inline void zsEncoder::ZSfillEmpty(void* ptr, int32_t shift, uint32_t feeId, int32_t orbit, int32_t linkid)
212{
215 o2::raw::RDHUtils::setHeartBeatBC(*rdh, shift);
217 o2::raw::RDHUtils::setVersion(*rdh, o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>());
218 o2::raw::RDHUtils::setFEEID(*rdh, feeId);
220 o2::raw::RDHUtils::setLinkID(*rdh, linkid);
221 o2::raw::RDHUtils::setPacketCounter(*rdh, packetCounter++);
222 o2::raw::RDHUtils::setPageCounter(*rdh, pageCounter++);
223}
224
225inline void zsEncoder::ZSstreamOut(uint16_t* bufIn, uint32_t& lenIn, uint8_t* bufOut, uint32_t& lenOut, uint32_t nBits)
226{
227 uint32_t byte = 0, bits = 0;
228 uint32_t mask = (1 << nBits) - 1;
229 for (uint32_t i = 0; i < lenIn; i++) {
230 byte |= (bufIn[i] & mask) << bits;
231 bits += nBits;
232 while (bits >= 8) {
233 bufOut[lenOut++] = (uint8_t)(byte & 0xFF);
234 byte = byte >> 8;
235 bits -= 8;
236 }
237 }
238 if (bits) {
239 bufOut[lenOut++] = byte;
240 }
241 lenIn = 0;
242}
243
244static inline auto ZSEncoderGetDigits(const GPUTrackingInOutDigits& in, int32_t i) { return in.tpcDigits[i]; }
245static inline auto ZSEncoderGetNDigits(const GPUTrackingInOutDigits& in, int32_t i) { return in.nTPCDigits[i]; }
246#ifdef GPUCA_O2_LIB
247using DigitArray = std::array<gsl::span<const o2::tpc::Digit>, o2::tpc::Sector::MAXSECTOR>;
248static inline auto ZSEncoderGetDigits(const DigitArray& in, int32_t i) { return in[i].data(); }
249static inline auto ZSEncoderGetNDigits(const DigitArray& in, int32_t i) { return in[i].size(); }
250#endif // GPUCA_O2_LIB
251
252// ------------------------------------------------- TPC ZS Original Row-based ZS -------------------------------------------------
253
254struct zsEncoderRow : public zsEncoder {
255 std::array<uint16_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> streamBuffer = {};
256 std::array<uint8_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> streamBuffer8 = {};
257 TPCZSHDR* hdr = nullptr;
258 TPCZSTBHDR* curTBHdr = nullptr;
259 uint8_t* nSeq = nullptr;
260 int32_t seqLen = 0;
261 int32_t endpointStart = 0;
262 int32_t nRowsInTB = 0;
263 uint32_t streamSize = 0, streamSize8 = 0;
264 constexpr static int32_t RAWLNK = rdh_utils::UserLogicLinkID;
265
266 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
267 bool writeSubPage();
268 void init() { encodeBits = zsVersion == 2 ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1; }
269 void initPage() {}
270 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
271
272 bool sort(const o2::tpc::Digit a, const o2::tpc::Digit b);
273 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
274};
275
276inline bool zsEncoderRow::sort(const o2::tpc::Digit a, const o2::tpc::Digit b)
277{
278 int32_t endpointa = param->tpcGeometry.GetRegion(a.getRow());
279 int32_t endpointb = param->tpcGeometry.GetRegion(b.getRow());
280 endpointa = 2 * endpointa + (a.getRow() >= param->tpcGeometry.GetRegionStart(endpointa) + param->tpcGeometry.GetRegionRows(endpointa) / 2);
281 endpointb = 2 * endpointb + (b.getRow() >= param->tpcGeometry.GetRegionStart(endpointb) + param->tpcGeometry.GetRegionRows(endpointb) / 2);
282 if (endpointa != endpointb) {
283 return endpointa <= endpointb;
284 }
285 if (a.getTimeStamp() != b.getTimeStamp()) {
286 return a.getTimeStamp() < b.getTimeStamp();
287 }
288 if (a.getRow() != b.getRow()) {
289 return a.getRow() < b.getRow();
290 }
291 return a.getPad() < b.getPad();
292}
293
294bool zsEncoderRow::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
295{
296 seqLen = 1;
297 if (lastRow != tmpBuffer[k].getRow()) {
298 endpointStart = param->tpcGeometry.GetRegionStart(curRegion);
299 endpoint = curRegion * 2;
300 if (tmpBuffer[k].getRow() >= endpointStart + param->tpcGeometry.GetRegionRows(curRegion) / 2) {
301 endpoint++;
302 endpointStart += param->tpcGeometry.GetRegionRows(curRegion) / 2;
303 }
304 }
305 for (uint32_t l = k + 1; l < tmpBuffer.size(); l++) {
306 if (tmpBuffer[l].getRow() == tmpBuffer[k].getRow() && tmpBuffer[l].getTimeStamp() == tmpBuffer[k].getTimeStamp() && tmpBuffer[l].getPad() == tmpBuffer[l - 1].getPad() + 1) {
307 seqLen++;
308 } else {
309 break;
310 }
311 }
312 if (lastEndpoint >= 0 && lastTime != -1 && (int32_t)hdr->nTimeBinSpan + tmpBuffer[k].getTimeStamp() - lastTime >= 256) {
313 lastEndpoint = -1;
314 }
315 if (endpoint == lastEndpoint) {
316 uint32_t sizeChk = (uint32_t)(pagePtr - reinterpret_cast<uint8_t*>(page)); // already written
317 sizeChk += 2 * (nRowsInTB + (tmpBuffer[k].getRow() != lastRow && tmpBuffer[k].getTimeStamp() == lastTime)); // TB HDR
318 sizeChk += streamSize8; // in stream buffer
319 sizeChk += (lastTime != tmpBuffer[k].getTimeStamp()) && ((sizeChk + (streamSize * encodeBits + 7) / 8) & 1); // time bin alignment
320 sizeChk += (tmpBuffer[k].getTimeStamp() != lastTime || tmpBuffer[k].getRow() != lastRow) ? 3 : 0; // new row overhead
321 sizeChk += (lastTime != -1 && tmpBuffer[k].getTimeStamp() > lastTime) ? ((tmpBuffer[k].getTimeStamp() - lastTime - 1) * 2) : 0; // empty time bins
322 sizeChk += 2; // sequence metadata
323 const uint32_t streamSizeChkBits = streamSize * encodeBits + ((lastTime != tmpBuffer[k].getTimeStamp() && (streamSize * encodeBits) % 8) ? (8 - (streamSize * encodeBits) % 8) : 0);
324 if (sizeChk + (encodeBits + streamSizeChkBits + 7) / 8 > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
325 lastEndpoint = -1;
326 } else if (sizeChk + (seqLen * encodeBits + streamSizeChkBits + 7) / 8 > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
327 seqLen = ((TPCZSHDR::TPC_ZS_PAGE_SIZE - sizeChk) * 8 - streamSizeChkBits) / encodeBits;
328 }
329 // sizeChk += (seqLen * encodeBits + streamSizeChkBits + 7) / 8;
330 // printf("Endpoint %d (%d), Pos %d, Chk %d, Len %d, rows %d, StreamSize %d %d, time %d (%d), row %d (%d), pad %d\n", endpoint, lastEndpoint, (int32_t) (pagePtr - reinterpret_cast<uint8_t*>(page)), sizeChk, seqLen, nRowsInTB, streamSize8, streamSize, (int32_t)tmpBuffer[k].getTimeStamp(), lastTime, (int32_t)tmpBuffer[k].getRow(), lastRow, tmpBuffer[k].getPad());
331 }
332 return endpoint != lastEndpoint || tmpBuffer[k].getTimeStamp() != lastTime;
333}
334
335bool zsEncoderRow::writeSubPage()
336{
337 if (pagePtr != reinterpret_cast<uint8_t*>(page)) {
338 pagePtr += 2 * nRowsInTB;
339 ZSstreamOut(streamBuffer.data(), streamSize, streamBuffer8.data(), streamSize8, encodeBits);
340 pagePtr = std::copy(streamBuffer8.data(), streamBuffer8.data() + streamSize8, pagePtr);
341 if (pagePtr - reinterpret_cast<uint8_t*>(page) > 8192) {
342 throw std::runtime_error("internal error during ZS encoding");
343 }
344 streamSize8 = 0;
345 for (int32_t l = 1; l < nRowsInTB; l++) {
346 curTBHdr->rowAddr1()[l - 1] += 2 * nRowsInTB;
347 }
348 }
349 return endpoint != lastEndpoint;
350}
351
352uint32_t zsEncoderRow::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
353{
354 if (tmpBuffer[k].getTimeStamp() != lastTime) {
355 if (lastTime != -1) {
356 hdr->nTimeBinSpan += tmpBuffer[k].getTimeStamp() - lastTime - 1;
357 pagePtr += (tmpBuffer[k].getTimeStamp() - lastTime - 1) * 2;
358 }
359 hdr->nTimeBinSpan++;
360 if ((pagePtr - reinterpret_cast<uint8_t*>(page)) & 1) {
361 pagePtr++;
362 }
363 curTBHdr = reinterpret_cast<TPCZSTBHDR*>(pagePtr);
364 curTBHdr->rowMask |= (endpoint & 1) << 15;
365 nRowsInTB = 0;
366 lastRow = GPUCA_ROW_COUNT;
367 }
368 if (tmpBuffer[k].getRow() != lastRow) {
369 curTBHdr->rowMask |= 1 << (tmpBuffer[k].getRow() - endpointStart);
370 ZSstreamOut(streamBuffer.data(), streamSize, streamBuffer8.data(), streamSize8, encodeBits);
371 if (nRowsInTB) {
372 curTBHdr->rowAddr1()[nRowsInTB - 1] = (pagePtr - reinterpret_cast<uint8_t*>(page)) + streamSize8;
373 }
374 nRowsInTB++;
375 nSeq = streamBuffer8.data() + streamSize8++;
376 *nSeq = 0;
377 }
378 (*nSeq)++;
379 streamBuffer8[streamSize8++] = tmpBuffer[k].getPad();
380 streamBuffer8[streamSize8++] = streamSize + seqLen;
381 for (int32_t l = 0; l < seqLen; l++) {
382 streamBuffer[streamSize++] = (uint16_t)(tmpBuffer[k + l].getChargeFloat() * encodeBitsFactor + 0.5f);
383 }
384 return seqLen;
385}
386
387void zsEncoderRow::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
388{
389 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
390 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
391 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
392 return;
393 }
394 decPagePtr += sizeof(o2::header::RAWDataHeader);
395 const TPCZSHDR* decHDR = reinterpret_cast<const TPCZSHDR*>(decPagePtr);
396 decPagePtr += sizeof(*decHDR);
397 if (decHDR->version != 1 && decHDR->version != 2) {
398 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " (1 or 2 expected)"s);
399 }
400 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
401 uint32_t mask = (1 << encodeBits) - 1;
402 int32_t cruid = decHDR->cruID;
403 uint32_t sector = cruid / 10;
404 if (sector != iSector) {
405 throw std::runtime_error("invalid TPC sector");
406 }
407 int32_t region = cruid % 10;
408 if ((uint32_t)region != decEndpoint / 2) {
409 throw std::runtime_error("CRU ID / endpoint mismatch");
410 }
411 int32_t nRowsRegion = param->tpcGeometry.GetRegionRows(region);
412
413 int32_t timeBin = (decHDR->timeOffset + (uint64_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN;
414 for (int32_t l = 0; l < decHDR->nTimeBinSpan; l++) {
415 if ((decPagePtr - reinterpret_cast<const uint8_t*>(decPage)) & 1) {
416 decPagePtr++;
417 }
418 const TPCZSTBHDR* tbHdr = reinterpret_cast<const TPCZSTBHDR*>(decPagePtr);
419 bool upperRows = tbHdr->rowMask & 0x8000;
420 if (tbHdr->rowMask != 0 && ((upperRows) ^ ((decEndpoint & 1) != 0))) {
421 throw std::runtime_error("invalid endpoint");
422 }
423 const int32_t rowOffset = param->tpcGeometry.GetRegionStart(region) + (upperRows ? (nRowsRegion / 2) : 0);
424 const int32_t nRows = upperRows ? (nRowsRegion - nRowsRegion / 2) : (nRowsRegion / 2);
425 const int32_t nRowsUsed = __builtin_popcount((uint32_t)(tbHdr->rowMask & 0x7FFF));
426 decPagePtr += nRowsUsed ? (2 * nRowsUsed) : 2;
427 int32_t rowPos = 0;
428 for (int32_t m = 0; m < nRows; m++) {
429 if ((tbHdr->rowMask & (1 << m)) == 0) {
430 continue;
431 }
432 const uint8_t* rowData = rowPos == 0 ? decPagePtr : (reinterpret_cast<const uint8_t*>(decPage) + tbHdr->rowAddr1()[rowPos - 1]);
433 const int32_t nSeqRead = *rowData;
434 const uint8_t* adcData = rowData + 2 * nSeqRead + 1;
435 int32_t nADC = (rowData[2 * nSeqRead] * encodeBits + 7) / 8;
436 decPagePtr += 1 + 2 * nSeqRead + nADC;
437 uint32_t byte = 0, bits = 0, posXbits = 0;
438 std::array<uint16_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> decBuffer;
439 for (int32_t n = 0; n < nADC; n++) {
440 byte |= *(adcData++) << bits;
441 bits += 8;
442 while (bits >= encodeBits) {
443 decBuffer[posXbits++] = byte & mask;
444 byte = byte >> encodeBits;
445 bits -= encodeBits;
446 }
447 }
448 posXbits = 0;
449 for (int32_t n = 0; n < nSeqRead; n++) {
450 const int32_t decSeqLen = rowData[(n + 1) * 2] - (n ? rowData[n * 2] : 0);
451 for (int32_t o = 0; o < decSeqLen; o++) {
452 outputBuffer.emplace_back(o2::tpc::Digit{cruid, decBuffer[posXbits++] * decodeBitsFactor, (tpccf::Row)(rowOffset + m), (tpccf::Pad)(rowData[n * 2 + 1] + o), timeBin + l});
453 }
454 }
455 rowPos++;
456 }
457 }
458}
459
460// ------------------------------------------------- TPC ZS Link Based ZS -------------------------------------------------
461
462#ifdef GPUCA_O2_LIB
463struct zsEncoderLinkBased : public zsEncoder {
464 TPCZSHDRV2* hdr = nullptr;
465 TPCZSHDRV2 hdrBuffer;
466 int32_t inverseChannelMapping[5][32];
467 int32_t nSamples = 0;
468 int32_t link = 0;
469 bool finishPage = false;
470 std::vector<uint16_t> adcValues = {};
471 std::bitset<80> bitmask = {};
472
473 void createBitmask(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
474 void init();
475 bool sort(const o2::tpc::Digit a, const o2::tpc::Digit b);
476};
477
478void zsEncoderLinkBased::init()
479{
480 encodeBits = TPCZSHDRV2::TPC_ZS_NBITS_V34;
481 for (int32_t i = 0; i < 5; i++) {
482 for (int32_t j = 0; j < 32; j++) {
483 inverseChannelMapping[i][j] = -1;
484 }
485 }
486 for (int32_t iCRU = 0; iCRU < 2; iCRU++) {
487 for (int32_t iChannel = 0; iChannel < 80; iChannel++) {
488 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
489 Mapper::getSampaAndChannelOnFEC(iCRU, iChannel, sampaOnFEC, channelOnSAMPA);
490 if (inverseChannelMapping[sampaOnFEC][channelOnSAMPA] != -1 && inverseChannelMapping[sampaOnFEC][channelOnSAMPA] != iChannel) {
491 GPUError("ERROR: Channel conflict: %d %d: %d vs %d", sampaOnFEC, channelOnSAMPA, inverseChannelMapping[sampaOnFEC][channelOnSAMPA], iChannel);
492 throw std::runtime_error("ZS error");
493 }
494 inverseChannelMapping[sampaOnFEC][channelOnSAMPA] = iChannel;
495 }
496 }
497 for (int32_t i = 0; i < 5; i++) {
498 for (int32_t j = 0; j < 32; j++) {
499 if (inverseChannelMapping[i][j] == -1) {
500 GPUError("ERROR: Map missing for sampa %d channel %d", i, j);
501 throw std::runtime_error("ZS error");
502 }
503 }
504 }
505}
506
507void zsEncoderLinkBased::createBitmask(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
508{
509 const auto& mapper = Mapper::instance();
510 nSamples = 0;
511 adcValues.clear();
512 bitmask.reset();
513 uint32_t l;
514 for (l = k; l < tmpBuffer.size(); l++) {
515 const auto& a = tmpBuffer[l];
516 int32_t cruinsector = param->tpcGeometry.GetRegion(a.getRow());
517 o2::tpc::GlobalPadNumber pad = mapper.globalPadNumber(o2::tpc::PadPos(a.getRow(), a.getPad()));
518 o2::tpc::FECInfo fec = mapper.fecInfo(pad);
519 o2::tpc::CRU cru = cruinsector;
520 int32_t fecInPartition = fec.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
521 int32_t tmpEndpoint = 2 * cruinsector + (fecInPartition >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
522 if (l == k) {
523 link = fecInPartition;
524 endpoint = tmpEndpoint;
525 } else if (endpoint != tmpEndpoint || link != fecInPartition || tmpBuffer[l].getTimeStamp() != tmpBuffer[k].getTimeStamp()) {
526 break;
527 }
528 int32_t channel = inverseChannelMapping[fec.getSampaChip()][fec.getSampaChannel()];
529 bitmask[channel] = 1;
530 adcValues.emplace_back((uint16_t)(a.getChargeFloat() * encodeBitsFactor + 0.5f));
531 }
532 nSamples = l - k;
533}
534
535bool zsEncoderLinkBased::sort(const o2::tpc::Digit a, const o2::tpc::Digit b)
536{
537 // Fixme: this is blasphemy... one shoult precompute all values and sort an index array
538 int32_t cruinsectora = param->tpcGeometry.GetRegion(a.getRow());
539 int32_t cruinsectorb = param->tpcGeometry.GetRegion(b.getRow());
540 if (cruinsectora != cruinsectorb) {
541 return cruinsectora < cruinsectorb;
542 }
543 const auto& mapper = Mapper::instance();
544 o2::tpc::GlobalPadNumber pada = mapper.globalPadNumber(o2::tpc::PadPos(a.getRow(), a.getPad()));
545 o2::tpc::GlobalPadNumber padb = mapper.globalPadNumber(o2::tpc::PadPos(b.getRow(), b.getPad()));
546 o2::tpc::FECInfo feca = mapper.fecInfo(pada);
547 o2::tpc::FECInfo fecb = mapper.fecInfo(padb);
548 o2::tpc::CRU cru = cruinsectora;
549 int32_t fecInPartitiona = feca.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
550 int32_t fecInPartitionb = fecb.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
551
552 int32_t endpointa = 2 * cruinsectora + (fecInPartitiona >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
553 int32_t endpointb = 2 * cruinsectorb + (fecInPartitionb >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
554 if (endpointa != endpointb) {
555 return endpointa < endpointb;
556 }
557 if (a.getTimeStamp() != b.getTimeStamp()) {
558 return a.getTimeStamp() < b.getTimeStamp();
559 }
560 if (fecInPartitiona != fecInPartitionb) {
561 return fecInPartitiona < fecInPartitionb;
562 }
563 return inverseChannelMapping[feca.getSampaChip()][feca.getSampaChannel()] < inverseChannelMapping[fecb.getSampaChip()][fecb.getSampaChannel()];
564}
565
566// ------------------------------------------------- TPC Improved Link Based ZS -------------------------------------------------
567
568struct zsEncoderImprovedLinkBased : public zsEncoderLinkBased {
569 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
570 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
571 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
572 bool writeSubPage();
573 void initPage();
574
575 constexpr static int32_t RAWLNK = rdh_utils::ILBZSLinkID;
576};
577
578bool zsEncoderImprovedLinkBased::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
579{
580 createBitmask(tmpBuffer, k);
581 finishPage = endpoint != lastEndpoint;
582 if (firstTimebinInPage != -1 && tmpBuffer[k].getTimeStamp() - firstTimebinInPage >= 1 << (sizeof(hdr->nTimeBinSpan) * 8)) {
583 finishPage = true;
584 }
585 if (!finishPage) {
586 uint32_t sizeChk = (uint32_t)(pagePtr - reinterpret_cast<uint8_t*>(page));
589 sizeChk += (nSamples * TPCZSHDRV2::TPC_ZS_NBITS_V34 + 127) / 128 * 16;
590 } else {
591 sizeChk += (nSamples + 2 * TPCZSHDRV2::SAMPLESPER64BIT - 1) / (2 * TPCZSHDRV2::SAMPLESPER64BIT) * 16;
592 }
593 if (sizeChk > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
594 finishPage = true;
595 }
596 }
597 return finishPage;
598}
599
600uint32_t zsEncoderImprovedLinkBased::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
601{
603 pagePtr += sizeof(*tbHdr);
604 tbHdr->bunchCrossing = (tmpBuffer[k].getTimeStamp() - firstTimebinInPage) * LHCBCPERTIMEBIN;
606 tbHdr->bitMaskHigh = (bitmask >> 64).to_ulong();
607 tbHdr->bitMaskLow = (bitmask & std::bitset<80>(0xFFFFFFFFFFFFFFFFlu)).to_ulong();
608 tbHdr->syncOffsetBC = 0;
609 tbHdr->fecInPartition = link;
610 hdr->nTimeBinSpan = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
611 hdr->nTimebinHeaders++;
613 tbHdr->numWordsPayload = (nSamples * TPCZSHDRV2::TPC_ZS_NBITS_V34 + 127) / 128; // tightly packed ADC samples
614 uint32_t tmp = 0;
615 uint32_t tmpIn = nSamples;
616 ZSstreamOut(adcValues.data(), tmpIn, pagePtr, tmp, encodeBits);
617 } else {
618 tbHdr->numWordsPayload = (nSamples + 2 * TPCZSHDRV2::SAMPLESPER64BIT - 1) / (2 * TPCZSHDRV2::SAMPLESPER64BIT);
619 uint64_t* payloadPtr = (uint64_t*)pagePtr;
620 for (uint32_t i = 0; i < 2 * tbHdr->numWordsPayload; i++) {
621 payloadPtr[i] = 0;
622 }
623 for (uint32_t i = 0; i < nSamples; i++) {
624 payloadPtr[i / TPCZSHDRV2::SAMPLESPER64BIT] |= ((uint64_t)adcValues[i]) << ((i % TPCZSHDRV2::SAMPLESPER64BIT) * TPCZSHDRV2::TPC_ZS_NBITS_V34);
625 }
626 }
627 pagePtr += tbHdr->numWordsPayload * 16;
628 return nSamples;
629}
630
631bool zsEncoderImprovedLinkBased::writeSubPage()
632{
633 return finishPage;
634}
635
636void zsEncoderImprovedLinkBased::initPage()
637{
639 hdr->nTimebinHeaders = 0;
640 hdr->firstZSDataOffset = 0;
641}
642
643void zsEncoderImprovedLinkBased::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
644{
645 const auto& mapper = Mapper::instance();
646 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
647 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
648 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
649 return;
650 }
651 decPagePtr += sizeof(o2::header::RAWDataHeader);
652 const TPCZSHDRV2* decHDR = reinterpret_cast<const TPCZSHDRV2*>(decPagePtr);
653 decPagePtr += sizeof(*decHDR);
654 if (decHDR->version != ZSVersion::ZSVersionLinkBasedWithMeta) {
655 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " ("s + std::to_string(ZSVersion::ZSVersionLinkBasedWithMeta) + " expected)"s);
656 }
658 throw std::runtime_error("Magic word missing");
659 }
660 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
661 uint32_t mask = (1 << encodeBits) - 1;
662 int32_t cruid = decHDR->cruID;
663 uint32_t sector = cruid / 10;
664 if (sector != iSector) {
665 throw std::runtime_error("invalid TPC sector");
666 }
667 int32_t region = cruid % 10;
668 decPagePtr += decHDR->firstZSDataOffset * 16;
669 for (uint32_t i = 0; i < decHDR->nTimebinHeaders; i++) {
671#if 0 // Decoding using the function for the original linkZS
672 o2::tpc::CRU cru = cruid % 10;
673 const int32_t feeLink = tbHdr->fecInPartition - (decEndpoint & 1) * ((mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
674 auto fillADC = [&outputBuffer](int32_t cru, int32_t rowInSector, int32_t padInRow, int32_t timeBin, float adcValue) {
675 outputBuffer.emplace_back(o2::tpc::Digit{cruid, adcValue, rowInSector, padInRow, timeBin});
676 return true;
677 };
678 size_t size = sizeof(*tbHdr) + tbHdr->numWordsPayload * 16;
679 raw_processing_helpersa::processZSdata((const char*)decPagePtr, size, rdh_utils::getFEEID(cruid, decEndpoint & 1, feeLink), o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, decHDR->timeOffset, fillADC);
680#else // Decoding directly
681 if (!tbHdr->isLinkZS()) {
682 throw std::runtime_error("ZS TB Hdr does not have linkZS magic word");
683 }
684 int32_t timeBin = (int32_t(decHDR->timeOffset) + int32_t(tbHdr->bunchCrossing) + (int32_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - triggerBC) / LHCBCPERTIMEBIN;
685 if (timeBin < 0) {
686 LOGP(debug, "zsEncoderImprovedLinkBased::decodePage skipping digits hdr->tOff {} + hdr->bc {} + (orbit {} - firstOrbit {}) * maxBunch {} - triggerBC {} = {} < 0", decHDR->timeOffset, tbHdr->bunchCrossing, o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, o2::constants::lhc::LHCMaxBunches, triggerBC, timeBin);
687 continue;
688 }
689 const uint8_t* adcData = (const uint8_t*)(decPagePtr + sizeof(*tbHdr));
690 const auto& bitmask = tbHdr->getChannelBits();
691 int32_t nADC = bitmask.count();
692 std::vector<uint16_t> decBuffer(nADC);
694 uint32_t byte = 0, bits = 0, posXbits = 0;
695 while (posXbits < nADC) {
696 byte |= *(adcData++) << bits;
697 bits += 8;
698 while (bits >= encodeBits) {
699 decBuffer[posXbits++] = byte & mask;
700 byte = byte >> encodeBits;
701 bits -= encodeBits;
702 }
703 }
704 } else {
705 const uint64_t* adcData64 = (const uint64_t*)adcData;
706 for (int32_t j = 0; j < nADC; j++) {
708 }
709 }
710 for (int32_t j = 0, k = 0; j < bitmask.size(); j++) {
711 if (bitmask[j]) {
712 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
713 mapper.getSampaAndChannelOnFEC(cruid, j, sampaOnFEC, channelOnSAMPA);
714 const auto padSecPos = mapper.padSecPos(cruid, tbHdr->fecInPartition, sampaOnFEC, channelOnSAMPA);
715 const auto& padPos = padSecPos.getPadPos();
716 outputBuffer.emplace_back(o2::tpc::Digit{cruid, decBuffer[k++] * decodeBitsFactor, (tpccf::Row)padPos.getRow(), (tpccf::Pad)padPos.getPad(), timeBin});
717 }
718 }
719#endif
720 decPagePtr += sizeof(*tbHdr) + tbHdr->numWordsPayload * 16;
721 }
722}
723
724// ------------------------------------------------- TPC ZS Dense Link Based ZS -------------------------------------------------
725
726struct zsEncoderDenseLinkBased : public zsEncoderLinkBased {
727 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
728 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
729 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
730 bool writeSubPage();
731 void initPage();
732 void amendPageErrorMessage(std::ostringstream& oss, const o2::header::RAWDataHeader* rdh, const TPCZSHDRV2* decHDR, const uint8_t* payloadEnd, const uint8_t* decPagePtr, uint32_t nOutput);
733
734 uint16_t curTimeBin = 0;
735 std::vector<uint8_t> sequenceBuffer;
736 std::vector<uint16_t> sequenceBufferADC;
737
738 constexpr static int32_t RAWLNK = rdh_utils::DLBZSLinkID;
739 constexpr static int32_t v2nbits = 10;
740};
741
742bool zsEncoderDenseLinkBased::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
743{
744 createBitmask(tmpBuffer, k);
745 finishPage = endpoint != lastEndpoint;
746 uint16_t newTimeBin = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
747 bool retVall = finishPage || newTimeBin != curTimeBin;
748 return retVall;
749}
750
751uint32_t zsEncoderDenseLinkBased::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
752{
753 if (sequenceBuffer.size() == 0) {
754 uint16_t bc = (int64_t)tmpBuffer[k].getTimeStamp() * LHCBCPERTIMEBIN - (int64_t)hbf * o2::constants::lhc::LHCMaxBunches;
755 if (zsVersion == ZSVersion::ZSVersionDenseLinkBasedV2) {
756 bc &= 0xFFC;
757 }
758 sequenceBuffer.emplace_back(bc << 4);
759 sequenceBuffer.emplace_back(bc >> 4);
760 curTimeBin = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
761 hdr->nTimeBinSpan = curTimeBin & 0xFF;
762 if (curTimeBin & 0x100) {
763 hdr->flags |= TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8;
764 }
765 hdr->nTimebinHeaders++;
766 }
767 sequenceBuffer[0]++;
768
769 sequenceBuffer.emplace_back(link);
770 uint8_t* plink = &sequenceBuffer.back();
771
772 std::bitset<10> bitmaskL2;
773 for (int32_t i = 9; i >= 0; i--) {
774 bitmaskL2.set(i, ((bitmask >> (i * 8)) & std::bitset<80>(0xFF)).any());
775 }
776 if (bitmaskL2.all()) {
777 *plink |= 0b00100000;
778 } else {
779 *plink |= (bitmaskL2.to_ulong() >> 2) & 0b11000000;
780 sequenceBuffer.emplace_back(bitmaskL2.to_ulong() & 0xFF);
781 }
782
783 for (int32_t i = 0; i < 10; i++) {
784 if (bitmaskL2.test(i)) {
785 sequenceBuffer.emplace_back(((bitmask >> (i * 8)) & std::bitset<80>(0xFF)).to_ulong());
786 }
787 }
788
789 static_assert(TPCZSHDRV2::TPC_ZS_NBITS_V34 == 12);
790 if (nSamples) {
791 sequenceBufferADC.insert(sequenceBufferADC.end(), adcValues.begin(), adcValues.end());
792 }
793
794 return nSamples;
795}
796
797bool zsEncoderDenseLinkBased::writeSubPage()
798{
799 uint32_t offset = sequenceBuffer.size();
800 if (sequenceBufferADC.size()) {
801 bool need12bit = zsVersion != ZSVersion::ZSVersionDenseLinkBasedV2;
802 uint32_t needNow = 0;
803 if (zsVersion == ZSVersion::ZSVersionDenseLinkBasedV2) {
804 for (uint32_t i = 0; i < sequenceBufferADC.size(); i++) {
805 if (sequenceBufferADC[i] >= (1 << v2nbits)) {
806 need12bit = true;
807 break;
808 }
809 }
810 }
811 uint32_t encodeBitsBlock = encodeBits;
812 if (!need12bit) {
813 encodeBitsBlock = v2nbits;
814 sequenceBuffer[0] |= 0x10;
815 }
816 sequenceBuffer.resize(offset + (sequenceBufferADC.size() * encodeBitsBlock + 7) / 8);
817 uint32_t tmp = 0;
818 uint32_t tmpIn = sequenceBufferADC.size();
819 ZSstreamOut(sequenceBufferADC.data(), tmpIn, sequenceBuffer.data() + offset, tmp, encodeBitsBlock);
820 sequenceBufferADC.clear();
821 }
822
823 if (sequenceBuffer.size()) {
824 uint32_t sizeLeft = TPCZSHDR::TPC_ZS_PAGE_SIZE - (pagePtr - (uint8_t*)page) - sizeof(TPCZSHDRV2) - (hdr->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0);
825 uint32_t size = sequenceBuffer.size();
826 uint32_t fill = std::min(sizeLeft, size);
827 memcpy(pagePtr, sequenceBuffer.data(), fill);
828 pagePtr += fill;
829 if (size != fill) {
831 sequenceBuffer.erase(sequenceBuffer.begin(), sequenceBuffer.begin() + fill);
832 } else {
833 sequenceBuffer.clear();
834 }
835 finishPage = finishPage || size >= sizeLeft || needAnotherPage;
836 }
837
838 return finishPage;
839}
840
841void zsEncoderDenseLinkBased::initPage()
842{
844 hdr->nTimebinHeaders = 0;
845 memcpy(pagePtr, sequenceBuffer.data(), sequenceBuffer.size());
846 hdr->firstZSDataOffset = sequenceBuffer.size() + sizeof(o2::header::RAWDataHeader);
847 pagePtr += sequenceBuffer.size();
848 sequenceBuffer.clear();
849 hdr->flags = 0;
850}
851
852void zsEncoderDenseLinkBased::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
853{
854 const auto& mapper = Mapper::instance();
855 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
856 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
857 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
858 return;
859 }
860 const TPCZSHDRV2* decHDR = reinterpret_cast<const TPCZSHDRV2*>(decPagePtr + o2::raw::RDHUtils::getMemorySize(*rdh) - sizeof(TPCZSHDRV2));
861 decPagePtr += sizeof(o2::header::RAWDataHeader);
862 if (decHDR->version < ZSVersion::ZSVersionDenseLinkBased || decHDR->version > ZSVersion::ZSVersionDenseLinkBasedV2) {
863 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " ("s + std::to_string(ZSVersion::ZSVersionDenseLinkBased) + " - "s + std::to_string(ZSVersion::ZSVersionDenseLinkBasedV2) + " expected)"s);
864 }
866 throw std::runtime_error("Magic word missing");
867 }
868 const uint8_t* payloadEnd = ((const uint8_t*)decPage) + o2::raw::RDHUtils::getMemorySize(*rdh) - sizeof(TPCZSHDRV2) - ((decHDR->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0);
869 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
870 int32_t cruid = decHDR->cruID;
871 uint32_t sector = cruid / 10;
872 if (sector != iSector) {
873 throw std::runtime_error("invalid TPC sector");
874 }
875 int32_t region = cruid % 10;
876 decPagePtr += decHDR->firstZSDataOffset - sizeof(o2::header::RAWDataHeader);
877 std::vector<uint8_t> tmpBuffer;
878 bool extendFailure = false;
879 uint32_t nOutput = 0;
880 uint32_t minTimeBin = -1, maxTimeBin = 0;
881 for (uint32_t i = 0; i < decHDR->nTimebinHeaders; i++) {
882 int32_t sizeLeftInPage = payloadEnd - decPagePtr;
883 if (sizeLeftInPage <= 0) {
884 throw std::runtime_error("Decoding ran beyond end of page before processing extended timebin");
885 }
887 if (o2::raw::RDHUtils::getMemorySize(*rdh) != TPCZSHDR::TPC_ZS_PAGE_SIZE) {
888 throw std::runtime_error("pageExtends signaled, but current page is not full");
889 }
890
891 const uint8_t* pageNext = ((const uint8_t*)decPage) + TPCZSHDR::TPC_ZS_PAGE_SIZE;
892 const o2::header::RAWDataHeader* rdhNext = (const o2::header::RAWDataHeader*)pageNext;
893
894 if ((uint16_t)(o2::raw::RDHUtils::getPageCounter(*rdh) + 1) != o2::raw::RDHUtils::getPageCounter(*rdhNext)) {
895 GPUError("Incomplete HBF: Payload extended to next page, but next page missing in stream (packet counters %d %d)", (int32_t)o2::raw::RDHUtils::getPageCounter(*rdh), (int32_t)o2::raw::RDHUtils::getPageCounter(*rdhNext));
896 extendFailure = true;
897 decPagePtr = payloadEnd; // Next 8kb page is missing in stream, cannot decode remaining data, skip it
898 break;
899 }
900
901 const TPCZSHDRV2* hdrNext = reinterpret_cast<const TPCZSHDRV2*>(pageNext + o2::raw::RDHUtils::getMemorySize(*rdhNext) - sizeof(TPCZSHDRV2));
902 tmpBuffer.resize(sizeLeftInPage + hdrNext->firstZSDataOffset - sizeof(o2::header::RAWDataHeader));
903 memcpy(tmpBuffer.data(), decPagePtr, sizeLeftInPage);
904 memcpy(tmpBuffer.data() + sizeLeftInPage, pageNext + sizeof(o2::header::RAWDataHeader), hdrNext->firstZSDataOffset - sizeof(o2::header::RAWDataHeader));
905 decPagePtr = tmpBuffer.data();
906 payloadEnd = decPagePtr + tmpBuffer.size();
907 }
908 uint8_t linkCount = *((const uint8_t*)decPagePtr) & 0x0F;
909 uint16_t linkBC = (*((const uint16_t*)decPagePtr) & 0xFFF0) >> 4;
910 bool v2Flag = decHDR->version == ZSVersion::ZSVersionDenseLinkBasedV2 && *((const uint8_t*)decPagePtr) & 0x10;
911 if (decHDR->version == ZSVersion::ZSVersionDenseLinkBasedV2) {
912 linkBC &= 0xFFC;
913 }
914 decPagePtr += sizeof(uint16_t);
915 std::vector<int32_t> links;
916 std::vector<std::bitset<80>> bitmasks;
917 uint32_t nTotalSamples = 0;
918 for (uint32_t l = 0; l < linkCount; l++) {
919 uint8_t decLinkX = *((const uint8_t*)decPagePtr);
920 decPagePtr += sizeof(uint8_t);
921 uint8_t decLink = decLinkX & 0b00011111;
922 std::bitset<10> bitmaskL2;
923 if (decLinkX & 0b00100000) {
924 bitmaskL2.set();
925 } else {
926 bitmaskL2 = std::bitset<10>(((((uint16_t)decLinkX) & 0b11000000) << 2) | (uint16_t) * ((const uint8_t*)decPagePtr));
927 decPagePtr += sizeof(uint8_t);
928 }
929
930 std::bitset<80> bitmask(0);
931 for (int32_t i = 0; i < 10; i++) {
932 if (bitmaskL2.test(i)) {
933 bitmask |= std::bitset<80>(*((const uint8_t*)decPagePtr)) << i * 8;
934 decPagePtr += sizeof(uint8_t);
935 }
936 }
937 links.emplace_back(decLink);
938 bitmasks.emplace_back(bitmask);
939 nTotalSamples += bitmask.count();
940 }
941
942 const uint8_t* adcData = (const uint8_t*)decPagePtr;
943 int32_t encodeBitsBlock = v2Flag ? v2nbits : encodeBits;
944 decPagePtr += (nTotalSamples * encodeBitsBlock + 7) / 8;
945
946 // time bin might be smaller 0 due to triggerBC
947 int32_t timeBin = (int32_t(linkBC) + (int32_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - int32_t(triggerBC)) / LHCBCPERTIMEBIN;
948 if (timeBin < 0 || nTotalSamples == 0) {
949 if (timeBin < 0 && nTotalSamples > 0) {
950 LOGP(debug, "zsEncoderDenseLinkBased::decodePage skipping digits (linkBC {} + orbit {} - firstOrbit {}) * maxBunch {} - triggerBC {} = {} < 0, nTotalSamples {}", linkBC, o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, o2::constants::lhc::LHCMaxBunches, triggerBC, timeBin, nTotalSamples);
951 }
952 continue;
953 }
954 if (timeBin > maxTimeBin) {
955 maxTimeBin = timeBin;
956 }
957 if (timeBin < minTimeBin) {
958 minTimeBin = timeBin;
959 }
960
961 std::vector<uint16_t> samples(nTotalSamples);
962 uint32_t mask = (1 << encodeBitsBlock) - 1;
963 uint32_t byte = 0, bits = 0, posXbits = 0;
964 while (posXbits < nTotalSamples) {
965 byte |= *(adcData++) << bits;
966 bits += 8;
967 while (bits >= encodeBitsBlock && posXbits < nTotalSamples) {
968 samples[posXbits++] = byte & mask;
969 byte = byte >> encodeBitsBlock;
970 bits -= encodeBitsBlock;
971 }
972 }
973 uint32_t samplePos = 0;
974
975 for (uint32_t l = 0; l < linkCount; l++) {
976 uint8_t decLink = links[l];
977 const auto& bitmask = bitmasks[l];
978 int32_t nADC = bitmask.count();
979
980 for (int32_t j = 0; j < bitmask.size(); j++) {
981 if (bitmask[j]) {
982 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
983 mapper.getSampaAndChannelOnFEC(cruid, j, sampaOnFEC, channelOnSAMPA);
984 const auto padSecPos = mapper.padSecPos(cruid, decLink, sampaOnFEC, channelOnSAMPA);
985 const auto& padPos = padSecPos.getPadPos();
986 outputBuffer.emplace_back(o2::tpc::Digit{cruid, samples[samplePos++] * decodeBitsFactor, (tpccf::Row)padPos.getRow(), (tpccf::Pad)padPos.getPad(), timeBin});
987 nOutput++;
988 }
989 }
990 }
991 }
992
993 int32_t hdrMinTimeBin = (int32_t(decHDR->timeOffset) + int32_t(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - triggerBC);
994 if (triggerBC > 0 && hdrMinTimeBin < 0) {
995 hdrMinTimeBin = 0;
996 }
997 hdrMinTimeBin /= LHCBCPERTIMEBIN;
998 int32_t hdrMaxTimeBin = hdrMinTimeBin + decHDR->nTimeBinSpan + ((decHDR->flags & TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8) ? 256 : 0);
999
1000 if (!extendFailure && nOutput != decHDR->nADCsamples) {
1001 std::ostringstream oss;
1002 oss << "Number of decoded digits " << nOutput << " does not match value from MetaInfo " << decHDR->nADCsamples;
1003 amendPageErrorMessage(oss, rdh, decHDR, nullptr, nullptr, nOutput);
1004 throw std::runtime_error(oss.str());
1005 }
1006
1007 if (decHDR->nADCsamples && (minTimeBin < hdrMinTimeBin || maxTimeBin > hdrMaxTimeBin)) {
1008 std::ostringstream oss;
1009 oss << "Incorrect time bin range in MetaInfo, header reports " << hdrMinTimeBin << " - " << hdrMaxTimeBin << "(timeOffset: " << decHDR->timeOffset << " + (orbit: " << o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) << " - firstOrbit " << firstOrbit << ") * LHCMaxBunches - triggerBC: " << triggerBC << ", decoded data is " << minTimeBin << " - " << maxTimeBin;
1010 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
1011 throw std::runtime_error(oss.str());
1012 }
1013
1014 if (decHDR->nTimebinHeaders && payloadEnd - decPagePtr < 0) {
1015 std::ostringstream oss;
1016 oss << "Decoding ran over end of page";
1017 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
1018 throw std::runtime_error(oss.str());
1019 }
1020 if (decHDR->nTimebinHeaders && payloadEnd - decPagePtr >= 2 * o2::raw::RDHUtils::GBTWord128) {
1021 std::ostringstream oss;
1022 oss << "Decoding didn't reach end of page";
1023 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
1024 throw std::runtime_error(oss.str());
1025 }
1026}
1027
1028void zsEncoderDenseLinkBased::amendPageErrorMessage(std::ostringstream& oss, const o2::header::RAWDataHeader* rdh, const TPCZSHDRV2* decHDR, const uint8_t* payloadEnd, const uint8_t* decPagePtr, uint32_t nOutput)
1029{
1030 if (payloadEnd && decPagePtr) {
1031 oss << " (payloadEnd " << (void*)payloadEnd << " - decPagePtr " << (void*)decPagePtr << " - " << (payloadEnd - decPagePtr) << " bytes left, " << nOutput << " of " << decHDR->nADCsamples << " digits decoded)\n";
1032 } else {
1033 oss << "\n";
1034 }
1035 constexpr size_t bufferSize = 3 * std::max(sizeof(*rdh), sizeof(*decHDR)) + 1;
1036 char dumpBuffer[bufferSize];
1037 for (size_t i = 0; i < sizeof(*rdh); i++) {
1038 snprintf(dumpBuffer + 3 * i, 4, "%02X ", (int32_t)((uint8_t*)rdh)[i]);
1039 }
1040 oss << "RDH of page: " << dumpBuffer << "\n";
1041 for (size_t i = 0; i < sizeof(*decHDR); i++) {
1042 snprintf(dumpBuffer + 3 * i, 4, "%02X ", (int32_t)((uint8_t*)decHDR)[i]);
1043 }
1044 oss << "Meta header of page: " << dumpBuffer << "\n";
1045}
1046
1047#endif // GPUCA_O2_LIB
1048
1049// ------------------------------------------------- TPC ZS Main Encoder -------------------------------------------------
1050
1051template <class T>
1052struct zsEncoderRun : public T {
1053 uint32_t run(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer, size_t* totalSize = nullptr);
1054 size_t compare(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer);
1055
1056 using T::bcShiftInFirstHBF;
1057 using T::checkInput;
1058 using T::curRegion;
1059 using T::decodePage;
1060 using T::encodeBits;
1061 using T::encodeBitsFactor;
1062 using T::encodeSequence;
1063 using T::endpoint;
1064 using T::firstTimebinInPage;
1065 using T::getHbf;
1066 using T::hbf;
1067 using T::hdr;
1068 using T::init;
1069 using T::initPage;
1070 using T::ir;
1071 using T::iSector;
1072 using T::lastEndpoint;
1073 using T::lastRow;
1074 using T::lastTime;
1075 using T::needAnotherPage;
1076 using T::nexthbf;
1077 using T::outputEndpoint;
1078 using T::outputRegion;
1079 using T::packetCounter;
1080 using T::padding;
1081 using T::page;
1082 using T::pageCounter;
1083 using T::pagePtr;
1084 using T::param;
1085 using T::raw;
1086 using T::sort;
1087 using T::writeSubPage;
1088 using T::ZSfillEmpty;
1089 using T::zsVersion;
1090};
1091
1092template <class T>
1093inline uint32_t zsEncoderRun<T>::run(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer, size_t* totalSize)
1094{
1095 uint32_t totalPages = 0;
1096 zsPage singleBuffer;
1097#ifdef GPUCA_O2_LIB
1098 int32_t maxhbf = 0;
1099 int32_t minhbf = o2::constants::lhc::LHCMaxBunches;
1100#endif
1101 bcShiftInFirstHBF = ir ? ir->bc : 0;
1102 int32_t orbitShift = ir ? ir->orbit : 0;
1103 int32_t rawcru = 0;
1104 int32_t rawendpoint = 0;
1105 (void)(rawcru + rawendpoint); // avoid compiler warning
1106 encodeBitsFactor = (1 << (encodeBits - 10));
1107
1108 std::sort(tmpBuffer.begin(), tmpBuffer.end(), [this](const o2::tpc::Digit a, const o2::tpc::Digit b) { return sort(a, b); });
1109 for (uint32_t k = 0; k <= tmpBuffer.size();) {
1110 bool mustWritePage = false, mustWriteSubPage = false;
1111 if (needAnotherPage) {
1112 needAnotherPage = false;
1113 mustWritePage = true;
1114 } else {
1115 if (k < tmpBuffer.size()) {
1116 if (tmpBuffer[k].getTimeStamp() != lastTime) {
1117 nexthbf = getHbf(tmpBuffer[k].getTimeStamp());
1118 if (nexthbf < 0) {
1119 throw std::runtime_error("Received digit before the defined first orbit");
1120 }
1121 if (hbf != nexthbf) {
1122 lastEndpoint = -2;
1123 mustWritePage = true;
1124 }
1125 }
1126 if (lastRow != tmpBuffer[k].getRow()) {
1127 curRegion = param->tpcGeometry.GetRegion(tmpBuffer[k].getRow());
1128 }
1129 mustWriteSubPage = checkInput(tmpBuffer, k);
1130 } else {
1131 nexthbf = -1;
1132 mustWritePage = true;
1133 }
1134 }
1135 if (mustWritePage || mustWriteSubPage) {
1136 mustWritePage |= writeSubPage();
1137
1138 if (page && mustWritePage) {
1139 if constexpr (std::is_same_v<T, struct zsEncoderDenseLinkBased>) {
1140 if ((pagePtr - (uint8_t*)page) % o2::raw::RDHUtils::GBTWord128) {
1142 }
1143 uint8_t* triggerWord = nullptr;
1144 if (hbf != nexthbf || endpoint != lastEndpoint) {
1145 if ((pagePtr - (uint8_t*)page) + sizeof(TPCZSHDRV2) + o2::tpc::TPCZSHDRV2::TRIGGER_WORD_SIZE <= TPCZSHDR::TPC_ZS_PAGE_SIZE) {
1146 if ((pagePtr - (uint8_t*)page) % (2 * o2::raw::RDHUtils::GBTWord128)) {
1147 pagePtr += o2::raw::RDHUtils::GBTWord128; // align to 256 bit, size constrained cannot be affected by this
1148 }
1150 } else {
1151 needAnotherPage = true;
1152 }
1153 if (this->sequenceBuffer.size()) {
1154 needAnotherPage = true;
1155 }
1156 }
1157 if (hdr->flags & o2::tpc::TPCZSHDRV2::TriggerWordPresent) {
1158 triggerWord = pagePtr;
1160 }
1161 if ((pagePtr - (uint8_t*)page) % (2 * o2::raw::RDHUtils::GBTWord128) == 0) {
1162 pagePtr += o2::raw::RDHUtils::GBTWord128; // align to 128bit mod 256
1163 }
1164 TPCZSHDRV2* pagehdr = (TPCZSHDRV2*)pagePtr;
1165 pagePtr += sizeof(TPCZSHDRV2);
1166 if (pagePtr - (uint8_t*)page > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
1167 throw std::runtime_error("TPC ZS page overflow");
1168 }
1169 memcpy(pagehdr, hdr, sizeof(*hdr));
1170 if (triggerWord) {
1171 memset(triggerWord, 0, o2::tpc::TPCZSHDRV2::TRIGGER_WORD_SIZE);
1172 }
1173 }
1174 const rdh_utils::FEEIDType rawfeeid = rdh_utils::getFEEID(rawcru, rawendpoint, this->RAWLNK);
1175 if (totalSize) {
1176 *totalSize += !std::is_same_v<T, struct zsEncoderDenseLinkBased> && (lastEndpoint == -1 || hbf == nexthbf) ? TPCZSHDR::TPC_ZS_PAGE_SIZE : (pagePtr - (uint8_t*)page);
1177 }
1178 size_t size = !std::is_same_v<T, struct zsEncoderDenseLinkBased> && (padding || lastEndpoint == -1 || hbf == nexthbf) ? TPCZSHDR::TPC_ZS_PAGE_SIZE : (pagePtr - (uint8_t*)page);
1179 size = CAMath::nextMultipleOf<o2::raw::RDHUtils::GBTWord128>(size);
1180#ifdef GPUCA_O2_LIB
1181 if (raw) {
1182 raw->addData(rawfeeid, rawcru, 0, rawendpoint, *ir + hbf * o2::constants::lhc::LHCMaxBunches, gsl::span<char>((char*)page + sizeof(o2::header::RAWDataHeader), (char*)page + size), true, 0, 2);
1183 maxhbf = std::max<int32_t>(maxhbf, hbf);
1184 minhbf = std::min<int32_t>(minhbf, hbf);
1185 } else
1186#endif
1187 {
1189 o2::raw::RDHUtils::setHeartBeatOrbit(*rdh, hbf + orbitShift);
1190 o2::raw::RDHUtils::setHeartBeatBC(*rdh, bcShiftInFirstHBF);
1192 o2::raw::RDHUtils::setVersion(*rdh, o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>());
1193 o2::raw::RDHUtils::setFEEID(*rdh, rawfeeid);
1195 o2::raw::RDHUtils::setLinkID(*rdh, this->RAWLNK);
1196 o2::raw::RDHUtils::setPacketCounter(*rdh, packetCounter++);
1197 o2::raw::RDHUtils::setPageCounter(*rdh, pageCounter++);
1198 }
1199 }
1200 if (k >= tmpBuffer.size() && !needAnotherPage) {
1201 break;
1202 }
1203 }
1204 if (mustWritePage) {
1205 if (!needAnotherPage) {
1206 if (hbf != nexthbf) {
1207 pageCounter = 0;
1208 }
1209 outputRegion = curRegion;
1210 outputEndpoint = endpoint;
1211 hbf = nexthbf;
1212 lastTime = -1;
1213 lastEndpoint = endpoint;
1214 }
1215 if (raw) {
1216 page = &singleBuffer;
1217 } else {
1218 if (buffer[outputEndpoint].size() == 0 && nexthbf > orbitShift) {
1219 buffer[outputEndpoint].emplace_back();
1220 ZSfillEmpty(&buffer[outputEndpoint].back(), bcShiftInFirstHBF, rdh_utils::getFEEID(iSector * 10 + outputEndpoint / 2, outputEndpoint & 1, this->RAWLNK), orbitShift, this->RAWLNK); // Emplace empty page with RDH containing beginning of TF
1221 if (totalSize) {
1222 *totalSize += sizeof(o2::header::RAWDataHeader);
1223 }
1224 totalPages++;
1225 }
1226 buffer[outputEndpoint].emplace_back();
1227 page = &buffer[outputEndpoint].back();
1228 }
1229 pagePtr = reinterpret_cast<uint8_t*>(page);
1230 std::fill(page->begin(), page->end(), 0);
1231 pagePtr += sizeof(o2::header::RAWDataHeader);
1232 if constexpr (std::is_same_v<T, struct zsEncoderDenseLinkBased>) {
1233 hdr = &this->hdrBuffer;
1234 } else {
1235 hdr = reinterpret_cast<decltype(hdr)>(pagePtr);
1236 pagePtr += sizeof(*hdr);
1237 }
1238 hdr->version = zsVersion;
1239 hdr->cruID = iSector * 10 + outputRegion;
1240 hdr->nTimeBinSpan = 0;
1241 hdr->nADCsamples = 0;
1242 rawcru = iSector * 10 + outputRegion;
1243 rawendpoint = outputEndpoint & 1;
1244 hdr->timeOffset = (int64_t)(needAnotherPage ? firstTimebinInPage : tmpBuffer[k].getTimeStamp()) * LHCBCPERTIMEBIN - (int64_t)hbf * o2::constants::lhc::LHCMaxBunches;
1245 firstTimebinInPage = tmpBuffer[k].getTimeStamp();
1246 initPage();
1247 totalPages++;
1248 }
1249 if (needAnotherPage) {
1250 continue;
1251 }
1252 uint32_t nEncoded = encodeSequence(tmpBuffer, k);
1253 lastTime = tmpBuffer[k].getTimeStamp();
1254 lastRow = tmpBuffer[k].getRow();
1255 hdr->nADCsamples += nEncoded;
1256 k += nEncoded;
1257 }
1258 if (raw) {
1259#ifdef GPUCA_O2_LIB
1260 if (iSector == 0) {
1261 for (int32_t i = minhbf; i <= maxhbf; i++) {
1262 raw->addData(46208, 360, rdh_utils::SACLinkID, 0, *ir + i * o2::constants::lhc::LHCMaxBunches, gsl::span<char>((char*)&singleBuffer, (char*)&singleBuffer), true, 0, 4);
1263 }
1264 }
1265#endif
1266 } else {
1267 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1268 if (buffer[j].size() == 0) {
1269 buffer[j].emplace_back();
1270 ZSfillEmpty(&buffer[j].back(), bcShiftInFirstHBF, rdh_utils::getFEEID(iSector * 10 + j / 2, j & 1, this->RAWLNK), orbitShift, this->RAWLNK);
1271 totalPages++;
1272 }
1273 }
1274 }
1275 return totalPages;
1276}
1277
1278template <class T>
1279size_t zsEncoderRun<T>::compare(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer)
1280{
1281 size_t nErrors = 0;
1282 std::vector<o2::tpc::Digit> compareBuffer;
1283 compareBuffer.reserve(tmpBuffer.size());
1284 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1285 uint32_t firstOrbit = ir ? ir->orbit : 0;
1286 for (uint32_t k = 0; k < buffer[j].size(); k++) {
1287 zsPage* decPage = &buffer[j][k];
1288 decodePage(compareBuffer, decPage, j, firstOrbit);
1289 }
1290 }
1291 if (tmpBuffer.size() != compareBuffer.size()) {
1292 nErrors += tmpBuffer.size();
1293 printf("Number of clusters mismatch %d %d\n", (int32_t)tmpBuffer.size(), (int32_t)compareBuffer.size());
1294 } else {
1295 for (uint32_t j = 0; j < tmpBuffer.size(); j++) {
1296 const float decodeBitsFactor = (1 << (encodeBits - 10));
1297 const float c = CAMath::Round(tmpBuffer[j].getChargeFloat() * decodeBitsFactor) / decodeBitsFactor;
1298 int32_t ok = c == compareBuffer[j].getChargeFloat() && (int32_t)tmpBuffer[j].getTimeStamp() == (int32_t)compareBuffer[j].getTimeStamp() && (int32_t)tmpBuffer[j].getPad() == (int32_t)compareBuffer[j].getPad() && (int32_t)tmpBuffer[j].getRow() == (int32_t)compareBuffer[j].getRow();
1299 if (ok) {
1300 continue;
1301 }
1302 nErrors++;
1303 printf("%4u: OK %d: Charge %3d %3d Time %4d %4d Pad %3d %3d Row %3d %3d\n", j, ok,
1304 (int32_t)c, (int32_t)compareBuffer[j].getChargeFloat(), (int32_t)tmpBuffer[j].getTimeStamp(), (int32_t)compareBuffer[j].getTimeStamp(), (int32_t)tmpBuffer[j].getPad(), (int32_t)compareBuffer[j].getPad(), (int32_t)tmpBuffer[j].getRow(), (int32_t)compareBuffer[j].getRow());
1305 }
1306 }
1307 return nErrors;
1308}
1309
1310} // anonymous namespace
1311} // namespace o2::gpu
1312#endif // GPUCA_TPC_GEOMETRY_O2
1313
1314template <class S>
1315void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<uint64_t[]>* outBuffer, uint32_t* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, const GPUParam& param, int32_t version, bool verify, float threshold, bool padding, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter)
1316{
1317 // Pass in either outBuffer / outSizes, to fill standalone output buffers, or raw to use RawFileWriter
1318 // ir is the interaction record for time bin 0
1319 if (((outBuffer == nullptr) ^ (outSizes == nullptr)) || ((raw != nullptr) && (ir == nullptr)) || !((outBuffer == nullptr) ^ (raw == nullptr)) || (raw && verify)) {
1320 throw std::runtime_error("Invalid parameters");
1321 }
1322#ifdef GPUCA_TPC_GEOMETRY_O2
1323 std::vector<zsPage> buffer[NSECTORS][GPUTrackingInOutZS::NENDPOINTS];
1324 struct tmpReductionResult {
1325 uint32_t totalPages = 0;
1326 size_t totalSize = 0;
1327 size_t nErrors = 0;
1328 size_t digitsInput = 0;
1329 size_t digitsEncoded = 0;
1330 };
1331 auto reduced = tbb::parallel_reduce(tbb::blocked_range<uint32_t>(0, NSECTORS), tmpReductionResult(), [&](const auto range, auto red) {
1332 for (uint32_t i = range.begin(); i < range.end(); i++) {
1333 std::vector<o2::tpc::Digit> tmpBuffer;
1334 red.digitsInput += ZSEncoderGetNDigits(in, i);
1335 tmpBuffer.resize(ZSEncoderGetNDigits(in, i));
1336 if (threshold > 0.f && !digitsFilter) {
1337 auto it = std::copy_if(ZSEncoderGetDigits(in, i), ZSEncoderGetDigits(in, i) + ZSEncoderGetNDigits(in, i), tmpBuffer.begin(), [threshold](auto& v) { return v.getChargeFloat() >= threshold; });
1338 tmpBuffer.resize(std::distance(tmpBuffer.begin(), it));
1339 } else {
1340 std::copy(ZSEncoderGetDigits(in, i), ZSEncoderGetDigits(in, i) + ZSEncoderGetNDigits(in, i), tmpBuffer.begin());
1341 }
1342
1343 if (digitsFilter) {
1344 digitsFilter(tmpBuffer);
1345 if (threshold > 0.f) {
1346 std::vector<o2::tpc::Digit> tmpBuffer2 = std::move(tmpBuffer);
1347 tmpBuffer = std::vector<o2::tpc::Digit>(tmpBuffer2.size());
1348 auto it = std::copy_if(tmpBuffer2.begin(), tmpBuffer2.end(), tmpBuffer.begin(), [threshold](auto& v) { return v.getChargeFloat() >= threshold; });
1349 tmpBuffer.resize(std::distance(tmpBuffer.begin(), it));
1350 }
1351 }
1352 red.digitsEncoded += tmpBuffer.size();
1353
1354 auto runZS = [&](auto& encoder) {
1355 encoder.zsVersion = version;
1356 encoder.init();
1357 red.totalPages += encoder.run(buffer[i], tmpBuffer, &red.totalSize);
1358 if (verify) {
1359 red.nErrors += encoder.compare(buffer[i], tmpBuffer); // Verification
1360 }
1361 };
1362
1363 if (version >= ZSVersion::ZSVersionRowBased10BitADC && version <= ZSVersion::ZSVersionRowBased12BitADC) {
1364 zsEncoderRun<zsEncoderRow> enc{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}};
1365 runZS(enc);
1366 } else if (version >= ZSVersion::ZSVersionLinkBasedWithMeta && version <= ZSVersion::ZSVersionDenseLinkBasedV2) {
1367#ifdef GPUCA_O2_LIB
1368 if (version == ZSVersion::ZSVersionLinkBasedWithMeta) {
1369 zsEncoderRun<zsEncoderImprovedLinkBased> enc{{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}}};
1370 runZS(enc);
1371 } else if (version >= ZSVersion::ZSVersionDenseLinkBased && version <= ZSVersion::ZSVersionDenseLinkBasedV2) {
1372 zsEncoderRun<zsEncoderDenseLinkBased> enc{{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}}};
1373 runZS(enc);
1374 }
1375#else
1376 throw std::runtime_error("Link based ZS encoding not supported in standalone build");
1377#endif
1378 } else {
1379 throw std::runtime_error("Invalid ZS version "s + std::to_string(version) + ", cannot decode"s);
1380 }
1381 }
1382 return red; }, [&](const auto& red1, const auto& red2) {
1383 auto red = red1;
1384 red.totalPages += red2.totalPages;
1385 red.totalSize += red2.totalSize;
1386 red.nErrors += red2.nErrors;
1387 red.digitsInput += red2.digitsInput;
1388 red.digitsEncoded += red2.digitsEncoded;
1389 return red; });
1390
1391 if (outBuffer) {
1392 outBuffer->reset(new uint64_t[reduced.totalPages * TPCZSHDR::TPC_ZS_PAGE_SIZE / sizeof(uint64_t)]);
1393 uint64_t offset = 0;
1394 for (uint32_t i = 0; i < NSECTORS; i++) {
1395 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1396 memcpy((char*)outBuffer->get() + offset, buffer[i][j].data(), buffer[i][j].size() * TPCZSHDR::TPC_ZS_PAGE_SIZE);
1398 outSizes[i * GPUTrackingInOutZS::NENDPOINTS + j] = buffer[i][j].size();
1399 }
1400 }
1401 }
1402 if (reduced.nErrors) {
1403 GPUError("ERROR: %lu INCORRECT SAMPLES DURING ZS ENCODING VERIFICATION!!!", reduced.nErrors);
1404 } else if (verify) {
1405 GPUInfo("ENCODING VERIFICATION PASSED");
1406 }
1407 GPUInfo("TOTAL ENCODED SIZE: %lu (%lu of %lu digits encoded)", reduced.totalSize, reduced.digitsEncoded, reduced.digitsInput);
1408#endif
1409}
1410
1411template void GPUReconstructionConvert::RunZSEncoder<GPUTrackingInOutDigits>(const GPUTrackingInOutDigits&, std::unique_ptr<uint64_t[]>*, uint32_t*, o2::raw::RawFileWriter*, const o2::InteractionRecord*, const GPUParam&, int32_t, bool, float, bool, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter);
1412#ifdef GPUCA_O2_LIB
1413template void GPUReconstructionConvert::RunZSEncoder<DigitArray>(const DigitArray&, std::unique_ptr<uint64_t[]>*, uint32_t*, o2::raw::RawFileWriter*, const o2::InteractionRecord*, const GPUParam&, int32_t, bool, float, bool, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter);
1414#endif
1415
1416void GPUReconstructionConvert::RunZSEncoderCreateMeta(const uint64_t* buffer, const uint32_t* sizes, void** ptrs, GPUTrackingInOutZS* out)
1417{
1418 uint64_t offset = 0;
1419 for (uint32_t i = 0; i < NSECTORS; i++) {
1420 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1421 ptrs[i * GPUTrackingInOutZS::NENDPOINTS + j] = (char*)buffer + offset;
1423 out->sector[i].zsPtr[j] = &ptrs[i * GPUTrackingInOutZS::NENDPOINTS + j];
1425 out->sector[i].count[j] = 1;
1426 }
1427 }
1428}
1429
1430void GPUReconstructionConvert::RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit, float threshold)
1431{
1432 for (uint32_t i = 0; i < NSECTORS; i++) {
1433 if (buffers[i].get() != ptrs[i] || nsb != ns) {
1434 throw std::runtime_error("Not owning digits");
1435 }
1436 uint32_t j = 0;
1437 const uint32_t decodeBits = zs12bit ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1;
1438 const float decodeBitsFactor = (1 << (decodeBits - 10));
1439 for (uint32_t k = 0; k < ns[i]; k++) {
1440 if (buffers[i][k].getChargeFloat() >= threshold) {
1441 if (k > j) {
1442 buffers[i][j] = buffers[i][k];
1443 }
1444 if (zs12bit) {
1445 buffers[i][j].setCharge(CAMath::Round(buffers[i][j].getChargeFloat() * decodeBitsFactor) / decodeBitsFactor);
1446 } else {
1447 buffers[i][j].setCharge(CAMath::Round(buffers[i][j].getChargeFloat()));
1448 }
1449 j++;
1450 }
1451 }
1452 nsb[i] = j;
1453 }
1454}
1455
1456#ifdef GPUCA_O2_LIB
1457namespace o2::gpu::internal
1458{
1459template <class T>
1460static inline auto GetDecoder_internal(const GPUParam* param, int32_t version)
1461{
1462 std::shared_ptr<T> enc = std::make_shared<T>();
1463 if (param == nullptr) {
1464 static GPUParam dummyParam;
1465 param = &dummyParam;
1466 }
1467 enc->param = param;
1468 enc->zsVersion = version;
1469 enc->init();
1470 return [enc](std::vector<o2::tpc::Digit>& outBuffer, const void* page, uint32_t firstTfOrbit, uint32_t triggerBC = 0) {
1471 const o2::header::RAWDataHeader& rdh = *(const o2::header::RAWDataHeader*)page;
1472 if (o2::raw::RDHUtils::getMemorySize(rdh) == sizeof(o2::header::RAWDataHeader)) {
1473 return;
1474 }
1475 if (o2::raw::RDHUtils::getDetectorField(rdh) != 2) {
1476 return;
1477 }
1478 o2::tpc::CRU cru(o2::tpc::rdh_utils::getCRU(rdh));
1479 enc->iSector = cru.sector();
1480 int32_t endpoint = cru.region() * 2 + o2::tpc::rdh_utils::getEndPoint(rdh);
1481 enc->decodePage(outBuffer, (const zsPage*)page, endpoint, firstTfOrbit, triggerBC);
1482 };
1483}
1484} // namespace o2::gpu::internal
1485
1486std::function<void(std::vector<o2::tpc::Digit>&, const void*, uint32_t, uint32_t)> GPUReconstructionConvert::GetDecoder(int32_t version, const GPUParam* param)
1487{
1489 return o2::gpu::internal::GetDecoder_internal<zsEncoderRow>(param, version);
1491 return o2::gpu::internal::GetDecoder_internal<zsEncoderImprovedLinkBased>(param, version);
1493 return o2::gpu::internal::GetDecoder_internal<zsEncoderDenseLinkBased>(param, version);
1494 } else {
1495 throw std::runtime_error("Invalid ZS version "s + std::to_string(version) + ", cannot create decoder"s);
1496 }
1497}
1498#endif
uint16_t padding
Definition of the TPC Digit.
uint64_t orbit
Definition RawEventData.h:6
uint64_t bc
Definition RawEventData.h:5
int32_t i
int32_t retVal
bool o
#define GPUCA_ROW_COUNT
Header to collect LHC related constants.
uint32_t j
Definition RawData.h:0
uint8_t endpoint
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
uint32_t version
Definition RawData.h:8
Utility class to write detectors data to (multiple) raw data file(s) respecting CRU format.
Definition of TPCFastTransform class.
TBranch * ptr
std::ostringstream debug
definitions to deal with the link based zero suppression format
Definitions of TPC Zero Suppression Data Headers.
int nClusters
static void RunZSEncoder(const S &in, std::unique_ptr< uint64_t[]> *outBuffer, uint32_t *outSizes, o2::raw::RawFileWriter *raw, const o2::InteractionRecord *ir, const GPUParam &param, int32_t version, bool verify, float threshold=0.f, bool padding=false, std::function< void(std::vector< o2::tpc::Digit > &)> digitsFilter=nullptr)
static void ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess *native, std::unique_ptr< GPUTPCClusterData[]> *clusters, uint32_t *nClusters, const TPCFastTransform *transform, int32_t continuousMaxTimeBin=0)
static void RunZSFilter(std::unique_ptr< o2::tpc::Digit[]> *buffers, const o2::tpc::Digit *const *ptrs, size_t *nsb, const size_t *ns, const GPUParam &param, bool zs12bit, float threshold)
static std::function< void(std::vector< o2::tpc::Digit > &, const void *, uint32_t, uint32_t)> GetDecoder(int32_t version, const GPUParam *param)
static int32_t GetMaxTimeBin(const o2::tpc::ClusterNativeAccess &native)
static void RunZSEncoderCreateMeta(const uint64_t *buffer, const uint32_t *sizes, void **ptrs, GPUTrackingInOutZS *out)
static void ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess &native, std::unique_ptr< o2::tpc::ClusterNative[]> &nativeBuffer, const AliHLTTPCRawCluster **rawClusters, uint32_t *nRawClusters)
int32_t float float float & x
void addData(uint16_t feeid, uint16_t cru, uint8_t lnk, uint8_t endpoint, const IR &ir, const gsl::span< char > data, bool preformatted=false, uint32_t trigger=0, uint32_t detField=0)
unsigned char partition() const
Definition CRU.h:63
unsigned char getSampaChannel() const
Definition FECInfo.h:45
unsigned char getIndex() const
Definition FECInfo.h:41
unsigned char getSampaChip() const
Definition FECInfo.h:44
static Mapper & instance(const std::string mappingDir="")
Definition Mapper.h:44
static constexpr void getSampaAndChannelOnFEC(const int cruID, const size_t rawFECChannel, int &sampaOnFEC, int &channelOnSAMPA)
Definition Mapper.h:275
static constexpr int MAXSECTOR
Definition Sector.h:44
std::array< gsl::span< const o2::tpc::Digit >, Sector::MAXSECTOR > DigitArray
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition glcorearb.h:2595
GLsizei samples
Definition glcorearb.h:1309
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLint y
Definition glcorearb.h:270
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
Definition glcorearb.h:4150
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLenum GLfloat param
Definition glcorearb.h:271
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
const GLuint * buffers
Definition glcorearb.h:656
uint8_t itsSharedClusterMap uint8_t
constexpr int LHCMaxBunches
const GBTLinkAttributes links[kNGBTLinks]
Definition RawWriter.h:54
RAWDataHeaderV7 RAWDataHeader
void dumpBuffer(gsl::span< const std::byte > buffer, std::ostream &out=std::cout, size_t maxbytes=std::numeric_limits< size_t >::max())
Definition DumpBuffer.h:139
constexpr int LHCBCPERTIMEBIN
Definition Constants.h:38
uint64_t getTimeStamp(o2::framework::ProcessingContext &pc)
uint16_t FEEIDType
Definition RDHUtils.h:26
Global TPC definitions and constants.
Definition SimTraits.h:167
@ ZSVersionDenseLinkBased
@ ZSVersionLinkBasedWithMeta
@ ZSVersionDenseLinkBasedV2
@ ZSVersionRowBased10BitADC
@ ZSVersionRowBased12BitADC
unsigned short GlobalPadNumber
global pad number
Definition Defs.h:129
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
int32_t GetCharge() const
uint16_t GetFlags() const
int32_t GetPadRow() const
uint32_t orbit
LHC orbit.
uint16_t bc
bunch crossing ID of interaction
const o2::tpc::Digit * tpcDigits[NSECTORS]
GPUTrackingInOutZSSector sector[NSECTORS]
static constexpr uint32_t NENDPOINTS
static void setLinkID(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:256
static void setDetectorField(H &rdh, uint32_t v, NOTPTR(H))
Definition RDHUtils.h:582
static constexpr int GBTWord128
Definition RDHUtils.h:52
static void setPageCounter(H &rdh, uint16_t v, NOTPTR(H))
Definition RDHUtils.h:557
static void setMemorySize(H &rdh, uint16_t v, NOTPTR(H))
Definition RDHUtils.h:240
static void setVersion(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:88
static void setPacketCounter(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:272
static void setHeartBeatOrbit(RDHv4 &rdh, uint32_t v)
Definition RDHUtils.h:362
static void setFEEID(RDHv4 &rdh, uint16_t v)
Definition RDHUtils.h:146
unsigned int nClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
const ClusterNative * clusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
unsigned int clusterOffset[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
const ClusterNative * clustersLinear
unsigned short nTimebinHeaders
unsigned char magicWord
unsigned short firstZSDataOffset
static constexpr bool TIGHTLY_PACKED_V3
static constexpr unsigned int TPC_ZS_NBITS_V34
static constexpr unsigned int SAMPLESPER64BIT
static constexpr unsigned int TRIGGER_WORD_SIZE
unsigned char nTimeBinSpan
unsigned char version
unsigned short timeOffset
unsigned short cruID
static constexpr unsigned int TPC_ZS_NBITS_V1
static constexpr unsigned int TPC_ZS_NBITS_V2
static constexpr size_t TPC_ZS_PAGE_SIZE
unsigned short nADCsamples
unsigned short rowMask
void compare(std::string_view s1, std::string_view s2)
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< Digit > digits
std::vector< int > row