Project
Loading...
Searching...
No Matches
RawWriter.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
12#include <fairlogger/Logger.h>
13
14#include <fmt/core.h>
15#include <gsl/span>
16#include <TSystem.h>
21#include "CPVBase/Geometry.h"
26
27using namespace o2::cpv;
28
30{
31 mRawWriter = std::make_unique<o2::raw::RawFileWriter>(o2::header::gDataOriginCPV, true); // true = cru detector
32 mRawWriter->setCarryOverCallBack(this);
33 mRawWriter->setApplyCarryOverToLastPage(true);
34 mRawWriter->useRDHVersion(mRDHVersion);
35 mRawWriter->useRDHDataFormat(mDataFormat);
36
37 // register all cpv links
38 for (auto&& link : links) {
39 std::string rawFileName = mOutputLocation + "/CPV_" + link.flpId + "_cru" + std::to_string(link.cruId) + "_" + std::to_string(link.endPointId);
40 if (mFileFor == FileFor_t::kLink) {
41 rawFileName += Form("link%d", link.linkId);
42 }
43 rawFileName += ".raw";
44 mRawWriter->registerLink(link.feeId, link.cruId, link.linkId, link.endPointId, rawFileName.data());
45 }
46
47 // CCDB setup
48 const auto& hbfutils = o2::raw::HBFUtils::Instance();
49 LOG(info) << "CCDB Url: " << mCcdbUrl;
51 ccdbMgr.setURL(mCcdbUrl);
52 bool isCcdbReachable = ccdbMgr.isHostReachable(); // if host is not reachable we can use only dummy calibration
53 if (!isCcdbReachable) { // dummy calibration
54 if (mCcdbUrl.compare("localtest") != 0) {
55 LOG(error) << "Host " << mCcdbUrl << " is not reachable!!!";
56 }
57 LOG(info) << "Using dummy calibration and default Lm-L0 delay";
58 mLM_L0_delay = o2::ctp::TriggerOffsetsParam::Instance().LM_L0;
59 mCalibParams = new o2::cpv::CalibParams(1);
60 mBadMap = new o2::cpv::BadChannelMap(1);
61 mPedestals = new o2::cpv::Pedestals(1);
62 } else { // read ccdb
63 ccdbMgr.setCaching(true); // make local cache of remote objects
64 ccdbMgr.setLocalObjectValidityChecking(true); // query objects from remote site only when local one is not valid
65 LOG(info) << "Successfully initializated BasicCCDBManager with caching option";
66
67 // read calibration from ccdb (for now do it only at the beginning of dataprocessing)
68 // setup timestamp according to anchors
69 ccdbMgr.setTimestamp(hbfutils.startTime);
70 LOG(info) << "Using time stamp " << ccdbMgr.getTimestamp();
71
72 // Lm-L0 delay
73 mLM_L0_delay = ccdbMgr.get<o2::ctp::TriggerOffsetsParam>("CTP/Config/TriggerOffsets")->LM_L0;
74
75 // gains
76 LOG(info) << "CCDB: Reading o2::cpv::CalibParams from CPV/Calib/Gains";
77 mCalibParams = ccdbMgr.get<o2::cpv::CalibParams>("CPV/Calib/Gains");
78 if (!mCalibParams) {
79 LOG(error) << "Cannot get o2::cpv::CalibParams from CCDB. using dummy calibration!";
80 mCalibParams = new o2::cpv::CalibParams(1);
81 }
82
83 // no need to mask bad channels -> they will be thrown away at reconstruntion anyway
84 /*
85 LOG(info) << "CCDB: Reading o2::cpv::BadChannelMap from CPV/Calib/BadChannelMap";
86 mBadMap = ccdbMgr.get<o2::cpv::BadChannelMap>("CPV/Calib/BadChannelMap"));
87 if (!mBadMap) {
88 LOG(error) << "Cannot get o2::cpv::BadChannelMap from CCDB. using dummy calibration!";
89 mBadMap = new o2::cpv::BadChannelMap(1);
90 }
91 */
92
93 // pedestals
94 LOG(info) << "CCDB: Reading o2::cpv::Pedestals from CPV/Calib/Pedestals";
95 mPedestals = ccdbMgr.get<o2::cpv::Pedestals>("CPV/Calib/Pedestals");
96 if (!mPedestals) {
97 LOG(error) << "Cannot get o2::cpv::Pedestals from CCDB. using dummy calibration!";
98 mPedestals = new o2::cpv::Pedestals(1);
99 }
100 LOG(info) << "Task configuration is done.";
101 }
102}
103
104void RawWriter::digitsToRaw(gsl::span<o2::cpv::Digit> digitsbranch, gsl::span<o2::cpv::TriggerRecord> triggerbranch)
105{
106 if (triggerbranch.begin() == triggerbranch.end()) { // do we have any data?
107 return;
108 }
109
110 // process digits which belong to same orbit (taking into account )
111 int iFirstTrgInCurrentOrbit = 0;
112 unsigned int currentOrbit = (triggerbranch[0].getBCData() + mLM_L0_delay).orbit;
113 int nTrgsInCurrentOrbit = 1;
114 for (unsigned int iTrg = 1; iTrg < triggerbranch.size(); iTrg++) {
115 if ((triggerbranch[iTrg].getBCData() + mLM_L0_delay).orbit != currentOrbit) { // if orbit changed, write previous orbit to file
116 processOrbit(digitsbranch, triggerbranch.subspan(iFirstTrgInCurrentOrbit, nTrgsInCurrentOrbit));
117 iFirstTrgInCurrentOrbit = iTrg; // orbit changed
118 nTrgsInCurrentOrbit = 1;
119 currentOrbit = (triggerbranch[iTrg].getBCData() + mLM_L0_delay).orbit;
120 } else {
121 nTrgsInCurrentOrbit++;
122 }
123 }
124 processOrbit(digitsbranch, triggerbranch.subspan(iFirstTrgInCurrentOrbit, nTrgsInCurrentOrbit)); // process last orbit
125}
126
127// prepare preformatted data for one orbit and send it to RawFileWriter
128bool RawWriter::processOrbit(const gsl::span<o2::cpv::Digit> digitsbranch, const gsl::span<o2::cpv::TriggerRecord> trgs)
129{
130 static int nMaxGbtWordsPerPage = o2::raw::RDHUtils::MAXCRUPage / o2::raw::RDHUtils::GBTWord128 - 4; // 512*16/16 - 4 = 508;
131 // 4 gbt words are reserved for RDH
132 static int nMaxCpvWordsPerPage = (mDataFormat == 2 ? (nMaxGbtWordsPerPage * 16 / 10) : nMaxGbtWordsPerPage);
133
134 // clear payloads of all links
135 for (auto& payload : mPayload) {
136 payload.clear();
137 }
138
139 // we're going to prepare preformatted pages
140 bool preformatted = true;
141
142 int cpvWordCounter[kNGBTLinks] = {0, 0, 0};
143 int cpvWordCounterBeforeCPVTrailer[kNGBTLinks] = {0, 0, 0};
144 bool isHeaderClosedWithTrailer[kNGBTLinks] = {false, false, false};
145 for (auto& trg : trgs) {
146 o2::InteractionRecord currentIR = trg.getBCData();
147 currentIR += mLM_L0_delay;
148 LOG(debug) << "RawWriter::processOrbit() : "
149 << "I start to process trigger record (orbit = " << currentIR.orbit
150 << ", BC = " << currentIR.bc << ")";
151 LOG(debug) << "First entry = " << trg.getFirstEntry() << ", Number of objects = " << trg.getNumberOfObjects();
152
153 // Clear array which is used to store digits
154 for (int i = kNcc; i--;) {
155 for (int j = kNDilogic; j--;) {
156 for (int k = kNGasiplex; k--;) {
157 mPadCharge[i][j][k].clear();
158 }
159 }
160 }
161
162 // make payload for current trigger
163 int nDigsInTrg[kNGBTLinks] = {0, 0, 0};
164 for (auto& dig : gsl::span(digitsbranch.data() + trg.getFirstEntry(), trg.getNumberOfObjects())) {
165
166 short absId = dig.getAbsId();
167 short ccId, dil, gas, pad;
168 o2::cpv::Geometry::absIdToHWaddress(absId, ccId, dil, gas, pad);
169
170 // Convert Amp to ADC counts
171 short charge = std::round(dig.getAmplitude() / mCalibParams->getGain(absId) + mPedestals->getPedestal(absId));
172 if (charge > 4095) {
173 charge = 4095;
174 }
175 mPadCharge[ccId][dil][gas].emplace_back(charge, pad);
176 nDigsInTrg[ccId / (kNcc / kNGBTLinks)]++; // linkId = ccId/8 or absId/7680
177 }
178 LOG(debug) << "I produced " << nDigsInTrg << " digits for this trigger record";
179
180 // we need to write header + at least 1 payload word + trailer
181 for (int iLink = 0; iLink < kNGBTLinks; iLink++) { // looping links
182 cpvWordCounterBeforeCPVTrailer[iLink] = 0;
183 if (nMaxCpvWordsPerPage - cpvWordCounter[iLink] < 3) { // write dma page to file because there are no space left for new trigger
184 LOG(debug) << "RawWriter::processOrbit() : before header: adding preformatted dma page of size " << mPayload[iLink].size();
185 // add 0xff padding in case when payload is not comlete 128-bits words
186 if (mDataFormat == 2 && mPayload[iLink].size() % 16) {
187 for (int i = 0; i < 16 - (mPayload[iLink].size() % 16); i++) {
188 mPayload[iLink].push_back(char(0xff));
189 }
190 }
191 mRawWriter->addData(links[iLink].feeId, links[iLink].cruId, links[iLink].linkId, links[iLink].endPointId, currentIR,
192 gsl::span<char>(mPayload[iLink].data(), mPayload[iLink].size()), preformatted);
193 mPayload[iLink].clear();
194 cpvWordCounter[iLink] = 0;
195 cpvWordCounterBeforeCPVTrailer[iLink] = 0;
196 }
197
198 // first, header goes
199 CpvHeader header(currentIR, false, false);
200 for (int i = 0; i < 10; i++) {
201 mPayload[iLink].push_back(header.mBytes[i]);
202 }
203 // add padding
204 if (mDataFormat == 0) {
205 for (int i = 0; i < 6; i++) {
206 mPayload[iLink].push_back(char(0));
207 }
208 }
209 isHeaderClosedWithTrailer[iLink] = false;
210 LOG(debug) << "RawWriter::processOrbit() : "
211 << "I wrote cpv header for orbit = " << currentIR.orbit
212 << " and BC = " << currentIR.bc;
213
214 cpvWordCounter[iLink]++;
215 cpvWordCounterBeforeCPVTrailer[iLink]++;
216
217 int nDigsToWriteLeft = nDigsInTrg[iLink];
218
219 for (char ccId = iLink * (kNcc / kNGBTLinks); (ccId < (iLink + 1) * (kNcc / kNGBTLinks)) && (ccId < kNcc); ccId++) {
220 int ccWordCounter = 0;
221 for (char dil = 0; dil < kNDilogic; dil++) {
222 for (char gas = 0; gas < kNGasiplex; gas++) {
223 for (padCharge& pc : mPadCharge[int(ccId)][int(dil)][int(gas)]) {
224 // Generate 3 CC words, add CC header and empty bits to complete 128 bits;
225 PadWord currentword = {0};
226 currentword.charge = pc.charge;
227 currentword.address = pc.pad;
228 currentword.gas = gas;
229 currentword.dil = dil;
230 mPayload[iLink].push_back(currentword.mBytes[0]);
231 mPayload[iLink].push_back(currentword.mBytes[1]);
232 mPayload[iLink].push_back(currentword.mBytes[2]);
233 ccWordCounter++;
234 nDigsToWriteLeft--;
235 if (ccWordCounter % 3 == 0) { // complete 3 channels (72 bit) + CC index (8 bits) + 6 empty bits = Generate 128 bits of data
236 mPayload[iLink].push_back(ccId);
237 if (mDataFormat == 0) { // add padding
238 for (int i = 6; i--;) {
239 mPayload[iLink].push_back(char(0));
240 }
241 }
242 cpvWordCounter[iLink]++;
243 cpvWordCounterBeforeCPVTrailer[iLink]++;
244 if (nMaxCpvWordsPerPage - cpvWordCounter[iLink] == 1) { // the only space for trailer left on current page
245 CpvTrailer tr(cpvWordCounterBeforeCPVTrailer[iLink], currentIR.bc, nDigsToWriteLeft == 0); // add trailer and flush page to file
246 for (int i = 0; i < 10; i++) {
247 mPayload[iLink].push_back(tr.mBytes[i]);
248 }
249 // add padding
250 if (mDataFormat == 0) {
251 for (int i = 0; i < 6; i++) {
252 mPayload[iLink].push_back(char(0));
253 }
254 }
255
256 isHeaderClosedWithTrailer[iLink] = true;
257 LOG(debug) << "RawWriter::processOrbit() : middle of payload: adding preformatted dma page of size " << mPayload[iLink].size();
258 // add 0xff padding in case when payload is not complete 128-bits words
259 if (mDataFormat == 2 && mPayload[iLink].size() % 16) {
260 for (int i = 0; i < 16 - (mPayload[iLink].size() % 16); i++) {
261 mPayload[iLink].push_back(char(0xff));
262 }
263 }
264 mRawWriter->addData(links[iLink].feeId, links[iLink].cruId, links[iLink].linkId, links[iLink].endPointId, currentIR,
265 gsl::span<char>(mPayload[iLink].data(), mPayload[iLink].size()), preformatted);
266
267 mPayload[iLink].clear();
268 cpvWordCounter[iLink] = 0;
269 cpvWordCounterBeforeCPVTrailer[iLink] = 0;
270 if (nDigsToWriteLeft) { // some digits left for writing
271 CpvHeader newHeader(currentIR, false, true);
272 for (int i = 0; i < 10; i++) { // so put a new header and continue
273 mPayload[iLink].push_back(newHeader.mBytes[i]);
274 }
275 // add padding
276 if (mDataFormat == 0) {
277 for (int i = 0; i < 6; i++) {
278 mPayload[iLink].push_back(char(0));
279 }
280 }
281 isHeaderClosedWithTrailer[iLink] = false;
282 cpvWordCounter[iLink]++;
283 cpvWordCounterBeforeCPVTrailer[iLink]++;
284 }
285 }
286 }
287 }
288 }
289 } // end of dil cycle
290 if (ccWordCounter % 3 != 0) {
291 while (ccWordCounter % 3 != 0) {
292 mPayload[iLink].push_back(char(255));
293 mPayload[iLink].push_back(char(255));
294 mPayload[iLink].push_back(char(255));
295 ccWordCounter++;
296 }
297 mPayload[iLink].push_back(ccId);
298 if (mDataFormat == 0) { // add padding
299 for (int i = 6; i--;) {
300 mPayload[iLink].push_back(char(0));
301 }
302 }
303 cpvWordCounter[iLink]++;
304 cpvWordCounterBeforeCPVTrailer[iLink]++;
305 if (nMaxCpvWordsPerPage - cpvWordCounter[iLink] == 1) { // the only space for trailer left on current page
306 CpvTrailer tr(cpvWordCounterBeforeCPVTrailer[iLink], currentIR.bc, nDigsToWriteLeft == 0); // add trailer and flush page to file
307 for (int i = 0; i < 10; i++) {
308 mPayload[iLink].push_back(tr.mBytes[i]);
309 }
310 if (mDataFormat == 0) { // add padding
311 for (int i = 6; i--;) {
312 mPayload[iLink].push_back(char(0));
313 }
314 }
315
316 isHeaderClosedWithTrailer[iLink] = true;
317 LOG(debug) << "RawWriter::processOrbit() : middle of payload (after filling empty words): adding preformatted dma page of size " << mPayload[iLink].size();
318 // add 0xff padding in case when payload is not complete 128-bits words
319 if (mDataFormat == 2 && mPayload[iLink].size() % 16) {
320 for (int i = 0; i < 16 - (mPayload[iLink].size() % 16); i++) {
321 mPayload[iLink].push_back(char(0xff));
322 }
323 }
324 mRawWriter->addData(links[iLink].feeId, links[iLink].cruId, links[iLink].linkId, links[iLink].endPointId, currentIR,
325 gsl::span<char>(mPayload[iLink].data(), mPayload[iLink].size()), preformatted);
326
327 mPayload[iLink].clear();
328 cpvWordCounter[iLink] = 0;
329 cpvWordCounterBeforeCPVTrailer[iLink] = 0;
330 if (nDigsToWriteLeft) { // some digits left for writing
331 for (int i = 0; i < 10; i++) { // so put a new header and continue
332 mPayload[iLink].push_back(header.mBytes[i]);
333 }
334 if (mDataFormat == 0) { // add padding
335 for (int i = 6; i--;) {
336 mPayload[iLink].push_back(char(0));
337 }
338 }
339
340 isHeaderClosedWithTrailer[iLink] = false;
341 cpvWordCounter[iLink]++;
342 cpvWordCounterBeforeCPVTrailer[iLink]++;
343 }
344 }
345 }
346 } // end of ccId cycle
347 if (!isHeaderClosedWithTrailer[iLink]) {
348 CpvTrailer tr(cpvWordCounterBeforeCPVTrailer[iLink], currentIR.bc, true);
349 for (int i = 0; i < 10; i++) {
350 mPayload[iLink].push_back(tr.mBytes[i]);
351 }
352 if (mDataFormat == 0) { // add padding
353 for (int i = 6; i--;) {
354 mPayload[iLink].push_back(char(0));
355 }
356 }
357
358 isHeaderClosedWithTrailer[iLink] = true;
359 cpvWordCounterBeforeCPVTrailer[iLink] = 0;
360 cpvWordCounter[iLink]++;
361 }
362 } // end of iLink cycle
363 } // end of "for (auto& trg : trgs)""
364
365 // flush payload to file (if any)
366 for (int iLink = 0; iLink < kNGBTLinks; iLink++) {
367 if (mPayload[iLink].size()) {
368 LOG(debug) << "RawWriter::processOrbit() : final payload: adding preformatted dma page of size " << mPayload[iLink].size();
369 // add 0xff padding in case when payload is not complete 128-bits words
370 if (mDataFormat == 2 && mPayload[iLink].size() % 16) {
371 for (int i = 0; i < 16 - (mPayload[iLink].size() % 16); i++) {
372 mPayload[iLink].push_back(char(0xff));
373 }
374 }
375 mRawWriter->addData(links[iLink].feeId, links[iLink].cruId, links[iLink].linkId, links[iLink].endPointId,
376 trgs.back().getBCData(), gsl::span<char>(mPayload[iLink].data(), mPayload[iLink].size()), preformatted);
377 mPayload[iLink].clear();
378 }
379 }
380 return true;
381}
382// carryover method is not used as we write preformatted pages
383int RawWriter::carryOverMethod(const header::RDHAny* rdh, const gsl::span<char> data,
384 const char* ptr, int maxSize, int splitID,
385 std::vector<char>& trailer, std::vector<char>& header) const
386{
387
388 constexpr int cpvTrailerSize = 36;
389 int offs = ptr - &data[0]; // offset wrt the head of the payload
390 assert(offs >= 0 && size_t(offs + maxSize) <= data.size()); // make sure ptr and end of the suggested block are within the payload
391 int leftBefore = data.size() - offs; // payload left before this splitting
392 int leftAfter = leftBefore - maxSize; // what would be left after the suggested splitting
393 int actualSize = maxSize;
394 if (leftAfter && leftAfter <= cpvTrailerSize) { // avoid splitting the trailer or writing only it.
395 actualSize -= (cpvTrailerSize - leftAfter) + 4; // (as we work with int, not char in decoding)
396 }
397 return actualSize;
398}
int16_t charge
Definition RawEventData.h:5
uint64_t orbit
Definition RawEventData.h:6
int32_t i
uint32_t j
Definition RawData.h:0
TBranch * ptr
std::ostringstream debug
static BasicCCDBManager & instance()
CCDB container for bad (masked) channels in CPV.
float getGain(unsigned short cellID) const
Get High Gain energy calibration coefficients.
Definition CalibParams.h:49
unsigned char mBytes[10]
Definition RawFormats.h:104
unsigned char mBytes[10]
Definition RawFormats.h:137
static bool absIdToHWaddress(unsigned short absId, short &ccId, short &dil, short &gas, short &pad)
Definition Geometry.cxx:137
short getPedestal(short cellID) const
Get pedestal.
Definition Pedestals.h:49
bool processOrbit(const gsl::span< o2::cpv::Digit > digitsbranch, const gsl::span< o2::cpv::TriggerRecord > trgs)
void digitsToRaw(gsl::span< o2::cpv::Digit > digits, gsl::span< o2::cpv::TriggerRecord > triggers)
int carryOverMethod(const header::RDHAny *rdh, const gsl::span< char > data, const char *ptr, int maxSize, int splitID, std::vector< char > &trailer, std::vector< char > &header) const
GLsizeiptr size
Definition glcorearb.h:659
GLboolean * data
Definition glcorearb.h:298
constexpr o2::header::DataOrigin gDataOriginCPV
Definition DataHeader.h:563
const GBTLinkAttributes links[kNGBTLinks]
Definition RawWriter.h:54
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
uint32_t orbit
LHC orbit.
uint16_t bc
bunch crossing ID of interaction
static constexpr int MAXCRUPage
Definition RDHUtils.h:53
static constexpr int GBTWord128
Definition RDHUtils.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
uint32_t dil
Bits 21 - 22 : dilogic (1..24)
Definition RawFormats.h:31
uint32_t charge
Bits 0 - 11 : charge.
Definition RawFormats.h:28
uint32_t address
Bits 12 - 17 : address (0..47)
Definition RawFormats.h:29
uint32_t gas
Bits 18 - 20 : gasiplex (1..10)
Definition RawFormats.h:30