Project
Loading...
Searching...
No Matches
GPUTPCCFDecodeZS.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#include "GPUTPCCFDecodeZS.h"
16#include "GPUCommonMath.h"
17#include "GPUTPCClusterFinder.h"
18#include "CfArray2D.h"
19#include "PackedCharge.h"
20#include "CfUtils.h"
22#include "GPUCommonAlgorithm.h"
23#include "TPCPadGainCalib.h"
24#include "TPCZSLinkMapping.h"
25#include "GPUTPCGeometry.h"
27
28using namespace o2::gpu;
29using namespace o2::gpu::tpccf;
30using namespace o2::tpc;
31using namespace o2::tpc::constants;
32
33// ===========================================================================
34// ===========================================================================
35// Decode ZS Row
36// ===========================================================================
37// ===========================================================================
38
39template <>
40GPUdii() void GPUTPCCFDecodeZS::Thread<GPUTPCCFDecodeZS::decodeZS>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut)
41{
42 GPUTPCCFDecodeZS::decode(clusterer, smem, nBlocks, nThreads, iBlock, iThread, firstHBF, tpcTimeBinCut);
43}
44
45GPUdii() void GPUTPCCFDecodeZS::decode(GPUTPCClusterFinder& clusterer, GPUSharedMemory& s, int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t firstHBF, int32_t tpcTimeBinCut)
46{
47 const uint32_t sector = clusterer.mISector;
48#ifdef GPUCA_GPUCODE
49 const uint32_t endpoint = clusterer.mPzsOffsets[iBlock].endpoint;
50#else
51 const bool deviceLayout = clusterer.IsDeviceProcessor(); // Host-compiled kernel running with threading as on a device
52 const uint32_t endpoint = deviceLayout ? clusterer.mPzsOffsets[iBlock].endpoint : iBlock;
53#endif
54 const GPUTrackingInOutZS::GPUTrackingInOutZSSector& zs = clusterer.GetConstantMem()->ioPtrs.tpcZS->sector[sector];
55 if (zs.count[endpoint] == 0) {
56 return;
57 }
58 CfChargePos* positions = clusterer.mPpositions;
59 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
60 const size_t nDigits = clusterer.mPzsOffsets[iBlock].offset;
61 if (iThread == 0) {
62 const int32_t region = endpoint / 2;
63 s.nRowsRegion = GPUTPCGeometry::GetRegionRows(region);
64 s.regionStartRow = GPUTPCGeometry::GetRegionStart(region);
65 s.nThreadsPerRow = CAMath::Max(1u, nThreads / ((s.nRowsRegion + (endpoint & 1)) / 2));
66 s.rowStride = nThreads / s.nThreadsPerRow;
67 s.rowOffsetCounter = 0;
68 }
69 GPUbarrier();
70 const uint32_t myRow = iThread / s.nThreadsPerRow;
71 const uint32_t mySequence = iThread % s.nThreadsPerRow;
72#ifdef GPUCA_GPUCODE
73 const uint32_t i = 0;
74 const uint32_t j = clusterer.mPzsOffsets[iBlock].num;
75 {
76 {
77#else
78 const uint32_t iFirst = deviceLayout ? 0 : clusterer.mMinMaxCN[endpoint].zsPtrFirst;
79 const uint32_t iLast = deviceLayout ? 1 : clusterer.mMinMaxCN[endpoint].zsPtrLast;
80 for (uint32_t i = iFirst; i < iLast; i++) {
81 const uint32_t minJ = deviceLayout ? clusterer.mPzsOffsets[iBlock].num : ((i == iFirst) ? clusterer.mMinMaxCN[endpoint].zsPageFirst : 0);
82 const uint32_t maxJ = deviceLayout ? (minJ + 1) : ((i + 1 == iLast) ? clusterer.mMinMaxCN[endpoint].zsPageLast : zs.nZSPtr[endpoint][i]);
83 for (uint32_t j = minJ; j < maxJ; j++) {
84#endif
85 const uint32_t* pageSrc = (const uint32_t*)(((const uint8_t*)zs.zsPtr[endpoint][i]) + j * TPCZSHDR::TPC_ZS_PAGE_SIZE);
86 GPUCA_SHARED_CACHE_REF(&s.ZSPage[0], pageSrc, TPCZSHDR::TPC_ZS_PAGE_SIZE, uint32_t, pageCache);
87 GPUbarrier();
88 const uint8_t* page = (const uint8_t*)pageCache;
90 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
91#ifdef GPUCA_GPUCODE
92 return;
93#else
94 continue;
95#endif
96 }
98 const TPCZSHDR* hdr = reinterpret_cast<const TPCZSHDR*>(pagePtr);
99 pagePtr += sizeof(*hdr);
100 const bool decode12bit = hdr->version == 2;
101 const uint32_t decodeBits = decode12bit ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1;
102 const float decodeBitsFactor = 1.f / (1 << (decodeBits - 10));
103 uint32_t mask = (1 << decodeBits) - 1;
104 int32_t timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN;
105 const int32_t rowOffset = s.regionStartRow + ((endpoint & 1) ? (s.nRowsRegion / 2) : 0);
106 const int32_t nRows = (endpoint & 1) ? (s.nRowsRegion - s.nRowsRegion / 2) : (s.nRowsRegion / 2);
107
108 for (int32_t l = 0; l < hdr->nTimeBinSpan; l++) { // TODO: Parallelize over time bins
109 pagePtr += (pagePtr - page) & 1; // Ensure 16 bit alignment
110 const TPCZSTBHDR* tbHdr = reinterpret_cast<const TPCZSTBHDR*>(pagePtr);
111 if ((tbHdr->rowMask & 0x7FFF) == 0) {
112 pagePtr += 2;
113 continue;
114 }
115 const int32_t nRowsUsed = CAMath::Popcount((uint32_t)(tbHdr->rowMask & 0x7FFF));
116 pagePtr += 2 * nRowsUsed;
117
118 GPUbarrier();
119 for (int32_t n = iThread; n < nRowsUsed; n += nThreads) {
120 const uint8_t* rowData = n == 0 ? pagePtr : (page + tbHdr->rowAddr1()[n - 1]);
121 s.RowClusterOffset[n] = CAMath::AtomicAddShared<uint32_t>(&s.rowOffsetCounter, rowData[2 * *rowData]);
122 }
123 /*if (iThread < GPUCA_WARP_SIZE) { // TODO: Seems to miscompile with HIP, CUDA performance doesn't really change, for now sticking to the AtomicAdd
124 GPUSharedMemory& smem = s;
125 int32_t o;
126 if (iThread < nRowsUsed) {
127 const uint8_t* rowData = iThread == 0 ? pagePtr : (page + tbHdr->rowAddr1()[iThread - 1]);
128 o = rowData[2 * *rowData];
129 } else {
130 o = 0;
131 }
132 int32_t x = warp_scan_inclusive_add(o);
133 if (iThread < nRowsUsed) {
134 s.RowClusterOffset[iThread] = s.rowOffsetCounter + x - o;
135 } else if (iThread == GPUCA_WARP_SIZE - 1) {
136 s.rowOffsetCounter += x;
137 }
138 }*/
139 GPUbarrier();
140
141 if (myRow < s.rowStride) {
142 for (int32_t m = myRow; m < nRows; m += s.rowStride) {
143 if ((tbHdr->rowMask & (1 << m)) == 0) {
144 continue;
145 }
146 const int32_t rowPos = CAMath::Popcount((uint32_t)(tbHdr->rowMask & ((1 << m) - 1)));
147 size_t nDigitsTmp = nDigits + s.RowClusterOffset[rowPos];
148 const uint8_t* rowData = rowPos == 0 ? pagePtr : (page + tbHdr->rowAddr1()[rowPos - 1]);
149 const int32_t nSeqRead = *rowData;
150 const int32_t nSeqPerThread = (nSeqRead + s.nThreadsPerRow - 1) / s.nThreadsPerRow;
151 const int32_t mySequenceStart = mySequence * nSeqPerThread;
152 const int32_t mySequenceEnd = CAMath::Min(mySequenceStart + nSeqPerThread, nSeqRead);
153 if (mySequenceEnd > mySequenceStart) {
154 const uint8_t* adcData = rowData + 2 * nSeqRead + 1;
155 const uint32_t nSamplesStart = mySequenceStart ? rowData[2 * mySequenceStart] : 0;
156 nDigitsTmp += nSamplesStart;
157 uint32_t nADCStartBits = nSamplesStart * decodeBits;
158 const uint32_t nADCStart = (nADCStartBits + 7) / 8;
159 const int32_t nADC = (rowData[2 * mySequenceEnd] * decodeBits + 7) / 8;
160 adcData += nADCStart;
161 nADCStartBits &= 0x7;
162 uint32_t byte = 0, bits = 0;
163 if (nADCStartBits) { // % 8 != 0
164 bits = 8 - nADCStartBits;
165 byte = ((*(adcData - 1) & (0xFF ^ ((1 << nADCStartBits) - 1)))) >> nADCStartBits;
166 }
167 int32_t nSeq = mySequenceStart;
168 int32_t seqLen = nSeq ? (rowData[(nSeq + 1) * 2] - rowData[nSeq * 2]) : rowData[2];
169 Pad pad = rowData[nSeq++ * 2 + 1];
170 for (int32_t n = nADCStart; n < nADC; n++) {
171 byte |= *(adcData++) << bits;
172 bits += 8;
173 while (bits >= decodeBits) {
174 if (seqLen == 0) {
175 seqLen = rowData[(nSeq + 1) * 2] - rowData[nSeq * 2];
176 pad = rowData[nSeq++ * 2 + 1];
177 }
178 const CfFragment& frag = clusterer.mPmemory->frag;
179 TPCTime globalTime = timeBin + l;
180 bool discardTimeBin = not frag.contains(globalTime);
181 discardTimeBin |= (tpcTimeBinCut > 0 && globalTime > tpcTimeBinCut);
182
183 Row row = rowOffset + m;
184 CfChargePos pos(row, Pad(pad), discardTimeBin ? INVALID_TIME_BIN : frag.toLocal(globalTime));
185 positions[nDigitsTmp++] = pos;
186
187 if (!discardTimeBin) {
188 float q = float(byte & mask) * decodeBitsFactor;
189 q *= clusterer.GetConstantMem()->calibObjects.tpcPadGain->getGainCorrection(sector, row, pad);
190 chargeMap[pos] = PackedCharge(q);
191 }
192 pad++;
193 byte = byte >> decodeBits;
194 bits -= decodeBits;
195 seqLen--;
196 }
197 }
198 }
199 }
200 }
201 if (nRowsUsed > 1) {
202 pagePtr = page + tbHdr->rowAddr1()[nRowsUsed - 2];
203 }
204 pagePtr += 2 * *pagePtr; // Go to entry for last sequence length
205 pagePtr += 1 + (*pagePtr * decodeBits + 7) / 8; // Go to beginning of next time bin
206 }
207 }
208 }
209}
210
211// ===========================================================================
212// ===========================================================================
213// Decode ZS Link
214// ===========================================================================
215// ===========================================================================
216
217template <>
218GPUdii() void GPUTPCCFDecodeZSLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut)
219{
220 Decode<GPUTPCCFDecodeZSLink>(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF, tpcTimeBinCut);
221}
222
223GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx)
224{
225 const CfFragment& frag = ctx.clusterer.mPmemory->frag;
226
227 const auto* rdHdr = ConsumeHeader<header::RAWDataHeader>(ctx.page);
228
229 if (o2::raw::RDHUtils::getMemorySize(*rdHdr) == sizeof(o2::header::RAWDataHeader)) {
230 return ctx.pageDigitOffset;
231 }
232
233 [[maybe_unused]] int32_t nDecoded = 0;
234 const auto* decHdr = ConsumeHeader<TPCZSHDRV2>(ctx.page);
235 ConsumeBytes(ctx.page, decHdr->firstZSDataOffset * 16);
236
237 assert(decHdr->version == ZSVersionLinkBasedWithMeta);
238 assert(decHdr->magicWord == o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader);
239
240 for (uint32_t t = 0; t < decHdr->nTimebinHeaders; t++) {
241 const auto* tbHdr = ConsumeHeader<zerosupp_link_based::CommonHeader>(ctx.page);
242 const auto* adcData = ConsumeBytes(ctx.page, tbHdr->numWordsPayload * 16); // Page now points to next timebin or past the page
243
244 int32_t timeBin = (decHdr->timeOffset + tbHdr->bunchCrossing + (uint64_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdHdr) - ctx.firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN;
245
246 uint32_t channelMask[3];
247 GetChannelBitmask(*tbHdr, channelMask);
248 uint32_t nAdc = CAMath::Popcount(channelMask[0]) + CAMath::Popcount(channelMask[1]) + CAMath::Popcount(channelMask[2]);
249
250 nDecoded += nAdc;
251
252 bool discardTimeBin = not frag.contains(timeBin);
253 discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut);
254
255 if (discardTimeBin) {
256 FillWithInvalid(ctx.clusterer, ctx.iThread, ctx.nThreads, ctx.pageDigitOffset, nAdc);
257 } else {
258 DecodeTB(
259 smem,
260 ctx,
261 adcData,
262 nAdc,
263 channelMask,
264 timeBin,
265 decHdr->cruID,
266 tbHdr->fecInPartition);
267 }
268
269 ctx.pageDigitOffset += nAdc;
270 } // for (uint32_t t = 0; t < decHdr->nTimebinHeaders; t++)
271
272#ifdef GPUCA_CHECK_TPCZS_CORRUPTION
273 if (iThread == 0 && nDecoded != decHdr->nADCsamples) {
274 clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_NADC, clusterer.mISector * 1000 + decHdr->cruID, decHdr->nADCsamples, nDecoded);
275 /*#ifndef GPUCA_GPUCODE
276 FILE* foo = fopen("dump.bin", "w+b");
277 fwrite(pageSrc, 1, o2::raw::RDHUtils::getMemorySize(*rdHdr), foo);
278 fclose(foo);
279 #endif*/
280 }
281#endif
282
283 return ctx.pageDigitOffset;
284}
285
286GPUd() void GPUTPCCFDecodeZSLink::DecodeTB(
287 GPUSharedMemory& smem,
288 DecodeCtx& ctx,
289 const uint8_t* adcData,
290 uint32_t nAdc,
291 const uint32_t* channelMask,
292 int32_t timeBin,
293 int32_t cru,
294 int32_t fecInPartition)
295{
296 constexpr int32_t NTHREADS = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFDecodeZSLink);
297 static_assert(NTHREADS == GPUCA_WARP_SIZE, "Decoding TB Headers in parallel assumes block size is a single warp.");
298
299 uint8_t blockOffset = 0;
300 for (uint8_t i = ctx.iThread; blockOffset < nAdc; i += NTHREADS) {
301
302 uint8_t rawFECChannel = i;
303
304 uint8_t myChannelActive = ChannelIsActive(channelMask, rawFECChannel);
305
306 uint8_t myOffset = warp_scan_inclusive_add(myChannelActive) - 1 + blockOffset;
307 blockOffset = warp_broadcast(myOffset, NTHREADS - 1) + 1;
308
309 if (not myChannelActive) {
310 continue;
311 }
312 assert(myOffset < nAdc);
313
314 uint32_t adc = 0;
315
316 if constexpr (TPCZSHDRV2::TIGHTLY_PACKED_V3) {
317
318 uint32_t adcBitOffset = myOffset * DECODE_BITS;
319 uint32_t adcByteOffset = adcBitOffset / CHAR_BIT;
320 uint32_t adcOffsetInByte = adcBitOffset - adcByteOffset * CHAR_BIT;
321
322 uint32_t byte = 0, bits = 0;
323
324 while (bits < DECODE_BITS) {
325 byte |= ((uint32_t)adcData[adcByteOffset]) << bits;
326 adcByteOffset++;
327 bits += CHAR_BIT;
328 }
329 adc = byte >> adcOffsetInByte;
330
331 } else { // ! TPCZSHDRV2::TIGHTLY_PACKED_V3
332 const uint64_t* adcData64 = (const uint64_t*)adcData;
333 adc = (adcData64[myOffset / TPCZSHDRV2::SAMPLESPER64BIT] >> ((myOffset % TPCZSHDRV2::SAMPLESPER64BIT) * DECODE_BITS)) & DECODE_MASK;
334 }
335
336 o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannel, fecInPartition);
337 const CfFragment& frag = ctx.clusterer.mPmemory->frag;
338 float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR);
339 WriteCharge(ctx.clusterer, charge, padAndRow, frag.toLocal(timeBin), ctx.pageDigitOffset + myOffset);
340
341 } // for (uint8_t i = iThread; blockOffset < nAdc; i += NThreads)
342}
343
344GPUd() void GPUTPCCFDecodeZSLink::GetChannelBitmask(const zerosupp_link_based::CommonHeader& tbHdr, uint32_t* chan)
345{
346 chan[0] = tbHdr.bitMaskLow & 0xfffffffful;
347 chan[1] = tbHdr.bitMaskLow >> (sizeof(uint32_t) * CHAR_BIT);
348 chan[2] = tbHdr.bitMaskHigh;
349}
350
351GPUd() bool GPUTPCCFDecodeZSLink::ChannelIsActive(const uint32_t* chan, uint8_t chanIndex)
352{
353 if (chanIndex >= zerosupp_link_based::ChannelPerTBHeader) {
354 return false;
355 }
356 constexpr uint8_t N_BITS_PER_ENTRY = sizeof(*chan) * CHAR_BIT;
357 const uint8_t entryIndex = chanIndex / N_BITS_PER_ENTRY;
358 const uint8_t bitInEntry = chanIndex % N_BITS_PER_ENTRY;
359 return chan[entryIndex] & (1 << bitInEntry);
360}
361
362// ===========================================================================
363// ===========================================================================
364// Decode ZS Link Base
365// ===========================================================================
366// ===========================================================================
367
368template <class Decoder>
369GPUd() void GPUTPCCFDecodeZSLinkBase::Decode(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, typename Decoder::GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut)
370{
371 const uint32_t sector = clusterer.mISector;
372
373#ifdef GPUCA_GPUCODE
374 const uint32_t endpoint = clusterer.mPzsOffsets[iBlock].endpoint;
375#else // CPU
376 const bool deviceLayout = clusterer.IsDeviceProcessor(); // Host-compiled kernel running as device kernel (CPUDevice backend): GPU work distribution, one block per page
377 const uint32_t endpoint = deviceLayout ? clusterer.mPzsOffsets[iBlock].endpoint : iBlock;
378#endif
379
380 const GPUTrackingInOutZS::GPUTrackingInOutZSSector& zs = clusterer.GetConstantMem()->ioPtrs.tpcZS->sector[sector];
381 if (zs.count[endpoint] == 0) {
382 return;
383 }
384
385 uint32_t pageDigitOffset = clusterer.mPzsOffsets[iBlock].offset;
386
387#ifdef GPUCA_GPUCODE
388 const uint32_t i = 0;
389 const uint32_t j = clusterer.mPzsOffsets[iBlock].num;
390 {
391 {
392#else // CPU
393 const uint32_t iFirst = deviceLayout ? 0 : clusterer.mMinMaxCN[endpoint].zsPtrFirst;
394 const uint32_t iLast = deviceLayout ? 1 : clusterer.mMinMaxCN[endpoint].zsPtrLast;
395 for (uint32_t i = iFirst; i < iLast; i++) {
396 const uint32_t minJ = deviceLayout ? clusterer.mPzsOffsets[iBlock].num : ((i == iFirst) ? clusterer.mMinMaxCN[endpoint].zsPageFirst : 0);
397 const uint32_t maxJ = deviceLayout ? (minJ + 1) : ((i + 1 == iLast) ? clusterer.mMinMaxCN[endpoint].zsPageLast : zs.nZSPtr[endpoint][i]);
398 for (uint32_t j = minJ; j < maxJ; j++) {
399#endif
400 const uint32_t* pageSrc = (const uint32_t*)(((const uint8_t*)zs.zsPtr[endpoint][i]) + j * TPCZSHDR::TPC_ZS_PAGE_SIZE);
401 // Cache zs page in shared memory. Curiously this actually degrades performance...
402 // GPUCA_SHARED_CACHE_REF(&smem.ZSPage[0], pageSrc, TPCZSHDR::TPC_ZS_PAGE_SIZE, uint32_t, pageCache);
403 // GPUbarrier();
404 // const uint8_t* page = (const uint8_t*)pageCache;
405 const uint8_t* page = (const uint8_t*)pageSrc;
406
407 const auto* rdHdr = Peek<header::RAWDataHeader>(page);
408
409 if (o2::raw::RDHUtils::getMemorySize(*rdHdr) == sizeof(o2::header::RAWDataHeader)) {
410#ifdef GPUCA_GPUCODE
411 return;
412#else
413 continue;
414#endif
415 }
416
417 DecodeCtx ctx{
418 .clusterer = clusterer,
419 .page = page,
420 .iBlock = iBlock,
421 .nThreads = nThreads,
422 .iThread = iThread,
423 .pageDigitOffset = pageDigitOffset,
424 .firstHBF = firstHBF,
425 .tpcTimeBinCut = tpcTimeBinCut,
426 };
427
428 pageDigitOffset = Decoder::DecodePage(smem, ctx);
429 } // [CPU] for (uint32_t j = minJ; j < maxJ; j++)
430 } // [CPU] for (uint32_t i = clusterer.mMinMaxCN[endpoint].zsPtrFirst; i < clusterer.mMinMaxCN[endpoint].zsPtrLast; i++)
431
432#ifdef GPUCA_CHECK_TPCZS_CORRUPTION
433 if (iThread == 0 && iBlock < nBlocks - 1) {
434 uint32_t maxOffset = clusterer.mPzsOffsets[iBlock + 1].offset;
435 if (pageDigitOffset != maxOffset) {
436 clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_OFFSET, clusterer.mISector * 1000 + endpoint, pageDigitOffset, maxOffset);
437 }
438 }
439#endif
440}
441
442GPUd() o2::tpc::PadPos GPUTPCCFDecodeZSLinkBase::GetPadAndRowFromFEC(processorType& clusterer, int32_t cru, int32_t rawFECChannel, int32_t fecInPartition)
443{
444#ifndef GPUCA_RUN2
445 // Ported from tpc::Mapper (Not available on GPU...)
446 constexpr GPUTPCGeometry geo;
447
448 const int32_t regionIter = cru % 2;
449 const int32_t istreamm = ((rawFECChannel % 10) / 2);
450 const int32_t partitionStream = istreamm + regionIter * 5;
451 const int32_t sampaOnFEC = geo.GetSampaMapping(partitionStream);
452 const int32_t channel = (rawFECChannel % 2) + 2 * (rawFECChannel / 10);
453 const int32_t channelOnSAMPA = channel + geo.GetChannelOffset(partitionStream);
454
455 const int32_t partition = (cru % 10) / 2;
456 const int32_t fecInSector = geo.GetSectorFECOffset(partition) + fecInPartition;
457
458 const TPCZSLinkMapping* gpuMapping = clusterer.GetConstantMem()->calibObjects.tpcZSLinkMapping;
459 assert(gpuMapping != nullptr);
460
461 uint16_t globalSAMPAId = (static_cast<uint16_t>(fecInSector) << 8) + (static_cast<uint16_t>(sampaOnFEC) << 5) + static_cast<uint16_t>(channelOnSAMPA);
462 const o2::tpc::PadPos pos = gpuMapping->FECIDToPadPos[globalSAMPAId];
463
464 return pos;
465#else
466 return o2::tpc::PadPos{};
467#endif
468}
469
470GPUd() void GPUTPCCFDecodeZSLinkBase::WriteCharge(processorType& clusterer, float charge, PadPos padAndRow, TPCFragmentTime localTime, size_t positionOffset)
471{
472 const uint32_t sector = clusterer.mISector;
473 CfChargePos* positions = clusterer.mPpositions;
474#ifdef GPUCA_CHECK_TPCZS_CORRUPTION
475 if (padAndRow.getRow() >= GPUTPCGeometry::NROWS) {
476 positions[positionOffset] = INVALID_CHARGE_POS;
477 clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_ROW, clusterer.mISector * 1000 + padAndRow.getRow());
478 return;
479 }
480#endif
481 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
482
483 CfChargePos pos(padAndRow.getRow(), padAndRow.getPad(), localTime);
484 positions[positionOffset] = pos;
485
486 if (charge >= clusterer.Param().rec.tpc.hipTailFilterMinimum) {
487 charge = 1023.f;
488 }
489
490 // Only apply gain correction if ADC not fully saturated
491 // and ensure gain correction doesn't accidentally saturate the ADC
492 if (charge < 1023.f) [[likely]] {
493 auto gain = clusterer.GetConstantMem()->calibObjects.tpcPadGain->getGainCorrection(sector, padAndRow.getRow(), padAndRow.getPad());
494 charge = CAMath::Min(charge * gain, 1022.f);
495 }
496
497 chargeMap[pos] = PackedCharge(charge);
498}
499
500GPUd() uint16_t GPUTPCCFDecodeZSLinkBase::FillWithInvalid(processorType& clusterer, int32_t iThread, int32_t nThreads, uint32_t pageDigitOffset, uint16_t nSamples)
501{
502 for (uint16_t i = iThread; i < nSamples; i += nThreads) {
503 clusterer.mPpositions[pageDigitOffset + i] = INVALID_CHARGE_POS;
504 }
505 return nSamples;
506}
507
508// ===========================================================================
509// ===========================================================================
510// Decode ZS Dense Link
511// ===========================================================================
512// ===========================================================================
513
514template <>
515GPUd() void GPUTPCCFDecodeZSDenseLink::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer, int32_t firstHBF, int32_t tpcTimeBinCut)
516{
517 Decode<GPUTPCCFDecodeZSDenseLink>(nBlocks, nThreads, iBlock, iThread, smem, clusterer, firstHBF, tpcTimeBinCut);
518}
519
520GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& ctx)
521{
522 const uint8_t* const pageStart = ctx.page;
523
524 const auto* rawDataHeader = Peek<header::RAWDataHeader>(ctx.page);
525 const auto* decHeader = Peek<TPCZSHDRV2>(ctx.page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2));
526 ConsumeHeader<header::RAWDataHeader>(ctx.page);
527
528 uint16_t nSamplesWritten = 0;
529 const uint16_t nSamplesInPage = decHeader->nADCsamples;
530
531 const auto* payloadEnd = Peek(pageStart, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2) - ((decHeader->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0));
532 const auto* nextPage = Peek(pageStart, TPCZSHDR::TPC_ZS_PAGE_SIZE);
533
534 const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage;
535
536 ConsumeBytes(ctx.page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader));
537
538 int err = GPUErrors::ERROR_NONE;
539
540 if (decHeader->version < ZSVersionDenseLinkBased) {
541 err = GPUErrors::ERROR_TPCZS_VERSION_MISMATCH;
542 }
543
544 if (decHeader->magicWord != zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) {
545 err = GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD;
546 }
547
548 for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) {
549
550 ptrdiff_t sizeLeftInPage = payloadEnd - ctx.page;
551 if (sizeLeftInPage <= 0) {
552 err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW;
553 break;
554 }
555
556 int16_t nSamplesWrittenTB = 0;
557 uint16_t nSamplesLeftInPage = nSamplesInPage - nSamplesWritten;
558
559 if (i == decHeader->nTimebinHeaders - 1 && extendsToNextPage) {
560 if (raw::RDHUtils::getMemorySize(*rawDataHeader) != TPCZSHDR::TPC_ZS_PAGE_SIZE) {
561 err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW;
562 break;
563 }
564
565 if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) {
566 nSamplesWrittenTB = DecodeTB<true>(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage);
567 } else {
568 err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF;
569 break;
570 }
571 } else {
572 nSamplesWrittenTB = DecodeTB<false>(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage);
573 }
574
575 // Abort decoding the page if an error was detected.
576 if (nSamplesWrittenTB < 0) {
577 err = -nSamplesWrittenTB;
578 break;
579 }
580
581 nSamplesWritten += nSamplesWrittenTB;
582 ctx.pageDigitOffset += nSamplesWrittenTB;
583 } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++)
584
585 if (nSamplesWritten != nSamplesInPage) {
586 if (nSamplesWritten < nSamplesInPage) {
587 ctx.pageDigitOffset += FillWithInvalid(ctx.clusterer, ctx.iThread, ctx.nThreads, ctx.pageDigitOffset, nSamplesInPage - nSamplesWritten);
588 }
589 err = !err ? GPUErrors::ERROR_TPCZS_INVALID_NADC : err; // Ensure we don't overwrite any previous error
590 }
591
592 if (ctx.iThread == 0 && err) {
593 [[maybe_unused]] bool dumpPage = false;
594
595 if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) {
596 ctx.clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased);
597 } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) {
598 ctx.clusterer.raiseError(err, decHeader->magicWord);
599 } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) {
600 ctx.clusterer.raiseError(err, ctx.clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage));
601 } else if (err == GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW) {
602 ctx.clusterer.raiseError(err, extendsToNextPage);
603 dumpPage = true;
604 } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) {
605 ctx.clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage);
606 dumpPage = true;
607 } else {
608 ctx.clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err);
609 }
610
611#ifdef GPUCA_CHECK_TPCZS_CORRUPTION
612#ifndef GPUCA_GPUCODE
613 if (dumpPage) {
614 // allocate more space on the stack for fname, so it can be overwritten by hand in a debugger.
615 const char fname[64] = "dump00.bin";
616 FILE* foo = fopen(fname, "w+b");
617 fwrite(pageStart, 1, TPCZSHDR::TPC_ZS_PAGE_SIZE, foo);
618 fclose(foo);
619 }
620#endif
621#endif
622 }
623
624 return ctx.pageDigitOffset;
625}
626
627template <bool PayloadExtendsToNextPage>
628GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB(
629 GPUSharedMemory& smem,
630 DecodeCtx& ctx,
631 const header::RAWDataHeader* rawDataHeader,
632 int32_t cru,
633 uint16_t nSamplesLeftInPage,
634 const uint8_t* payloadEnd,
635 const uint8_t* nextPage)
636{
637#define MAYBE_PAGE_OVERFLOW(pagePtr) \
638 if constexpr (PayloadExtendsToNextPage) { \
639 if (pagePtr >= payloadEnd && pagePtr < nextPage) { \
640 ptrdiff_t diff = pagePtr - payloadEnd; \
641 pagePtr = nextPage; \
642 ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \
643 } \
644 } else { \
645 if (pagePtr > payloadEnd) { \
646 return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \
647 } \
648 }
649
650#define PEEK_OVERFLOW(pagePtr, offset) \
651 (*(PayloadExtendsToNextPage && (pagePtr) < nextPage && (pagePtr) + (offset) >= payloadEnd \
652 ? nextPage + sizeof(header::RAWDataHeader) + ((pagePtr) + (offset) - payloadEnd) \
653 : (pagePtr) + (offset)))
654
655#define TEST_BIT(x, bit) static_cast<bool>((x) & (1 << (bit)))
656
657 constexpr int32_t NTHREADS = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFDecodeZSDenseLink);
658 static_assert(NTHREADS == GPUCA_WARP_SIZE, "Decoding TB Headers in parallel assumes block size is a single warp.");
659
660 const CfFragment& frag = ctx.clusterer.mPmemory->frag;
661
662 // Read timebin block header
663 uint16_t tbbHdr = ConsumeByte(ctx.page);
665 tbbHdr |= static_cast<uint16_t>(ConsumeByte(ctx.page)) << CHAR_BIT;
667
668 uint8_t nLinksInTimebin = tbbHdr & 0x000F;
669 uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4;
670 int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - ctx.firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN;
671
672 int16_t nSamplesInTB = 0;
673
674 // Read timebin link headers
675 for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) {
676 uint8_t timebinLinkHeaderStart = ConsumeByte(ctx.page);
678
679 if (ctx.iThread == 0) {
680 smem.linkIds[iLink] = timebinLinkHeaderStart & 0b00011111;
681 }
682 bool bitmaskIsFlat = timebinLinkHeaderStart & 0b00100000;
683
684 uint16_t bitmaskL2 = 0x03FF;
685 if (not bitmaskIsFlat) {
686 bitmaskL2 = static_cast<uint16_t>(timebinLinkHeaderStart & 0b11000000) << 2 | static_cast<uint16_t>(ConsumeByte(ctx.page));
688 }
689
690 int32_t nBytesBitmask = CAMath::Popcount(bitmaskL2);
691
692 for (int32_t chan = ctx.iThread; chan < CAMath::nextMultipleOf<NTHREADS>(80); chan += NTHREADS) {
693 int32_t chanL2Idx = chan / 8;
694 bool l2 = TEST_BIT(bitmaskL2, chanL2Idx);
695
696 int32_t chanByteOffset = nBytesBitmask - 1 - CAMath::Popcount(bitmaskL2 >> (chanL2Idx + 1));
697
698 uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(ctx.page, chanByteOffset), chan % 8) : 0);
699
700 int32_t nSamplesStep;
701 int32_t threadSampleOffset = CfUtils::warpPredicateScan(myChannelHasData, &nSamplesStep);
702
703 if (myChannelHasData) {
704 smem.rawFECChannels[nSamplesInTB + threadSampleOffset] = chan;
705 }
706
707 nSamplesInTB += nSamplesStep;
708 }
709
710 ConsumeBytes(ctx.page, nBytesBitmask);
712
713 if (ctx.iThread == 0) {
714 smem.samplesPerLinkEnd[iLink] = nSamplesInTB;
715 }
716
717 } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++)
718
719 GPUbarrierWarp(); // Ensure all writes to shared memory are finished, before reading it
720
721 if (nSamplesInTB > nSamplesLeftInPage) {
722 return -GPUErrors::ERROR_TPCZS_INVALID_NADC;
723 }
724
725 // This needs to happen BEFORE checking if the timebin is in fragment
726 // to ensure ADC bytes are always consumed, even if data isn't decoded
727 const uint8_t* adcData = ConsumeBytes(ctx.page, (nSamplesInTB * DECODE_BITS + 7) / 8);
729
730 bool discardTimeBin = not frag.contains(timeBin);
731 discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut);
732
733 if (discardTimeBin) {
734 return FillWithInvalid(ctx.clusterer, ctx.iThread, NTHREADS, ctx.pageDigitOffset, nSamplesInTB);
735 }
736
737 // Unpack ADC
738 int32_t iLink = 0;
739 for (uint16_t sample = ctx.iThread; sample < nSamplesInTB; sample += NTHREADS) {
740 const uint16_t adcBitOffset = sample * DECODE_BITS;
741 uint16_t adcByteOffset = adcBitOffset / CHAR_BIT;
742 const uint8_t adcOffsetInByte = adcBitOffset - adcByteOffset * CHAR_BIT;
743
744 static_assert(DECODE_BITS <= sizeof(uint16_t) * CHAR_BIT);
745
746 uint16_t adc = 0;
747 for (uint8_t bits = 0; bits < DECODE_BITS; bits += CHAR_BIT) {
748 adc |= static_cast<uint16_t>(PEEK_OVERFLOW(adcData, adcByteOffset)) << bits;
749 adcByteOffset++;
750 }
751 adc >>= adcOffsetInByte;
752
753 while (smem.samplesPerLinkEnd[iLink] <= sample) {
754 iLink++;
755 }
756
757 int32_t rawFECChannelLink = smem.rawFECChannels[sample];
758
759 // Unpack data for cluster finder
760 o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannelLink, smem.linkIds[iLink]);
761
762 float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR);
763 WriteCharge(ctx.clusterer, charge, padAndRow, frag.toLocal(timeBin), ctx.pageDigitOffset + sample);
764
765 } // for (uint16_t sample = iThread; sample < nSamplesInTB; sample += NTHREADS)
766
767 GPUbarrierWarp(); // Ensure all reads to shared memory are finished, before decoding next header into shmem
768
769 return nSamplesInTB;
770
771#undef TEST_BIT
772#undef PEEK_OVERFLOW
773#undef MAYBE_PAGE_OVERFLOW
774}
775
776GPUd() bool GPUTPCCFDecodeZSDenseLink::ChannelIsActive(const uint8_t* chan, uint16_t chanIndex)
777{
778 constexpr uint8_t N_BITS_PER_ENTRY = sizeof(*chan) * CHAR_BIT;
779 const uint8_t entryIndex = chanIndex / N_BITS_PER_ENTRY;
780 const uint8_t bitInEntry = chanIndex % N_BITS_PER_ENTRY;
781 return chan[entryIndex] & (1 << bitInEntry);
782}
#define INVALID_TIME_BIN
Definition CfChargePos.h:25
int16_t charge
Definition RawEventData.h:5
int32_t i
#define GPUbarrierWarp()
#define GPUbarrier()
#define GPUCA_GET_THREAD_COUNT(...)
#define GPUCA_SHARED_CACHE_REF(target, src, size, reftype, ref)
Definition GPUDef.h:64
uint8_t * nSeq
TPCZSHDR * hdr
zsPage * page
uint8_t * pagePtr
int32_t nSamples
GPUdii() void GPUTPCCFDecodeZS
#define TEST_BIT(x, bit)
#define MAYBE_PAGE_OVERFLOW(pagePtr)
#define PEEK_OVERFLOW(pagePtr, offset)
Header to collect LHC related constants.
uint16_t pos
Definition RawData.h:3
uint32_t j
Definition RawData.h:0
uint8_t endpoint
Definition RawData.h:0
GPUd() static o2 float o2::tpc::PadPos tpccf::TPCFragmentTime localTime
GPUd() static o2 float o2::tpc::PadPos pos
int32_t int32_t int32_t Decoder::GPUSharedMemory processorType & clusterer
int32_t int32_t uint32_t pageDigitOffset
GPUd() static o2 float o2::tpc::PadPos tpccf::TPCFragmentTime size_t positionOffset
int32_t int32_t uint32_t uint16_t nSamples
int32_t int32_t int32_t iThread
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
Definition glcorearb.h:4150
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLuint mask
Definition glcorearb.h:291
uint8_t itsSharedClusterMap uint8_t
GPUd() const expr uint32_t MultivariatePolynomialHelper< Dim
RAWDataHeaderV7 RAWDataHeader
const track::TrackFitContext< NLayers > const TrackFollowContext< NLayers > & ctx
Global TPC definitions and constants.
Definition SimTraits.h:172
GPUd() void PIDResponse
Definition PIDResponse.h:71
@ ZSVersionDenseLinkBased
@ ZSVersionLinkBasedWithMeta
constexpr std::array< int, nLayers > nRows
Definition Specs.h:59
o2::tpc::PadPos FECIDToPadPos[TPC_FEC_IDS_IN_SECTOR]
unsigned char nTimeBinSpan
unsigned char version
unsigned short timeOffset
unsigned short rowMask
HistogramRegistry foo()
coder decode(ctfImage, triggersD, clustersD)
std::vector< int > row
ArrayADC adc