Line data Source code
1 :
2 : #include "TRACE/tracemf.h"
3 : #define TRACE_NAME "DTC.cpp"
4 :
5 : #include "DTC.h"
6 : #include "EVBErrorStatus.h"
7 : #define TLVL_GetData TLVL_DEBUG + 5
8 : #define TLVL_GetJSONData TLVL_DEBUG + 6
9 : #define TLVL_ReadBuffer TLVL_DEBUG + 7
10 : #define TLVL_ReadNextDAQPacket TLVL_DEBUG + 8
11 : #define TLVL_ReadNextDCSPacket TLVL_DEBUG + 9
12 : #define TLVL_SendDCSRequestPacket TLVL_DEBUG + 10
13 : #define TLVL_SendHeartbeatPacket TLVL_DEBUG + 11
14 : #define TLVL_VerifySimFileInDTC TLVL_DEBUG + 12
15 : #define TLVL_VerifySimFileInDTC2 TLVL_DEBUG + 13
16 : #define TLVL_VerifySimFileInDTC3 TLVL_DEBUG + 14
17 : #define TLVL_WriteSimFileToDTC TLVL_DEBUG + 15
18 : #define TLVL_WriteSimFileToDTC2 TLVL_DEBUG + 16
19 : #define TLVL_WriteSimFileToDTC3 TLVL_DEBUG + 17
20 : #define TLVL_WriteDetectorEmulatorData TLVL_DEBUG + 18
21 : #define TLVL_WriteDataPacket TLVL_DEBUG + 19
22 : #define TLVL_ReleaseBuffers TLVL_DEBUG + 20
23 : #define TLVL_GetCurrentBuffer TLVL_DEBUG + 21
24 : #define TLVL_GetData_3 TLVL_DEBUG + 22
25 : #define TLVL_GetData_2 TLVL_DEBUG + 23
26 : #define TLVL_NextSubEventCout 24 // TTEST adds TLVL_DEBUG
27 : #define TLVL_Crossover TLVL_DEBUG + 30
28 : #define TLVL_Crossover1 TLVL_DEBUG + 31
29 :
30 : #include "dtcInterfaceLib/otsStyleCoutMacros.h"
31 :
32 : #define DTC_TLOG(lvl) TLOG(lvl) << "DTC " << device_.getDeviceUID() << ": "
33 : #undef __COUT_HDR__
34 : #define __COUT_HDR__ "DTC " << device_.getDeviceUID() << ": "
35 :
36 : #include <unistd.h>
37 : #include <fstream>
38 : #include <iostream>
39 : #include <sstream> // Convert uint to hex string
40 : #include <iomanip> // std::setw / std::setfill for record hex dumps
41 : #include <algorithm> // std::min
42 :
43 0 : DTCLib::DTC::DTC(DTC_SimMode mode, int dtc, unsigned rocMask, std::string expectedDesignVersion, bool skipInit, std::string simMemoryFile, const std::string& uid)
44 0 : : DTC_Registers(mode, dtc, simMemoryFile, rocMask, expectedDesignVersion, skipInit, uid), daqDMAInfo_(), dcsDMAInfo_()
45 : {
46 0 : __COUT__ << "CONSTRUCTOR";
47 0 : }
48 :
49 0 : DTCLib::DTC::~DTC()
50 : {
51 0 : __COUT__ << "DESTRUCTOR";
52 : // TLOG_ENTEX(-6);
53 0 : TRACE_EXIT
54 : {
55 0 : __COUT__ << "DESTRUCTOR exit";
56 0 : };
57 :
58 : // do not want to release all from destructor, in case this instance of the device is not the 'owner' of a DMA-channel
59 : // so do not call ReleaseAllBuffers(DTC_DMA_Engine_DAQ) or ReleaseAllBuffers(DTC_DMA_Engine_DCS);
60 :
61 : // assume the destructor is destructive (could be in response to exceptions), so force ending of dcs lock
62 : try
63 : {
64 0 : device_.end_dcs_transaction();
65 : }
66 0 : catch (...)
67 : {
68 0 : __COUT_WARN__ << "Ignoring exception caught in DESTRUCTOR while ending of dcs lock";
69 0 : }
70 0 : }
71 :
72 : //
73 : // DMA Functions -- This if for HW event building
74 : //
75 0 : std::vector<std::unique_ptr<DTCLib::DTC_Event>> DTCLib::DTC::GetData(DTC_EventWindowTag when, bool matchEventWindowTag)
76 : {
77 0 : DTC_TLOG(TLVL_GetData) << "GetData begin EventWindowTag=" << when.GetEventWindowTag(true) << ", matching=" << (matchEventWindowTag ? "true" : "false");
78 0 : std::vector<std::unique_ptr<DTC_Event>> output;
79 0 : std::unique_ptr<DTC_Event> packet = nullptr;
80 :
81 : // Release read buffers here "I am done with everything I read before" (because the return is pointers to the raw data, not copies)
82 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ); // Currently race condition because GetCurrentBuffer(info) is used inside to decide how many buffers to release.
83 :
84 : try
85 : {
86 : // Read the next DTC_Event
87 0 : auto tries = 0;
88 0 : while (packet == nullptr && tries < 3)
89 : {
90 0 : DTC_TLOG(TLVL_GetData) << "GetData before ReadNextDAQPacket, tries=" << tries;
91 0 : packet = ReadNextDAQDMA(100);
92 0 : if (packet != nullptr)
93 : {
94 0 : DTC_TLOG(TLVL_GetData) << "GetData after ReadDMADAQPacket, ts=0x" << std::hex
95 0 : << packet->GetEventWindowTag().GetEventWindowTag(true);
96 : }
97 0 : tries++;
98 : // if (packet == nullptr) usleep(5000);
99 : }
100 0 : if (packet == nullptr)
101 : {
102 0 : DTC_TLOG(TLVL_GetData) << "GetData: Timeout Occurred! (DTC_Event is nullptr after retries)";
103 0 : return output;
104 : }
105 :
106 0 : if (packet->GetEventWindowTag() != when && matchEventWindowTag)
107 : {
108 0 : DTC_TLOG(TLVL_ERROR) << "GetData: Error: DTC_Event has wrong Event Window Tag! 0x" << std::hex << when.GetEventWindowTag(true)
109 0 : << "(expected) != 0x" << std::hex << packet->GetEventWindowTag().GetEventWindowTag(true);
110 0 : packet.reset(nullptr);
111 0 : daqDMAInfo_.currentReadPtr = daqDMAInfo_.lastReadPtr;
112 0 : return output;
113 : }
114 :
115 0 : when = packet->GetEventWindowTag();
116 :
117 0 : DTC_TLOG(TLVL_GetData) << "GetData: Adding DTC_Event " << (void*)daqDMAInfo_.lastReadPtr << " to the list (first)";
118 0 : output.push_back(std::move(packet));
119 :
120 0 : auto done = false;
121 0 : while (!done)
122 : {
123 0 : DTC_TLOG(TLVL_GetData) << "GetData: Reading next DAQ Packet";
124 0 : packet = ReadNextDAQDMA(0);
125 0 : if (packet == nullptr) // End of Data
126 : {
127 0 : DTC_TLOG(TLVL_GetData) << "GetData: Next packet is nullptr; we're done";
128 0 : done = true;
129 0 : daqDMAInfo_.currentReadPtr = nullptr;
130 : }
131 0 : else if (packet->GetEventWindowTag() != when)
132 : {
133 0 : DTC_TLOG(TLVL_GetData) << "GetData: Next packet has ts=0x" << std::hex << packet->GetEventWindowTag().GetEventWindowTag(true)
134 0 : << ", not 0x" << std::hex << when.GetEventWindowTag(true) << "; we're done";
135 0 : done = true;
136 0 : daqDMAInfo_.currentReadPtr = daqDMAInfo_.lastReadPtr;
137 : }
138 : else
139 : {
140 0 : DTC_TLOG(TLVL_GetData) << "GetData: Next packet has same ts=0x" << std::hex
141 0 : << packet->GetEventWindowTag().GetEventWindowTag(true) << ", continuing (bc=0x" << std::hex
142 0 : << packet->GetEventByteCount() << ")";
143 : }
144 :
145 0 : if (!done)
146 : {
147 0 : DTC_TLOG(TLVL_GetData) << "GetData: Adding pointer " << (void*)daqDMAInfo_.lastReadPtr << " to the list";
148 0 : output.push_back(std::move(packet));
149 : }
150 : }
151 : }
152 0 : catch (DTC_WrongPacketTypeException& ex)
153 : {
154 0 : DTC_TLOG(TLVL_WARNING) << "GetData: Bad omen: Wrong packet type at the current read position";
155 0 : daqDMAInfo_.currentReadPtr = nullptr;
156 0 : }
157 0 : catch (DTC_IOErrorException& ex)
158 : {
159 0 : daqDMAInfo_.currentReadPtr = nullptr;
160 0 : DTC_TLOG(TLVL_WARNING) << "GetData: IO Exception Occurred!";
161 0 : }
162 0 : catch (DTC_DataCorruptionException& ex)
163 : {
164 0 : daqDMAInfo_.currentReadPtr = nullptr;
165 0 : DTC_TLOG(TLVL_WARNING) << "GetData: Data Corruption Exception Occurred!";
166 0 : }
167 :
168 0 : DTC_TLOG(TLVL_GetData) << "GetData RETURN";
169 0 : return output;
170 0 : } // GetData
171 :
172 : //
173 : // GetSubEventData v3 -- extract subevents into Events from the current data stream
174 0 : std::vector<std::shared_ptr<DTCLib::DTC_Event>> DTCLib::DTC::GetSubEventDataAsEvents(
175 : DTC_EventWindowTag when, bool matchEventWindowTag,
176 : const size_t vectorBundleTarget /* = 1 */, const size_t retries /* = 3 */)
177 : {
178 : (void)when; // not yet used; filtering can be added once basic flow works
179 : (void)matchEventWindowTag; // not yet used; filtering can be added once basic flow works
180 :
181 : // Save up to 8 trailing QWs of the current DMA buffer for diagnostic dumps on later
182 : // exceptions. Takes its inputs as parameters so it doesn't capture variables that
183 : // aren't in scope yet at the lambda's definition site.
184 0 : auto saveBufferTail = [this](const uint8_t* dmaBufferStartPtr, size_t dmaBytes) {
185 0 : const size_t tailStart = (dmaBytes >= 64) ? dmaBytes - 64 : 0;
186 0 : lastBufferTailCount_ = 0;
187 0 : for (size_t i = tailStart; i + 8 <= dmaBytes && lastBufferTailCount_ < 8; i += 8)
188 0 : lastBufferTailQwords_[lastBufferTailCount_++] =
189 0 : *reinterpret_cast<const uint64_t*>(dmaBufferStartPtr + i);
190 0 : };
191 :
192 : // Returns the last good event window tag as a string, or "-1" if unavailable.
193 0 : auto ewtTag = [&]() -> std::string {
194 0 : if (!hasLastGoodSubEventHeader_) return "-1";
195 0 : return std::to_string(lastGoodSubEventHeader_.event_tag_low |
196 0 : (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32));
197 0 : };
198 :
199 : // Returns the ring-buffer slot index of a DMA buffer pointer as a 2-digit string
200 : // (e.g. "07", "42"), or "??" when the pointer is outside the mmap'd region.
201 0 : auto bufferIndexStr = [this](const void* ptr) -> std::string {
202 0 : const int idx = GetDevice()->GetBufferIndex(DTC_DMA_Engine_DAQ, ptr);
203 0 : if (idx < 0) return "??";
204 0 : std::ostringstream oss;
205 0 : oss << std::setw(2) << std::setfill('0') << idx;
206 0 : return oss.str();
207 0 : };
208 :
209 0 : if (needToFinishEvent_)
210 : {
211 0 : auto inProgress = std::move(extractedEvents_.back());
212 0 : extractedEvents_.clear();
213 0 : extractedEvents_.push_back(std::move(inProgress));
214 0 : }
215 : else
216 : {
217 0 : extractedEvents_.clear();
218 0 : currentEventSize_ = 0;
219 0 : subEventByteCount_ = 0;
220 0 : extractedSubeventBytes_ = 0;
221 : }
222 0 : extractedEvents_.reserve(vectorBundleTarget + 1);
223 0 : size_t eventsParsedThisTime = 0;
224 :
225 : // Release the previous buffer NOW — we are done with it.
226 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
227 :
228 : // ------------------------------------------------------------------
229 : do // primary loop to read buffers until target event count is reached or timeout
230 : {
231 0 : DTC_TLOG(TLVL_GetCurrentBuffer) << "GetSubEventData BEGIN [last EWT=" << ewtTag()
232 0 : << "] eventsParsedThisTime=" << eventsParsedThisTime
233 0 : << " totalEventsParsed_=" << totalEventsParsed_;
234 :
235 : // Read next buffer
236 : int sts;
237 : {
238 0 : int retry = retries;
239 : do {
240 0 : sts = ReadBuffer(DTC_DMA_Engine_DAQ, 100 /*ms*/);
241 0 : } while (sts <= 0 && --retry > 0);
242 : }
243 0 : if (sts <= 0)
244 : {
245 0 : DTC_TLOG(TLVL_GetCurrentBuffer) << "GetSubEventData: ReadBuffer returned " << sts << ", no data";
246 : {
247 0 : std::vector<std::shared_ptr<DTC_Event>> result;
248 0 : size_t cc = extractedEvents_.size() - (needToFinishEvent_ ? 1 : 0);
249 0 : result.reserve(cc);
250 0 : for (size_t i = 0; i < cc; ++i)
251 0 : result.push_back(extractedEvents_[i]);
252 0 : return result;
253 0 : }
254 : }
255 :
256 0 : if (lastBufferTailCount_ == 8)
257 : {
258 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: Last dmaBufferIndex #" << lastDMABufferIndex_ << " tail qwords: "
259 0 : << std::hex
260 0 : << lastBufferTailQwords_[0] << " "
261 0 : << lastBufferTailQwords_[1] << " "
262 0 : << lastBufferTailQwords_[2] << " "
263 0 : << lastBufferTailQwords_[3] << " | "
264 0 : << lastBufferTailQwords_[4] << " "
265 0 : << lastBufferTailQwords_[5] << " "
266 0 : << lastBufferTailQwords_[6] << " "
267 0 : << lastBufferTailQwords_[7];
268 : }
269 :
270 : // sts = number of bytes the DMA engine transferred into this buffer
271 0 : const size_t dmaBytes = static_cast<size_t>(sts);
272 :
273 0 : const bool prevBufferWasFull = lastDMABufferWasFull_;
274 0 : const bool prevBufferWasMax = lastDMABufferWasMax_;
275 0 : const size_t MAX_TRANSFER_SIZE = 0xffe9;
276 0 : lastDMABufferWasFull_ = (dmaBytes == MAX_TRANSFER_SIZE);
277 0 : lastDMABufferWasMax_ = (dmaBytes == sizeof(mu2e_databuff_t));
278 :
279 0 : const uint8_t* dmaBufferStartPtr = reinterpret_cast<const uint8_t*>(&daqDMAInfo_.buffer.back()[0]);
280 : {
281 0 : const uint64_t* qw = reinterpret_cast<const uint64_t*>(dmaBufferStartPtr);
282 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: first 8 QWs of dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << ": " << std::hex
283 0 : << qw[0] << " " << qw[1] << " " << qw[2] << " " << qw[3] << " | "
284 0 : << qw[4] << " " << qw[5] << " " << qw[6] << " " << qw[7] << std::dec;
285 : }
286 0 : saveBufferTail(dmaBufferStartPtr, dmaBytes);
287 0 : lastDMABufferIndex_ = bufferIndexStr(dmaBufferStartPtr);
288 0 : if (lastBufferTailCount_ == 8)
289 : {
290 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: This dmaBufferIndex #" << lastDMABufferIndex_ << " tail qwords: "
291 0 : << std::hex
292 0 : << lastBufferTailQwords_[0] << " "
293 0 : << lastBufferTailQwords_[1] << " "
294 0 : << lastBufferTailQwords_[2] << " "
295 0 : << lastBufferTailQwords_[3] << " | "
296 0 : << lastBufferTailQwords_[4] << " "
297 0 : << lastBufferTailQwords_[5] << " "
298 0 : << lastBufferTailQwords_[6] << " "
299 0 : << lastBufferTailQwords_[7];
300 : }
301 : {
302 0 : const uint64_t* qw = reinterpret_cast<const uint64_t*>(dmaBufferStartPtr);
303 0 : if (qw[0] == 0xdeadbeef)
304 : {
305 0 : __SS__ << "GetSubEventData: Detected 0xdeadbeef at start of dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " -- this buffer is likely not filled by DMA yet! Is kernel driver and DTC out of sync? Try sending a finite number (e.g. 2) of events through the system TWICE. If that doesn't work, try resetting the PCIe.\nSpy output follows:\n"
306 0 : << __E__;
307 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */ | (1 << 28) /* to force spy */, ss);
308 0 : __SS_THROW__;
309 : // continue; // skip processing this buffer, try to read the next one
310 0 : }
311 : }
312 :
313 : // The DMA descriptor byte count (sts) is the number of bytes actually filled.
314 : // The last byte is the AXI tlast flag byte when the buffer is not completely full.
315 : // When it IS completely full (dmaBytes == sizeof(mu2e_databuff_t)) there is no tlast byte.
316 0 : const size_t payloadBytes = (dmaBytes < sizeof(mu2e_databuff_t)) ? dmaBytes - 1 : dmaBytes;
317 :
318 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: new buffer dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
319 0 : << " dmaBytes=" << dmaBytes << "(0x" << std::hex << dmaBytes << std::dec << ")"
320 0 : << " bufferTotalBytes=" << payloadBytes << "(0x" << std::hex << payloadBytes << std::dec << ")"
321 0 : << " maxBufferBytes=" << sizeof(mu2e_databuff_t) << "(0x" << std::hex << sizeof(mu2e_databuff_t) << std::dec << ")"
322 0 : << " prevBufferWasFull=" << (prevBufferWasFull ? "true" : "false")
323 0 : << " prevBufferWasMax=" << (prevBufferWasMax ? "true" : "false")
324 0 : << " thisBufferIsFull=" << (lastDMABufferWasFull_ ? "true" : "false")
325 0 : << " thisBufferIsMax=" << (lastDMABufferWasMax_ ? "true" : "false");
326 :
327 0 : if (dmaBytes > sizeof(mu2e_databuff_t))
328 : {
329 0 : __SS__ << "GetSubEventData: Detected overflow transfer size at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
330 0 : << " [0x" << std::hex << dmaBytes << " > sizeof(mu2e_databuff_t)=0x" << sizeof(mu2e_databuff_t) << std::dec
331 0 : << "]\nSpy output follows:\n"
332 0 : << __E__;
333 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | (1 << 28), ss);
334 0 : __SS_THROW__;
335 0 : }
336 :
337 0 : size_t dmaBufferOffset = 0;
338 :
339 0 : if (needToFinishEvent_ && !prevBufferWasMax && payloadBytes >= sizeof(uint64_t))
340 : {
341 0 : const size_t remainingBytes = subEventByteCount_ - extractedSubeventBytes_;
342 0 : const size_t continuationPrefix = static_cast<size_t>(*reinterpret_cast<const uint64_t*>(dmaBufferStartPtr) & 0xFFFFFFULL);
343 0 : if (continuationPrefix == remainingBytes + 8)
344 : {
345 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: continuation prefix validated (final) at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
346 0 : << " prefixVal=0x" << std::hex << continuationPrefix << std::dec
347 0 : << " == remainingBytes(" << remainingBytes << ") + 8"
348 0 : << "; skipping 8-byte prefix";
349 0 : dmaBufferOffset = sizeof(uint64_t);
350 : }
351 0 : else if (continuationPrefix == MAX_TRANSFER_SIZE - 1)
352 : {
353 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: continuation prefix validated (intermediate, another max sub-transfer) at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
354 0 : << " prefixVal=0x" << std::hex << continuationPrefix << std::dec
355 0 : << " == MAX_TRANSFER_SIZE-1=0x" << (MAX_TRANSFER_SIZE - 1) << std::dec
356 0 : << " remainingBytes=" << remainingBytes
357 0 : << "; skipping 8-byte prefix";
358 0 : dmaBufferOffset = sizeof(uint64_t);
359 : }
360 : else
361 : {
362 0 : __SS__ << "GetSubEventData: continuation prefix mismatch at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
363 0 : << " prefixVal=" << continuationPrefix << "(0x" << std::hex << continuationPrefix << std::dec << ")"
364 0 : << " expected " << (remainingBytes + 8) << "(0x" << std::hex << (remainingBytes + 8) << std::dec << ")"
365 0 : << " or MAX_TRANSFER_SIZE-1=0x" << std::hex << (MAX_TRANSFER_SIZE - 1) << std::dec
366 0 : << " remainingBytes=" << remainingBytes
367 0 : << " extractedSubeventBytes_=" << extractedSubeventBytes_ << "/" << subEventByteCount_
368 : << " prevBufferWasFull=" << (prevBufferWasFull ? "true" : "false")
369 0 : << " prevBufferWasMax=" << (prevBufferWasMax ? "true" : "false");
370 0 : __SS_THROW__;
371 0 : }
372 0 : }
373 0 : else if (needToFinishEvent_ && prevBufferWasMax)
374 : {
375 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: raw continuation (prevBufferWasMax) at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr)
376 0 : << " -- no prefix, starting at offset 0";
377 : }
378 :
379 0 : while (dmaBufferOffset < payloadBytes)
380 : {
381 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: top of buffer processing loop, dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " dmaBufferOffset= " << dmaBufferOffset
382 0 : << "/" << payloadBytes
383 0 : << " eventsParsedThisTime=" << eventsParsedThisTime
384 0 : << " totalEventsParsed_=" << totalEventsParsed_
385 0 : << " needToFinishEvent_=" << (needToFinishEvent_ ? "true" : "false")
386 0 : << " subEventHeaderQwsFilled_=" << subEventHeaderQwsFilled_
387 0 : << " extractedSubeventBytes_=" << extractedSubeventBytes_ << "/" << subEventByteCount_;
388 :
389 : // ---------------------------------------------------------------
390 : // Assemble the DTC_SubEventHeader one QW at a time into the standing
391 : // buffer (subEventHeaderBuf_). The header is 6 QWs (48 B); it may
392 : // span DMA buffer boundaries, so state persists in DTC class members.
393 : // As soon as the first 2 QWs are in, we know the event size and can
394 : // emplace_back the DTC_Event. Once all 6 QWs are in, the assembled
395 : // header is memcpy'd into the Event's owned buffer.
396 : // ---------------------------------------------------------------
397 0 : if (currentEventSize_ == 0) // haven't yet determined the event size from the first 2 header QWs
398 : {
399 : // Fill QWs from the current buffer until we have the first 2 (or the buffer runs out)
400 0 : while (subEventHeaderQwsFilled_ < 2 && (payloadBytes - dmaBufferOffset) >= sizeof(uint64_t))
401 : {
402 0 : subEventHeaderBuf_[subEventHeaderQwsFilled_++] =
403 0 : *reinterpret_cast<const uint64_t*>(dmaBufferStartPtr + dmaBufferOffset);
404 0 : dmaBufferOffset += sizeof(uint64_t);
405 : }
406 0 : if (subEventHeaderQwsFilled_ < 2)
407 : {
408 : // Header straddles DMA buffer boundary; resume on next ReadBuffer
409 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: only " << subEventHeaderQwsFilled_
410 0 : << " header QW(s) filled, need 2 before sizing; breaking to fetch next buffer";
411 0 : break;
412 : }
413 :
414 : // QW0: per-sub-transfer framing prefix -> bits[0:23] = transfer byte count (24 bits)
415 : // When == MAX_TRANSFER_SIZE - 1, the event is larger than one sub-transfer (multi-buffer event).
416 : // QW1: DTC_SubEventHeader -> bits[0:24] = inclusive_subevent_byte_count (25 bits)
417 0 : const size_t potentialEventTransferPrefix = static_cast<size_t>(subEventHeaderBuf_[0] & 0xFFFFFFULL);
418 0 : subEventByteCount_ = static_cast<size_t>(subEventHeaderBuf_[1] & 0x1FFFFFFULL);
419 :
420 0 : if (potentialEventTransferPrefix == MAX_TRANSFER_SIZE - 1)
421 : {
422 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: multi-buffer event detected"
423 0 : << " potentialEventTransferPrefix=0x" << std::hex << potentialEventTransferPrefix
424 0 : << " == MAX_TRANSFER_SIZE-1=0x" << (MAX_TRANSFER_SIZE - 1) << std::dec
425 0 : << " subEventByteCount_=" << subEventByteCount_
426 0 : << " (event spans multiple DMA buffers)";
427 : }
428 0 : else if (potentialEventTransferPrefix != subEventByteCount_ + 8)
429 : {
430 0 : __SS__ << "GetSubEventData: Header sanity check FAILED: potentialEventTransferPrefix=" << potentialEventTransferPrefix << "(0x" << std::hex << potentialEventTransferPrefix << std::dec << ")"
431 0 : << " subEventByteCount_=" << subEventByteCount_ << "(0x" << std::hex << subEventByteCount_ << std::dec << ")"
432 0 : << " [expected potentialEventTransferPrefix == 0x" << std::hex << (MAX_TRANSFER_SIZE - 1) << std::dec << " (multi-buffer) or potentialEventTransferPrefix - 8 == subEventByteCount_ (single-buffer), diff="
433 0 : << std::dec << (static_cast<int64_t>(potentialEventTransferPrefix) - 8 - static_cast<int64_t>(subEventByteCount_))
434 0 : << "] at dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " dmaBufferOffset=" << dmaBufferOffset
435 0 : << " payloadBytes=" << payloadBytes
436 0 : << " last EWT=" << ewtTag() << " eventsParsedThisTime=" << eventsParsedThisTime
437 0 : << " totalEventsParsed_=" << totalEventsParsed_;
438 :
439 0 : __SS_THROW__;
440 0 : }
441 :
442 : // currentEventSize_ = destination buffer size: struct EventHeader + full subevent
443 0 : currentEventSize_ = sizeof(DTC_EventHeader) + subEventByteCount_;
444 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: sized event, potentialEventTransferPrefix=" << potentialEventTransferPrefix << "(0x" << std::hex << potentialEventTransferPrefix << std::dec << ")"
445 0 : << " subEventByteCount_=" << subEventByteCount_ << "(0x" << std::hex << subEventByteCount_ << std::dec << ")"
446 0 : << " currentEventSize_=" << currentEventSize_ << "(0x" << std::hex << currentEventSize_ << std::dec << ")"
447 0 : << " at dmaBufferOffset=" << dmaBufferOffset;
448 : }
449 :
450 : // fill subevent header cache
451 0 : if (!needToFinishEvent_ && currentEventSize_ > 0 && subEventHeaderQwsFilled_ < kSubEventHeaderQws)
452 : {
453 : // We have a partially filled header from the previous buffer(s); try to fill the rest before doing anything else
454 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: Continuing to fill subevent header, currently have "
455 0 : << subEventHeaderQwsFilled_ << "/" << kSubEventHeaderQws << " QWs filled";
456 0 : while (subEventHeaderQwsFilled_ < kSubEventHeaderQws && (payloadBytes - dmaBufferOffset) >= sizeof(uint64_t))
457 : {
458 0 : subEventHeaderBuf_[subEventHeaderQwsFilled_++] =
459 0 : *reinterpret_cast<const uint64_t*>(dmaBufferStartPtr + dmaBufferOffset);
460 0 : dmaBufferOffset += sizeof(uint64_t);
461 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: filled QW " << subEventHeaderQwsFilled_ << "/" << kSubEventHeaderQws;
462 : }
463 0 : if (subEventHeaderQwsFilled_ < kSubEventHeaderQws)
464 : {
465 : // Still don't have the full header; wait for the next buffer
466 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: still only have " << subEventHeaderQwsFilled_
467 0 : << " header QW(s) filled, need " << kSubEventHeaderQws
468 0 : << "; breaking to fetch next buffer";
469 0 : break;
470 : }
471 :
472 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: assembled subevent header: "
473 0 : << reinterpret_cast<const DTC_SubEventHeader*>(subEventHeaderBuf_.data() + 1)->toJson();
474 : } // end fill subevent header cache
475 :
476 : // start a new event and finish copying its subevent header
477 0 : if (!needToFinishEvent_ && currentEventSize_ > 0 && subEventHeaderQwsFilled_ == kSubEventHeaderQws)
478 : {
479 0 : extractedEvents_.push_back(std::make_shared<DTC_Event>(currentEventSize_));
480 :
481 0 : DTCLib::DTC_EventHeader evtHdr;
482 0 : evtHdr.inclusive_event_byte_count = currentEventSize_;
483 0 : evtHdr.num_dtcs = 1;
484 0 : evtHdr.event_tag_low = reinterpret_cast<const DTC_SubEventHeader*>(subEventHeaderBuf_.data() + 1)->event_tag_low;
485 0 : evtHdr.event_tag_high = reinterpret_cast<const DTC_SubEventHeader*>(subEventHeaderBuf_.data() + 1)->event_tag_high;
486 0 : uint8_t* eventBuf = static_cast<uint8_t*>(const_cast<void*>(extractedEvents_.back()->GetRawBufferPointer()));
487 0 : memcpy(eventBuf, &evtHdr, sizeof(DTCLib::DTC_EventHeader));
488 0 : *extractedEvents_.back()->GetHeader() = evtHdr;
489 0 : extractedSubeventBytes_ = 0;
490 :
491 0 : memcpy(eventBuf + sizeof(DTCLib::DTC_EventHeader) + extractedSubeventBytes_, subEventHeaderBuf_.data() + 1, sizeof(DTC_SubEventHeader));
492 0 : extractedSubeventBytes_ += sizeof(DTC_SubEventHeader);
493 :
494 0 : subEventHeaderQwsFilled_ = 0;
495 0 : needToFinishEvent_ = true;
496 :
497 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: copied " << sizeof(DTC_SubEventHeader)
498 0 : << "-byte subevent header from dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " into Event vector position #" << extractedEvents_.size()
499 0 : << " EWT=" << extractedEvents_.back()->GetEventWindowTag() << "(0x" << std::hex << extractedEvents_.back()->GetEventWindowTag() << std::dec << ")"
500 0 : << " (extractedSubeventBytes_=" << extractedSubeventBytes_
501 0 : << "/" << subEventByteCount_ << ")";
502 : }
503 :
504 0 : if (needToFinishEvent_)
505 : {
506 0 : size_t bytesToCopy = std::min(payloadBytes - dmaBufferOffset, subEventByteCount_ - extractedSubeventBytes_);
507 :
508 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: copying " << bytesToCopy << " bytes from dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " into Event vector position #" << extractedEvents_.size()
509 0 : << " EWT=" << extractedEvents_.back()->GetEventWindowTag() << "(0x" << std::hex << extractedEvents_.back()->GetEventWindowTag() << std::dec << ")"
510 0 : << " (before extractedSubeventBytes_=" << extractedSubeventBytes_
511 0 : << "/" << subEventByteCount_ << ")";
512 :
513 0 : uint8_t* destPtr = static_cast<uint8_t*>(const_cast<void*>(extractedEvents_.back()->GetRawBufferPointer())) + sizeof(DTC_EventHeader) + extractedSubeventBytes_;
514 0 : memcpy(destPtr, dmaBufferStartPtr + dmaBufferOffset, bytesToCopy);
515 0 : dmaBufferOffset += bytesToCopy;
516 0 : extractedSubeventBytes_ += bytesToCopy;
517 :
518 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: copied " << bytesToCopy
519 0 : << "-bytes of subevent payload from dmaBufferIndex #" << bufferIndexStr(dmaBufferStartPtr) << " into Event vector position #" << extractedEvents_.size()
520 0 : << " EWT=" << extractedEvents_.back()->GetEventWindowTag() << "(0x" << std::hex << extractedEvents_.back()->GetEventWindowTag() << std::dec << ")"
521 0 : << " (extractedSubeventBytes_=" << extractedSubeventBytes_
522 0 : << "/" << subEventByteCount_ << ")";
523 :
524 0 : if (extractedSubeventBytes_ == subEventByteCount_)
525 : {
526 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: finished assembling Event vector position #" << extractedEvents_.size()
527 0 : << " EWT=" << extractedEvents_.back()->GetEventWindowTag() << "(0x" << std::hex << extractedEvents_.back()->GetEventWindowTag() << std::dec << ")"
528 0 : << " (total extractedSubeventBytes_=" << extractedSubeventBytes_
529 0 : << "/" << subEventByteCount_ << ", eventBytes=" << currentEventSize_ << "); resetting for next event"
530 0 : << " eventsParsedThisTime=" << eventsParsedThisTime
531 0 : << " totalEventsParsed_=" << totalEventsParsed_;
532 0 : currentEventSize_ = 0;
533 0 : subEventByteCount_ = 0;
534 0 : extractedSubeventBytes_ = 0;
535 0 : needToFinishEvent_ = false;
536 0 : eventsParsedThisTime++;
537 0 : totalEventsParsed_++;
538 0 : subEventHeaderQwsFilled_ = 0;
539 :
540 0 : extractedEvents_.back()->SetupEvent();
541 : }
542 : else
543 : {
544 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData: still need to copy "
545 0 : << (subEventByteCount_ - extractedSubeventBytes_)
546 0 : << " bytes to finish Event vector position #" << extractedEvents_.size()
547 0 : << " EWT=" << extractedEvents_.back()->GetEventWindowTag() << "(0x" << std::hex << extractedEvents_.back()->GetEventWindowTag() << std::dec << ")"
548 0 : << "; breaking to fetch next buffer";
549 0 : break;
550 : }
551 : }
552 : } // end buffer processing loop
553 :
554 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
555 :
556 0 : } while (eventsParsedThisTime < vectorBundleTarget);
557 : // ------------------------------------------------------------------
558 :
559 0 : size_t completedCount = extractedEvents_.size() - (needToFinishEvent_ ? 1 : 0);
560 0 : DTC_TLOG(TLVL_GetData_3) << "GetSubEventData END [last EWT=" << ewtTag()
561 0 : << "] eventsParsedThisTime=" << eventsParsedThisTime
562 0 : << " totalEventsParsed_=" << totalEventsParsed_
563 0 : << " completedCount=" << completedCount
564 0 : << " needToFinishEvent_=" << (needToFinishEvent_ ? "true" : "false");
565 :
566 0 : std::vector<std::shared_ptr<DTC_Event>> result;
567 0 : result.reserve(completedCount);
568 0 : for (size_t i = 0; i < completedCount; ++i)
569 0 : result.push_back(extractedEvents_[i]);
570 0 : return result;
571 0 : } // end GetSubEventDataAsEvents()
572 :
573 : // ---------------------------------------------------------------------------
574 : // GetEVBDataAsEvents -- FAFA chunk parser for EVB3 DMA stream
575 : // ---------------------------------------------------------------------------
576 : // Reads one DMA buffer per call, parses FAFA chunk framing, accumulates
577 : // per-source reassembly buffers, and extracts complete subevents.
578 : // Each complete subevent is wrapped in a DTC_Event (with a synthetic
579 : // DTC_EventHeader) and returned. Incomplete subevents (remote chunk
580 : // fragmentation) remain in evbPerSourceReassembly_ for the next call.
581 : // ---------------------------------------------------------------------------
582 0 : std::vector<std::shared_ptr<DTCLib::DTC_Event>> DTCLib::DTC::GetEVBDataAsEvents(
583 : DTC_EventWindowTag when, bool matchEventWindowTag, const size_t retries)
584 : {
585 : (void)when; // reserved for future EWT filtering
586 : (void)matchEventWindowTag;
587 0 : std::vector<std::shared_ptr<DTC_Event>> output;
588 :
589 : // Step profile: one steady_clock read per step boundary; totals are reported by FormatEVBReadProfile().
590 : using ProfileClock = std::chrono::steady_clock;
591 0 : const ProfileClock::time_point callStart = ProfileClock::now();
592 0 : ProfileClock::time_point lapStart = callStart;
593 0 : auto lap = [&lapStart](uint64_t& accumulator) {
594 0 : const ProfileClock::time_point now = ProfileClock::now();
595 0 : accumulator += static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(now - lapStart).count());
596 0 : lapStart = now;
597 0 : };
598 0 : ++evbReadProfile_.calls;
599 : struct ProfileTotal
600 : {
601 : uint64_t& total;
602 : ProfileClock::time_point start;
603 0 : ~ProfileTotal() { total += static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(ProfileClock::now() - start).count()); }
604 0 : } profileTotal{evbReadProfile_.totalNs, callStart};
605 :
606 : static const size_t RECORD_HEADER_SIZE = sizeof(uint64_t); // 8 bytes: firmware record header word
607 :
608 : // Hex-dump a record / subevent as 64-bit words in DMA order, 4 per line
609 0 : auto dumpRecord = [](std::ostream& os, const char* label, uint8_t src, const uint8_t* data, size_t bytes) {
610 0 : os << label << " src=0x" << std::hex << static_cast<int>(src) << std::dec
611 0 : << " (" << bytes << " bytes, " << bytes / sizeof(uint64_t) << " words):\n";
612 0 : const uint64_t* w = reinterpret_cast<const uint64_t*>(data);
613 0 : size_t nw = bytes / sizeof(uint64_t);
614 0 : for (size_t i = 0; i < nw; ++i)
615 : {
616 0 : if (i % 4 == 0) os << " +" << std::dec << std::setw(3) << (i * 8) << ":";
617 0 : os << " " << std::hex << std::setw(16) << std::setfill('0') << w[i] << std::setfill(' ');
618 0 : if (i % 4 == 3 || i + 1 == nw) os << "\n";
619 : }
620 0 : os << std::dec;
621 0 : };
622 :
623 : // Describe a pending (incomplete) tag: which sources are present, dump each staged subevent
624 0 : auto describePending = [&](std::ostream& os, uint64_t tag, const EVBPendingTag& pend) {
625 0 : os << " EWT=" << tag << ": " << pend.subevents.size() << " of " << static_cast<int>(evbNumSources_)
626 0 : << " sources present, age " << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - pend.firstArrival).count()
627 0 : << " ms. Present source_dtc_id:";
628 0 : for (auto& [sid, bytes] : pend.subevents) os << " 0x" << std::hex << static_cast<int>(sid) << std::dec;
629 0 : os << ". Sources seen this run (any tag):";
630 0 : for (auto& [sid, buf] : evbPerSourceReassembly_) os << " 0x" << std::hex << static_cast<int>(sid) << std::dec;
631 0 : os << "\n";
632 0 : for (auto& [sid, bytes] : pend.subevents)
633 0 : dumpRecord(os, " staged subevent (no record header)", sid, bytes.data(), bytes.size());
634 0 : };
635 :
636 0 : auto dumpEVBErrorStatus = [&](std::ostream& os) {
637 0 : uint32_t evbErr = ReadRegister_(DTC_Register_EventBuilderErrorFlags);
638 0 : os << "EVB Error/Status (0x9370) at abort: 0x" << std::hex << std::setw(8) << std::setfill('0') << evbErr << std::dec << std::setfill(' ') << "\n";
639 0 : for (const auto& line : DecodeEVBErrorStatus(evbErr))
640 0 : os << " " << line << "\n";
641 0 : };
642 :
643 : // Step 0: event-completion timeout / overflow scan. Runs on EVERY call, including calls
644 : // where no DMA data arrives, so a tag whose remaining subevents never come is reported.
645 0 : if (!evbPendingTags_.empty())
646 : {
647 0 : auto now = std::chrono::steady_clock::now();
648 0 : bool overflow = evbPendingTags_.size() > evbMaxOpenTags_;
649 0 : for (auto& [tag, pend] : evbPendingTags_)
650 : {
651 0 : bool timedOut = (now - pend.firstArrival) > evbEventTimeout_;
652 0 : if (!timedOut && !overflow) continue;
653 0 : std::stringstream ss;
654 : ss << "GetEVBDataAsEvents: event assembly " << (timedOut ? "TIMEOUT" : "OVERFLOW")
655 0 : << " -- EWT=" << tag << " has waited " << std::chrono::duration_cast<std::chrono::milliseconds>(now - pend.firstArrival).count()
656 0 : << " ms since its first subevent (limit " << evbEventTimeout_.count() << " ms), "
657 0 : << pend.subevents.size() << " of " << static_cast<int>(evbNumSources_) << " sources present; open tags=" << evbPendingTags_.size()
658 0 : << " (max " << evbMaxOpenTags_ << "). Stopping EVB DMA parsing.\n";
659 0 : describePending(ss, tag, pend);
660 0 : dumpEVBErrorStatus(ss);
661 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
662 0 : ++evbFramingErrors_;
663 0 : throw std::runtime_error(ss.str());
664 0 : }
665 : }
666 :
667 0 : lap(evbReadProfile_.timeoutScanNs);
668 :
669 : // Step 1: read one DMA buffer directly from hardware
670 0 : void* buffer = nullptr;
671 0 : int dmaBytes = device_.read_data(DTC_DMA_Engine_DAQ, &buffer, 1 /*tmo_ms*/);
672 0 : lap(evbReadProfile_.readDataNs);
673 :
674 0 : if (dmaBytes <= 0 || buffer == nullptr)
675 : {
676 0 : if (dmaBytes < 0)
677 0 : DTC_TLOG(TLVL_ERROR) << "GetEVBDataAsEvents: read_data error " << dmaBytes;
678 0 : return output;
679 : }
680 0 : ++evbReadProfile_.callsWithData;
681 :
682 : // DMA buffer lifetime: this one buffer is released when the function exits by ANY path
683 : // (normal return or a throw from any check below). Step 3 copies every chunk payload out
684 : // of the buffer into host memory; nothing after that holds a pointer into the DMA ring.
685 : // Event completion therefore never depends on holding DMA buffers -- an event whose
686 : // subevents span hundreds of DMA transfers costs host memory only.
687 : struct DmaRelease
688 : {
689 : decltype(device_)& d;
690 : uint64_t& releaseNs;
691 0 : ~DmaRelease()
692 : {
693 0 : const ProfileClock::time_point releaseStart = ProfileClock::now();
694 : try
695 : {
696 0 : d.read_release(DTC_DMA_Engine_DAQ, 1);
697 : }
698 0 : catch (...)
699 0 : {}
700 0 : releaseNs += static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(ProfileClock::now() - releaseStart).count());
701 0 : }
702 0 : } dmaRelease{device_, evbReadProfile_.releaseBufferNs};
703 :
704 : // Step 2: compute usable payload (strip tlast byte if short DMA)
705 0 : size_t payloadBytes = static_cast<size_t>(dmaBytes);
706 0 : if (payloadBytes < sizeof(mu2e_databuff_t))
707 0 : payloadBytes -= 1; // tlast marker byte
708 0 : size_t payloadWords = payloadBytes / sizeof(uint64_t);
709 0 : const uint64_t* words = static_cast<const uint64_t*>(buffer);
710 0 : ++evbDMABuffersRead_;
711 :
712 : // Step 2b: expected number of source DTCs per tag (= EVB destination-node count) and this DTC's
713 : // MAC, read once per run (first data buffer after ResetEVBAssembly); both are constant for a run
714 : // and the two register reads per buffer they used to cost are now gone from the data path.
715 0 : if (!evbSourcesKnown_)
716 : {
717 0 : uint8_t n = ReadEVBNumberOfDestinationNodes();
718 0 : evbNumSources_ = (n == 0) ? 1 : n;
719 0 : evbLocalMac_ = ReadEVBLocalMACAddress();
720 0 : evbSourcesKnown_ = true;
721 : }
722 0 : lap(evbReadProfile_.registerReadNs);
723 :
724 : // Step 3: FAFA chunk walk
725 0 : size_t ptr = 0;
726 0 : std::vector<std::tuple<size_t, uint8_t, uint16_t>> chunksSeenInThisBuffer;
727 0 : while (ptr < payloadWords)
728 : {
729 0 : uint64_t w = words[ptr];
730 0 : if ((w >> 48) == 0xFAFA)
731 : {
732 0 : uint16_t chunk_wc = static_cast<uint16_t>((w >> 8) & 0xFFFF);
733 0 : uint8_t chunk_src = static_cast<uint8_t>(w & 0xFF);
734 :
735 0 : if (ptr + 1 + chunk_wc > payloadWords)
736 : {
737 : // A chunk that does not fit its DMA buffer never happens in normal running (hw
738 : // agent 2026-10-05: a chunk never straddles a DMA close); it is the aftermath of
739 : // a TX disable or reset mid-run, or a firmware fault. Report everything that
740 : // locates it and stop, rather than silently dropping the chunk and continuing.
741 0 : std::stringstream ss;
742 0 : ss << "GetEVBDataAsEvents: FAFA framing error at word " << ptr << " of " << payloadWords
743 0 : << " (DMA " << dmaBytes << " bytes): chunk header 0x" << std::hex << std::setw(16) << std::setfill('0') << w << std::setfill(' ') << std::dec
744 0 : << " = src 0x" << std::hex << static_cast<int>(chunk_src) << std::dec << ", chunk_wc=" << chunk_wc
745 0 : << " exceeds remaining " << (payloadWords - ptr - 1) << " words (short by " << (ptr + 1 + chunk_wc - payloadWords)
746 0 : << "). Chunks already taken from this buffer (word:src/wc):";
747 0 : if (chunksSeenInThisBuffer.empty())
748 0 : ss << " none";
749 : else
750 0 : for (const auto& [chunkWord, chunkSrc, chunkWordCount] : chunksSeenInThisBuffer)
751 0 : ss << " " << chunkWord << ":0x" << std::hex << static_cast<int>(chunkSrc) << std::dec << "/" << chunkWordCount;
752 : ss
753 0 : << ". Last released EWT=" << (evbHaveReleasedTag_ ? std::to_string(evbLastReleasedTag_) : std::string("none")) << " eventsReleased=" << evbEventsReleased_ << " openTags=" << evbPendingTags_.size()
754 0 : << "; reassembly bytes pending per source:";
755 0 : for (const auto& [sourceId, sourceBuffer] : evbPerSourceReassembly_)
756 0 : ss << " 0x" << std::hex << static_cast<int>(sourceId) << std::dec << "=" << sourceBuffer.size();
757 0 : ss << ".\n";
758 0 : const size_t dumpFromWord = ptr >= 16 ? ptr - 16 : 0;
759 0 : const size_t dumpBytes = std::min(static_cast<size_t>(payloadWords - dumpFromWord) * sizeof(uint64_t), static_cast<size_t>(1024));
760 0 : dumpRecord(ss, "DMA buffer from 16 words before the bad chunk header (offsets relative to that point)", chunk_src,
761 0 : reinterpret_cast<const uint8_t*>(&words[dumpFromWord]), dumpBytes);
762 0 : auto lg = evbLastGoodRecord_.find(chunk_src);
763 0 : if (lg != evbLastGoodRecord_.end())
764 0 : dumpRecord(ss, "Last GOOD record (same src, for comparison)", chunk_src, lg->second.data(), lg->second.size());
765 : else
766 0 : ss << "(no previous good record from src=0x" << std::hex << static_cast<int>(chunk_src) << std::dec << ")\n";
767 0 : dumpEVBErrorStatus(ss);
768 0 : device_.resetSpyHasOccurred();
769 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | 16, ss);
770 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
771 0 : ++evbFramingErrors_;
772 0 : throw std::runtime_error(ss.str());
773 0 : }
774 0 : chunksSeenInThisBuffer.emplace_back(ptr, chunk_src, chunk_wc);
775 :
776 0 : size_t chunkBytes = static_cast<size_t>(chunk_wc) * sizeof(uint64_t);
777 0 : evbDrainedBytes_ += chunkBytes;
778 0 : if (!evbDrainOnly_)
779 : {
780 0 : auto& srcBuf = evbPerSourceReassembly_[chunk_src];
781 0 : const uint8_t* chunkData = reinterpret_cast<const uint8_t*>(&words[ptr + 1]);
782 0 : srcBuf.insert(srcBuf.end(), chunkData, chunkData + chunkBytes);
783 : }
784 :
785 0 : ++evbChunksParsed_;
786 0 : if (chunk_src != evbLocalMac_ && chunk_wc < evbRemoteChunkSizeHistogram_.size())
787 0 : ++evbRemoteChunkSizeHistogram_[chunk_wc];
788 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: FAFA chunk src=0x" << std::hex << static_cast<int>(chunk_src)
789 0 : << " wc=" << std::dec << chunk_wc << " words";
790 0 : ptr += 1 + chunk_wc;
791 : }
792 : else
793 : {
794 : // close filler or end of data
795 0 : break;
796 : }
797 : }
798 :
799 0 : lap(evbReadProfile_.chunkWalkNs);
800 :
801 : // Drain-only: the DMA buffer is released on return; nothing else is done with the data
802 0 : if (evbDrainOnly_)
803 0 : return output;
804 :
805 : // Step 4: extract complete subevents from each source's reassembly buffer
806 : // Each subevent (local and remote) is preceded by an 8-byte firmware
807 : // record header word (bits [15:0] = total record byte count including
808 : // this word). The AXI demux routes the full ROC transfer to both the
809 : // local buffer manager and the DDR->10GbE path, so the record header
810 : // is present in both. Skip it to reach the DTC_SubEventHeader.
811 0 : for (auto& [src, srcBuf] : evbPerSourceReassembly_)
812 : {
813 0 : while (srcBuf.size() >= RECORD_HEADER_SIZE + sizeof(DTC_SubEventHeader))
814 : {
815 0 : const auto* subHdr = reinterpret_cast<const DTC_SubEventHeader*>(srcBuf.data() + RECORD_HEADER_SIZE);
816 0 : size_t subEvtByteCount = subHdr->inclusive_subevent_byte_count;
817 :
818 0 : if (subEvtByteCount < sizeof(DTC_SubEventHeader))
819 : {
820 0 : DTC_TLOG(TLVL_ERROR) << "GetEVBDataAsEvents: invalid inclusive_subevent_byte_count=" << subEvtByteCount
821 0 : << " from source " << static_cast<int>(src) << "; clearing source buffer";
822 0 : ++evbFramingErrors_;
823 0 : srcBuf.clear();
824 0 : break;
825 : }
826 :
827 0 : size_t totalRecordBytes = RECORD_HEADER_SIZE + subEvtByteCount;
828 :
829 : // Split records (EVB3_DMA_FAFA_protocol.md "Split-record layout", hw agent 2026-10-05):
830 : // a subevent larger than one AXIMux DMA transfer (~64 KB) arrives as several records.
831 : // The first carries the subevent header; each continuation record is only its own
832 : // 8-byte count word (bytes incl. itself, low nibble 8) followed by the next raw words.
833 : // Payloads (record bytes - 8) must add up exactly to the inclusive count. Wait until
834 : // every piece has arrived, then remove the continuation count words so the subevent
835 : // is contiguous behind the first count word.
836 0 : bool splitRecordJoined = false;
837 : {
838 0 : const uint64_t firstCountWord = *reinterpret_cast<const uint64_t*>(srcBuf.data());
839 0 : const size_t firstRecordBytes = static_cast<size_t>(firstCountWord & 0xFFFF);
840 0 : if ((firstRecordBytes & 0xF) == 0x8 && firstRecordBytes > RECORD_HEADER_SIZE + sizeof(DTC_SubEventHeader) &&
841 0 : firstRecordBytes < totalRecordBytes)
842 : {
843 0 : std::vector<size_t> continuationCountWordOffsets;
844 0 : size_t payloadBytesCollected = firstRecordBytes - RECORD_HEADER_SIZE;
845 0 : size_t nextRecordOffset = firstRecordBytes;
846 0 : bool allPiecesPresent = true;
847 0 : std::string splitError;
848 0 : while (payloadBytesCollected < subEvtByteCount)
849 : {
850 0 : if (srcBuf.size() < nextRecordOffset + RECORD_HEADER_SIZE)
851 : {
852 0 : allPiecesPresent = false;
853 0 : break;
854 : }
855 0 : const uint64_t continuationCountWord = *reinterpret_cast<const uint64_t*>(srcBuf.data() + nextRecordOffset);
856 0 : const size_t continuationRecordBytes = static_cast<size_t>(continuationCountWord & 0xFFFF);
857 0 : const size_t continuationPayloadBytes = continuationRecordBytes - RECORD_HEADER_SIZE;
858 0 : if ((continuationRecordBytes & 0xF) != 0x8 || continuationRecordBytes <= RECORD_HEADER_SIZE)
859 : {
860 0 : std::stringstream reason;
861 0 : reason << "continuation count word at byte " << nextRecordOffset << " = 0x" << std::hex << std::setw(16) << std::setfill('0')
862 0 : << continuationCountWord << std::setfill(' ') << std::dec << " is not a count quadword (low nibble must be 8)";
863 0 : splitError = reason.str();
864 0 : break;
865 0 : }
866 0 : if (payloadBytesCollected + continuationPayloadBytes > subEvtByteCount)
867 : {
868 0 : std::stringstream reason;
869 0 : reason << "continuation record at byte " << nextRecordOffset << " (" << continuationRecordBytes << " bytes) overshoots: "
870 0 : << payloadBytesCollected << " + " << continuationPayloadBytes << " > " << subEvtByteCount;
871 0 : splitError = reason.str();
872 0 : break;
873 0 : }
874 0 : if (srcBuf.size() < nextRecordOffset + continuationRecordBytes)
875 : {
876 0 : allPiecesPresent = false;
877 0 : break;
878 : }
879 0 : continuationCountWordOffsets.push_back(nextRecordOffset);
880 0 : payloadBytesCollected += continuationPayloadBytes;
881 0 : nextRecordOffset += continuationRecordBytes;
882 : }
883 :
884 0 : if (!splitError.empty())
885 : {
886 0 : std::stringstream ss;
887 0 : ss << "GetEVBDataAsEvents: split subevent from src=0x" << std::hex << static_cast<int>(src) << std::dec
888 0 : << " cannot be reassembled: first record " << firstRecordBytes << " bytes, subevent inclusive count " << subEvtByteCount
889 0 : << "; " << splitError << "; tag_low=" << subHdr->event_tag_low << ".\n";
890 0 : dumpRecord(ss, "BAD split record (reassembly buffer head)", src, srcBuf.data(), std::min(srcBuf.size(), static_cast<size_t>(1024)));
891 0 : if (nextRecordOffset < srcBuf.size())
892 0 : dumpRecord(ss, "at the bad continuation", src, srcBuf.data() + nextRecordOffset,
893 0 : std::min(srcBuf.size() - nextRecordOffset, static_cast<size_t>(256)));
894 0 : dumpEVBErrorStatus(ss);
895 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
896 0 : ++evbFramingErrors_;
897 0 : srcBuf.clear();
898 0 : throw std::runtime_error(ss.str());
899 0 : }
900 0 : if (!allPiecesPresent)
901 : {
902 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: src=0x" << std::hex << static_cast<int>(src) << std::dec
903 0 : << " split subevent incomplete: have " << payloadBytesCollected << " of " << subEvtByteCount
904 0 : << " payload bytes in " << (continuationCountWordOffsets.size() + 1) << " record(s); waiting for more chunks";
905 0 : break;
906 : }
907 : // remove the continuation count words, last first so earlier offsets stay valid
908 0 : for (auto offsetIterator = continuationCountWordOffsets.rbegin(); offsetIterator != continuationCountWordOffsets.rend(); ++offsetIterator)
909 0 : srcBuf.erase(srcBuf.begin() + *offsetIterator, srcBuf.begin() + *offsetIterator + RECORD_HEADER_SIZE);
910 0 : splitRecordJoined = true;
911 0 : ++evbSplitSubeventsJoined_;
912 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: src=0x" << std::hex << static_cast<int>(src) << std::dec << " joined split subevent tag_low="
913 0 : << subHdr->event_tag_low << " from " << (continuationCountWordOffsets.size() + 1) << " records, " << subEvtByteCount << " bytes";
914 0 : }
915 : }
916 :
917 : // Header consistency BEFORE waiting for more data: the observed firmware record
918 : // header word bits[15:0] = total record bytes = subevent inclusive count + 8. If a
919 : // word-replacement corruption lands on either size field, the two disagree; without
920 : // this check a too-large count would silently "wait for more chunks" forever.
921 : {
922 0 : uint64_t recHdrWord = *reinterpret_cast<const uint64_t*>(srcBuf.data());
923 0 : size_t recHdrBytes = static_cast<size_t>(recHdrWord & 0xFFFF);
924 0 : bool recHdrOK = splitRecordJoined || (recHdrBytes == totalRecordBytes);
925 0 : bool numRocsOK = (subHdr->num_rocs >= 1 && subHdr->num_rocs <= 6);
926 0 : if (!recHdrOK || !numRocsOK)
927 : {
928 0 : std::stringstream ss;
929 0 : ss << "GetEVBDataAsEvents: record header inconsistent from src=0x" << std::hex << static_cast<int>(src) << std::dec
930 0 : << ": record header word=0x" << std::hex << std::setw(16) << std::setfill('0') << recHdrWord << std::setfill(' ') << std::dec
931 0 : << " -> " << recHdrBytes << " bytes, but subevent inclusive count=" << subEvtByteCount
932 0 : << " (+8 = " << totalRecordBytes << ")" << (recHdrOK ? "" : " MISMATCH")
933 0 : << "; tag_low=" << subHdr->event_tag_low << " tag_high=0x" << std::hex << subHdr->event_tag_high << std::dec
934 0 : << " num_rocs=" << subHdr->num_rocs << (numRocsOK ? "" : " (INVALID, expect 1..6)")
935 0 : << ". Aborting rather than waiting forever for a corrupt-sized record.\n";
936 0 : size_t dumpBytes = std::min(srcBuf.size(), static_cast<size_t>(1024));
937 0 : dumpRecord(ss, "BAD record (reassembly buffer head)", src, srcBuf.data(), dumpBytes);
938 0 : auto lg = evbLastGoodRecord_.find(src);
939 0 : if (lg != evbLastGoodRecord_.end())
940 0 : dumpRecord(ss, "Last GOOD record (same src, for comparison)", src, lg->second.data(), lg->second.size());
941 : else
942 0 : ss << "(no previous good record from src=0x" << std::hex << static_cast<int>(src) << std::dec << ")\n";
943 0 : dumpEVBErrorStatus(ss);
944 0 : device_.resetSpyHasOccurred();
945 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | 16, ss);
946 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
947 0 : ++evbFramingErrors_;
948 0 : srcBuf.clear();
949 0 : throw std::runtime_error(ss.str());
950 0 : }
951 : }
952 :
953 0 : if (srcBuf.size() < totalRecordBytes)
954 : {
955 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: src=0x" << std::hex << static_cast<int>(src) << std::dec
956 0 : << " record incomplete: have " << srcBuf.size() << " of " << totalRecordBytes << " bytes; waiting for more chunks";
957 0 : break; // incomplete subevent, wait for more chunks
958 : }
959 0 : lap(evbReadProfile_.recordScanNs);
960 :
961 0 : size_t eventSize = sizeof(DTC_EventHeader) + subEvtByteCount;
962 0 : auto event = std::make_shared<DTC_Event>(eventSize);
963 :
964 0 : DTC_EventHeader evtHdr{};
965 0 : evtHdr.inclusive_event_byte_count = eventSize;
966 0 : evtHdr.num_dtcs = 1;
967 0 : evtHdr.event_tag_low = subHdr->event_tag_low;
968 0 : evtHdr.event_tag_high = subHdr->event_tag_high;
969 :
970 0 : if (subHdr->subevent_format_version != CURRENT_SUBEVENT_FORMAT_VERSION)
971 : {
972 0 : std::stringstream ss;
973 0 : ss << "GetEVBDataAsEvents: subevent_format_version mismatch: 0x"
974 0 : << std::hex << subHdr->subevent_format_version
975 0 : << " != expected 0x" << static_cast<uint16_t>(CURRENT_SUBEVENT_FORMAT_VERSION)
976 0 : << ". Check that your DTC FPGA version matches the software expectation.\n";
977 0 : dumpRecord(ss, "BAD record", src, srcBuf.data(), totalRecordBytes);
978 0 : dumpEVBErrorStatus(ss);
979 0 : device_.resetSpyHasOccurred();
980 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | 16, ss);
981 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
982 0 : ++evbFramingErrors_;
983 0 : srcBuf.erase(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
984 0 : throw std::runtime_error(ss.str());
985 0 : }
986 :
987 : // Guard: DTC_SubEvent::SetupSubEvent() never returns if the FIRST ROC data header
988 : // (subevent offset 48) is not a DataHeader packet -- its while-loop catches the
989 : // constructor exception without advancing byte_count and spins forever, flooding
990 : // the log. Pre-check with the same test SetupSubEvent uses, and throw with a dump.
991 0 : if (subEvtByteCount >= sizeof(DTC_SubEventHeader) + 16)
992 : {
993 0 : const uint8_t* roc0Hdr = srcBuf.data() + RECORD_HEADER_SIZE + sizeof(DTC_SubEventHeader);
994 0 : DTC_EventWindowTag tag(static_cast<uint32_t>(subHdr->event_tag_low), static_cast<uint16_t>(subHdr->event_tag_high));
995 0 : bool hdrOK = DTC_DataHeaderPacket::IsDataHeaderPacket(roc0Hdr, tag);
996 0 : if (!hdrOK)
997 : {
998 0 : std::stringstream ss;
999 0 : ss << "GetEVBDataAsEvents: record failed pre-parse validation from src=0x" << std::hex << static_cast<int>(src) << std::dec
1000 0 : << ": subevent header tag_low=" << subHdr->event_tag_low
1001 0 : << " tag_high=0x" << std::hex << subHdr->event_tag_high << std::dec
1002 0 : << " num_rocs=" << subHdr->num_rocs
1003 0 : << "; first ROC data header word (offset 48) = 0x" << std::hex << std::setw(16) << std::setfill('0')
1004 0 : << *reinterpret_cast<const uint64_t*>(roc0Hdr) << std::setfill(' ') << std::dec
1005 : << (hdrOK ? " (valid)" : " (FAILED IsDataHeaderPacket check against the header's tag)")
1006 0 : << ". A corrupt header makes SetupSubEvent loop forever; aborting.\n";
1007 0 : dumpRecord(ss, "BAD record", src, srcBuf.data(), totalRecordBytes);
1008 0 : auto lg = evbLastGoodRecord_.find(src);
1009 0 : if (lg != evbLastGoodRecord_.end())
1010 0 : dumpRecord(ss, "Last GOOD record (same src, for comparison)", src, lg->second.data(), lg->second.size());
1011 : else
1012 0 : ss << "(no previous good record from src=0x" << std::hex << static_cast<int>(src) << std::dec << ")\n";
1013 0 : dumpEVBErrorStatus(ss);
1014 0 : device_.resetSpyHasOccurred();
1015 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | 16, ss);
1016 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1017 0 : ++evbFramingErrors_;
1018 0 : srcBuf.erase(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
1019 0 : throw std::runtime_error(ss.str());
1020 0 : }
1021 0 : }
1022 :
1023 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: parsing subevent src=0x" << std::hex << static_cast<int>(src)
1024 0 : << " EWT=" << std::dec << ((static_cast<uint64_t>(subHdr->event_tag_high) << 32) | subHdr->event_tag_low)
1025 0 : << " bytes=" << subEvtByteCount << " num_rocs=" << subHdr->num_rocs
1026 0 : << " dtc_mac=0x" << std::hex << subHdr->dtc_mac
1027 0 : << " fmt=0x" << subHdr->subevent_format_version << std::dec;
1028 :
1029 0 : uint8_t* eventBuf = static_cast<uint8_t*>(const_cast<void*>(event->GetRawBufferPointer()));
1030 0 : memcpy(eventBuf, &evtHdr, sizeof(DTC_EventHeader));
1031 0 : *event->GetHeader() = evtHdr;
1032 :
1033 0 : memcpy(eventBuf + sizeof(DTC_EventHeader), srcBuf.data() + RECORD_HEADER_SIZE, subEvtByteCount);
1034 :
1035 0 : event->SetupEvent();
1036 :
1037 0 : if (event->IsCorrupt())
1038 : {
1039 0 : std::stringstream ss;
1040 0 : ss << "GetEVBDataAsEvents: event corruption detected at EWT="
1041 0 : << event->GetEventWindowTag().GetEventWindowTag(true)
1042 0 : << " from src=0x" << std::hex << static_cast<int>(src) << std::dec << "\n";
1043 0 : dumpRecord(ss, "BAD record", src, srcBuf.data(), totalRecordBytes);
1044 0 : auto lg = evbLastGoodRecord_.find(src);
1045 0 : if (lg != evbLastGoodRecord_.end())
1046 0 : dumpRecord(ss, "Last GOOD record (same src, for comparison)", src, lg->second.data(), lg->second.size());
1047 : else
1048 0 : ss << "(no previous good record from src=0x" << std::hex << static_cast<int>(src) << std::dec << ")\n";
1049 0 : dumpEVBErrorStatus(ss);
1050 0 : device_.resetSpyHasOccurred();
1051 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 | 8 | 16, ss);
1052 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1053 0 : srcBuf.erase(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
1054 0 : throw std::runtime_error(ss.str());
1055 0 : }
1056 :
1057 0 : lap(evbReadProfile_.subeventSetupNs);
1058 0 : evbLastGoodRecord_[src].assign(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
1059 :
1060 : // Stage the validated subevent (record header stripped) into the per-tag assembly
1061 : // table. Subevents for one tag arrive once from each of the N source DTCs, in no
1062 : // fixed order and intermixed with other tags; the event is released only when all
1063 : // N are present (Step 5). A second delivery from the same source is an error.
1064 : {
1065 0 : uint64_t tag = (static_cast<uint64_t>(subHdr->event_tag_high) << 32) | subHdr->event_tag_low;
1066 0 : uint8_t srcId = static_cast<uint8_t>(subHdr->source_dtc_id);
1067 0 : auto& pend = evbPendingTags_[tag];
1068 0 : if (pend.subevents.empty())
1069 0 : pend.firstArrival = std::chrono::steady_clock::now();
1070 0 : if (pend.subevents.count(srcId))
1071 : {
1072 0 : std::stringstream ss;
1073 0 : ss << "GetEVBDataAsEvents: DUPLICATE subevent for EWT=" << tag << " from source_dtc_id=0x" << std::hex << static_cast<int>(srcId)
1074 0 : << " (chunk src=0x" << static_cast<int>(src) << std::dec << "); this source already delivered this tag.\n";
1075 0 : dumpRecord(ss, "DUPLICATE record", src, srcBuf.data(), totalRecordBytes);
1076 0 : describePending(ss, tag, pend);
1077 0 : dumpEVBErrorStatus(ss);
1078 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1079 0 : ++evbFramingErrors_;
1080 0 : srcBuf.erase(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
1081 0 : throw std::runtime_error(ss.str());
1082 0 : }
1083 0 : pend.subevents[srcId].assign(srcBuf.begin() + RECORD_HEADER_SIZE, srcBuf.begin() + totalRecordBytes);
1084 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: staged EWT=" << tag << " source_dtc_id=0x" << std::hex << static_cast<int>(srcId) << std::dec
1085 0 : << " (" << pend.subevents.size() << "/" << static_cast<int>(evbNumSources_) << " sources); open tags=" << evbPendingTags_.size();
1086 : }
1087 :
1088 0 : srcBuf.erase(srcBuf.begin(), srcBuf.begin() + totalRecordBytes);
1089 0 : ++evbReadProfile_.recordsExtracted;
1090 0 : lap(evbReadProfile_.stagingNs);
1091 0 : }
1092 : }
1093 0 : lap(evbReadProfile_.recordScanNs); // loop exits (incomplete record / empty source) land here
1094 :
1095 : // Step 5: release complete events, strictly in increasing tag order. Because each source
1096 : // delivers its tags in increasing order and every source contributes to every tag owned by
1097 : // this DTC, the lowest pending tag is always the next to complete; a later tag completing
1098 : // first means a record was lost for the lower tag, which Step 0's timeout will report.
1099 0 : while (!evbPendingTags_.empty())
1100 : {
1101 0 : auto it = evbPendingTags_.begin();
1102 0 : auto& pend = it->second;
1103 0 : if (pend.subevents.size() < evbNumSources_)
1104 0 : break; // oldest tag still incomplete: wait (Step 0 guards against waiting forever)
1105 :
1106 0 : uint64_t tag = it->first;
1107 0 : if (evbHaveReleasedTag_ && tag <= evbLastReleasedTag_)
1108 : {
1109 0 : std::stringstream ss;
1110 0 : ss << "GetEVBDataAsEvents: ORDERING violation -- completed EWT=" << tag << " is not greater than last released EWT=" << evbLastReleasedTag_ << ".\n";
1111 0 : describePending(ss, tag, pend);
1112 0 : dumpEVBErrorStatus(ss);
1113 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1114 0 : ++evbFramingErrors_;
1115 0 : throw std::runtime_error(ss.str());
1116 0 : }
1117 :
1118 0 : size_t total = 0;
1119 0 : for (auto& [sid, bytes] : pend.subevents) total += bytes.size();
1120 0 : size_t eventSize = sizeof(DTC_EventHeader) + total;
1121 0 : auto event = std::make_shared<DTC_Event>(eventSize);
1122 :
1123 0 : const auto* firstSub = reinterpret_cast<const DTC_SubEventHeader*>(pend.subevents.begin()->second.data());
1124 0 : DTC_EventHeader evtHdr{};
1125 0 : evtHdr.inclusive_event_byte_count = eventSize;
1126 0 : evtHdr.num_dtcs = pend.subevents.size();
1127 0 : evtHdr.event_tag_low = static_cast<uint32_t>(tag & 0xFFFFFFFF);
1128 0 : evtHdr.event_tag_high = static_cast<uint16_t>(tag >> 32);
1129 0 : evtHdr.event_mode = firstSub->event_mode;
1130 0 : evtHdr.dtc_mac = evbLocalMac_;
1131 0 : evtHdr.partition_id = firstSub->partition_id;
1132 0 : evtHdr.evb_mode = firstSub->evb_mode;
1133 :
1134 0 : uint8_t* buf = static_cast<uint8_t*>(const_cast<void*>(event->GetRawBufferPointer()));
1135 0 : memcpy(buf, &evtHdr, sizeof(DTC_EventHeader));
1136 0 : *event->GetHeader() = evtHdr;
1137 0 : size_t off = sizeof(DTC_EventHeader);
1138 0 : for (auto& [sid, bytes] : pend.subevents) // std::map: ascending source_dtc_id
1139 : {
1140 0 : memcpy(buf + off, bytes.data(), bytes.size());
1141 0 : off += bytes.size();
1142 : }
1143 :
1144 0 : event->SetupEvent();
1145 0 : if (event->IsCorrupt())
1146 : {
1147 0 : std::stringstream ss;
1148 0 : ss << "GetEVBDataAsEvents: assembled event EWT=" << tag << " (" << pend.subevents.size() << " subevents, " << eventSize
1149 0 : << " bytes) failed SetupEvent although each subevent validated individually.\n";
1150 0 : describePending(ss, tag, pend);
1151 0 : dumpEVBErrorStatus(ss);
1152 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1153 0 : ++evbFramingErrors_;
1154 0 : throw std::runtime_error(ss.str());
1155 0 : }
1156 :
1157 0 : evbLastReleasedTag_ = tag;
1158 0 : evbHaveReleasedTag_ = true;
1159 0 : ++evbEventsReleased_;
1160 0 : DTC_TLOG(TLVL_DEBUG + 1) << "GetEVBDataAsEvents: released complete event EWT=" << tag << " with " << pend.subevents.size()
1161 0 : << " subevents, " << eventSize << " bytes; open tags=" << (evbPendingTags_.size() - 1);
1162 0 : output.push_back(std::move(event));
1163 0 : evbPendingTags_.erase(it);
1164 0 : }
1165 0 : lap(evbReadProfile_.eventReleaseNs);
1166 :
1167 : // Step 6: the DMA buffer is released by dmaRelease (RAII, declared after read_data) on return.
1168 0 : return output;
1169 0 : } // end GetEVBDataAsEvents()
1170 :
1171 0 : std::string DTCLib::DTC::FormatEVBReadProfile() const
1172 : {
1173 0 : const EVBReadProfile& profile = evbReadProfile_;
1174 0 : std::ostringstream o;
1175 0 : if (profile.calls == 0)
1176 : {
1177 0 : o << "no GetEVBDataAsEvents calls since reset";
1178 0 : return o.str();
1179 : }
1180 0 : const double totalMs = profile.totalNs / 1e6;
1181 0 : o << "calls " << profile.calls << " (" << profile.callsWithData << " with data, " << (profile.calls - profile.callsWithData) << " idle), records "
1182 0 : << profile.recordsExtracted << ", events released " << evbEventsReleased_ << ", time in GetEVBDataAsEvents " << std::fixed << std::setprecision(1)
1183 0 : << totalMs << " ms\n";
1184 0 : auto row = [&](const char* label, uint64_t ns, uint64_t perCount, const char* perLabel) {
1185 0 : o << " " << std::left << std::setw(36) << label << std::right << std::setw(10) << std::fixed << std::setprecision(1) << (ns / 1e6) << " ms "
1186 0 : << std::setw(6) << std::setprecision(1) << (profile.totalNs ? 100.0 * ns / profile.totalNs : 0.0) << " %";
1187 0 : if (perCount)
1188 0 : o << " " << std::setw(8) << std::setprecision(2) << (ns / 1e3 / perCount) << " us/" << perLabel;
1189 0 : o << "\n";
1190 0 : };
1191 0 : const uint64_t dataCalls = profile.callsWithData ? profile.callsWithData : 1;
1192 0 : row("Step 0 open-tag timeout scan", profile.timeoutScanNs, profile.calls, "call");
1193 0 : row("Step 1 read_data (incl. idle wait)", profile.readDataNs, profile.calls, "call");
1194 0 : row("Step 3 FAFA chunk walk + copy", profile.chunkWalkNs, dataCalls, "buffer");
1195 0 : row("Step 2b register reads (N, MAC, once)", profile.registerReadNs, dataCalls, "buffer");
1196 0 : row("Step 4b record scan + checks", profile.recordScanNs, dataCalls, "buffer");
1197 0 : row("Step 4c DTC_Event alloc+copy+Setup", profile.subeventSetupNs, profile.recordsExtracted, "record");
1198 0 : row("Step 4d staging copies + erase", profile.stagingNs, profile.recordsExtracted, "record");
1199 0 : row("Step 5 event assembly + release", profile.eventReleaseNs, evbEventsReleased_, "event");
1200 0 : row("DMA read_release", profile.releaseBufferNs, dataCalls, "buffer");
1201 0 : const uint64_t accounted = profile.timeoutScanNs + profile.readDataNs + profile.chunkWalkNs + profile.registerReadNs + profile.recordScanNs +
1202 0 : profile.subeventSetupNs + profile.stagingNs + profile.eventReleaseNs + profile.releaseBufferNs;
1203 0 : row("(unaccounted: clock reads, returns)", profile.totalNs > accounted ? profile.totalNs - accounted : 0, 0, "");
1204 0 : return o.str();
1205 0 : } // end FormatEVBReadProfile()
1206 :
1207 0 : std::string DTCLib::DTC::FormatEVBRemoteChunkSizes() const
1208 : {
1209 0 : uint64_t total = 0, under64 = 0, weightedWords = 0;
1210 0 : for (size_t words = 0; words < evbRemoteChunkSizeHistogram_.size(); ++words)
1211 : {
1212 0 : total += evbRemoteChunkSizeHistogram_[words];
1213 0 : weightedWords += evbRemoteChunkSizeHistogram_[words] * words;
1214 0 : if (words < 64) under64 += evbRemoteChunkSizeHistogram_[words];
1215 : }
1216 0 : std::ostringstream o;
1217 0 : if (total == 0)
1218 : {
1219 0 : o << "no remote chunks seen";
1220 0 : return o.str();
1221 : }
1222 0 : uint64_t running = 0;
1223 0 : size_t median = 0;
1224 0 : for (size_t words = 0; words < evbRemoteChunkSizeHistogram_.size(); ++words)
1225 : {
1226 0 : running += evbRemoteChunkSizeHistogram_[words];
1227 0 : if (running * 2 >= total)
1228 : {
1229 0 : median = words;
1230 0 : break;
1231 : }
1232 : }
1233 0 : std::vector<std::pair<uint64_t, size_t>> topSizes;
1234 0 : for (size_t words = 0; words < evbRemoteChunkSizeHistogram_.size(); ++words)
1235 0 : if (evbRemoteChunkSizeHistogram_[words]) topSizes.emplace_back(evbRemoteChunkSizeHistogram_[words], words);
1236 0 : std::sort(topSizes.rbegin(), topSizes.rend());
1237 0 : o << total << " remote chunks, median " << median << " words, mean " << std::fixed << std::setprecision(1)
1238 0 : << (static_cast<double>(weightedWords) / total) << " words, under 64 words: " << under64 << " (" << std::setprecision(1)
1239 0 : << (100.0 * under64 / total) << " %); most common sizes (words:count):";
1240 0 : for (size_t index = 0; index < topSizes.size() && index < 8; ++index)
1241 0 : o << " " << topSizes[index].second << ":" << topSizes[index].first;
1242 0 : return o.str();
1243 0 : } // end FormatEVBRemoteChunkSizes()
1244 :
1245 : // ---------------------------------------------------------------------------
1246 : // GetSubEventData v2 -- simplified, one-buffer-per-call subevent extractor
1247 : // ---------------------------------------------------------------------------
1248 : // Design rules:
1249 : // • Each call reads exactly ONE new DMA buffer from hardware (or completes a
1250 : // pending cross-buffer subevent from the previous call) then returns.
1251 : // • All complete subevents found inside that buffer are returned immediately.
1252 : // • If a subevent straddles the end of the buffer, we save the partial bytes
1253 : // into pendingSubEventBytes_ and return. On the next call we finish it
1254 : // before touching the new buffer.
1255 : // • We never hold more than 2 DMA buffers at once (the previous one, still
1256 : // referenced by pending data, plus the new one). As soon as we are done
1257 : // with a buffer we release it via read_release.
1258 : // ---------------------------------------------------------------------------
1259 0 : std::vector<std::unique_ptr<DTCLib::DTC_SubEvent>> DTCLib::DTC::GetSubEventData(
1260 : DTC_EventWindowTag when, bool matchEventWindowTag)
1261 : {
1262 : (void)matchEventWindowTag; // not yet used; filtering can be added once basic flow works
1263 0 : std::vector<std::unique_ptr<DTC_SubEvent>> output;
1264 :
1265 : // ------------------------------------------------------------------
1266 : // Step 1: get the next DMA buffer from hardware
1267 : // ------------------------------------------------------------------
1268 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData BEGIN EWT=" << when.GetEventWindowTag(true)
1269 0 : << " pendingBytes=" << pendingSubEventBytes_.size()
1270 0 : << " pendingTotal=" << pendingSubEventTotalBytes_
1271 0 : << " pendingPrefixConsumed=" << pendingPrefixConsumed_
1272 0 : << " daqBufs=" << daqDMAInfo_.buffer.size();
1273 :
1274 : // Release the previous buffer NOW — we are done with it.
1275 : // (Pending bytes have already been copied out of it into pendingSubEventBytes_.)
1276 0 : if (daqDMAInfo_.buffer.size() > 0)
1277 : {
1278 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: releasing " << daqDMAInfo_.buffer.size() << " previous buffer(s)";
1279 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
1280 : }
1281 :
1282 : // Read next buffer
1283 : int sts;
1284 : {
1285 0 : int retry = 3;
1286 : do {
1287 0 : sts = ReadBuffer(DTC_DMA_Engine_DAQ, 100 /*ms*/);
1288 0 : } while (sts <= 0 && --retry > 0);
1289 : }
1290 0 : if (sts <= 0)
1291 : {
1292 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: ReadBuffer returned " << sts << ", no data";
1293 0 : return output;
1294 : }
1295 :
1296 : // sts = number of bytes the DMA engine transferred into this buffer
1297 0 : const size_t dmaBytes = static_cast<size_t>(sts);
1298 :
1299 : // Track whether PREVIOUS buffer was full (for diagnostics) and update for this buffer.
1300 0 : const bool prevBufferWasFull = lastDMABufferWasFull_;
1301 0 : lastDMABufferWasFull_ = (dmaBytes == sizeof(mu2e_databuff_t));
1302 :
1303 0 : const uint8_t* bufStart = reinterpret_cast<const uint8_t*>(&daqDMAInfo_.buffer.back()[0]);
1304 : // The DMA descriptor byte count (sts) is the number of bytes actually filled.
1305 : // The last byte is the AXI tlast flag byte when the buffer is not completely full.
1306 : // When it IS completely full (dmaBytes == sizeof(mu2e_databuff_t)) there is no tlast byte.
1307 0 : const size_t payloadBytes = (dmaBytes < sizeof(mu2e_databuff_t)) ? dmaBytes - 1 : dmaBytes;
1308 :
1309 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: new buffer bufStart=" << (void*)bufStart
1310 0 : << " dmaBytes=" << dmaBytes
1311 0 : << " payloadBytes=" << payloadBytes
1312 0 : << " sizeof(mu2e_databuff_t)=" << sizeof(mu2e_databuff_t);
1313 :
1314 : // Print first and last 8 quad-words of buffer for orientation / continuity checking
1315 0 : if (TTEST(TLVL_GetData_2 - TLVL_DEBUG))
1316 : {
1317 : {
1318 0 : std::stringstream ss;
1319 : // TLOG_SCOPED(TLVL_GetData_2) <<
1320 0 : ss << "GetSubEventData: buffer first 8 qwords: ";
1321 0 : for (int i = 0; i < 8 && (size_t)(i * 8) < payloadBytes; ++i)
1322 : // TLOG_ADD
1323 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1324 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1325 0 : DTC_TLOG(TLVL_GetData_2) << ss.str();
1326 0 : }
1327 :
1328 : {
1329 0 : std::stringstream ss;
1330 : // TLOG_SCOPED(TLVL_GetData_2) <<
1331 0 : ss << "GetSubEventData: buffer last 8 qwords: ";
1332 0 : const size_t lastStart = (payloadBytes >= 64) ? payloadBytes - 64 : 0;
1333 0 : for (size_t i = lastStart; i + 8 <= payloadBytes; i += 8)
1334 : // TLOG_ADD
1335 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1336 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i) << " ";
1337 0 : DTC_TLOG(TLVL_GetData_2) << ss.str();
1338 0 : }
1339 : }
1340 :
1341 : // Capture this buffer's tail (last up to 8 qwords) for the next call's diagnostic printout.
1342 : // Called at every return path so lastBufferTailQwords_ always reflects the most recently
1343 : // processed buffer by the time the next call enters Step 2.
1344 0 : auto saveBufferTail = [&]() {
1345 0 : const size_t tailStart = (payloadBytes >= 64) ? payloadBytes - 64 : 0;
1346 0 : lastBufferTailCount_ = 0;
1347 0 : for (size_t i = tailStart; i + 8 <= payloadBytes && lastBufferTailCount_ < 8; i += 8)
1348 0 : lastBufferTailQwords_[lastBufferTailCount_++] =
1349 0 : *reinterpret_cast<const uint64_t*>(bufStart + i);
1350 0 : };
1351 :
1352 : // Returns the last good event window tag as a string, or "-1" if unavailable.
1353 0 : auto ewtTag = [&]() -> std::string {
1354 0 : if (!hasLastGoodSubEventHeader_) return "-1";
1355 0 : return std::to_string(lastGoodSubEventHeader_.event_tag_low |
1356 0 : (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32));
1357 0 : };
1358 :
1359 : // ------------------------------------------------------------------
1360 : // Step 2: if we have a pending (cross-buffer) subevent, finish it
1361 : // using data from the new buffer before parsing the rest
1362 : // ------------------------------------------------------------------
1363 0 : size_t bufOffset = 0; // our walk position within this buffer
1364 :
1365 0 : if (!pendingSubEventBytes_.empty() || pendingPrefixConsumed_)
1366 : {
1367 : // Prefix handling for continuation buffers:
1368 : //
1369 : // When the previous buffer was completely full (prevBufferWasFull == true),
1370 : // the hardware split mid-sub-transfer. This buffer is a raw continuation —
1371 : // NO framing prefix at offset 0. bufOffset = 0.
1372 : //
1373 : // When the previous buffer was NOT full (had tlast, prevBufferWasFull == false),
1374 : // the sub-transfer completed. This buffer starts a NEW sub-transfer with an
1375 : // 8-byte per-sub-transfer byte count prefix. bufOffset = 8.
1376 : //
1377 : // When pendingPrefixConsumed_ is true, the prefix was already consumed in the
1378 : // previous buffer's Step 3. This buffer starts at offset 0.
1379 :
1380 : // Use TLVL_Crossover only for the interesting cases (header/prefix crossover).
1381 0 : const bool isHeaderOrPrefixCrossover = (pendingSubEventTotalBytes_ == 0) || pendingPrefixConsumed_;
1382 0 : const auto crossoverLogLevel = isHeaderOrPrefixCrossover ? TLVL_Crossover : TLVL_GetData_2;
1383 :
1384 0 : if (pendingPrefixConsumed_)
1385 : {
1386 : // Prefix was already consumed in previous buffer's Step 3
1387 0 : bufOffset = 0;
1388 : }
1389 0 : else if (prevBufferWasFull)
1390 : {
1391 : // Previous buffer was full — raw continuation, no prefix
1392 0 : bufOffset = 0;
1393 : }
1394 : else
1395 : {
1396 : // Previous buffer was NOT full — new sub-transfer with 8-byte prefix
1397 0 : if (payloadBytes < sizeof(uint64_t))
1398 : {
1399 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: continuation buffer too small ("
1400 0 : << payloadBytes << " bytes) to hold prefix -- dropping"
1401 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1402 0 : pendingSubEventBytes_.clear();
1403 0 : pendingSubEventTotalBytes_ = 0;
1404 0 : pendingPrefixConsumed_ = false;
1405 0 : saveBufferTail();
1406 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
1407 0 : return output;
1408 : }
1409 0 : bufOffset = sizeof(uint64_t);
1410 : }
1411 :
1412 0 : DTC_TLOG(crossoverLogLevel) << "===== SUBEVENT CROSSOVER: continuation buffer ====="
1413 0 : << " pendingBytes=" << pendingSubEventBytes_.size()
1414 0 : << " pendingTotal=" << pendingSubEventTotalBytes_
1415 0 : << " pendingPrefixConsumed=" << pendingPrefixConsumed_
1416 0 : << " prevBufferWasFull=" << prevBufferWasFull
1417 0 : << " bufOffset=" << bufOffset
1418 0 : << " payloadBytes=" << payloadBytes
1419 0 : << " firstQword=0x" << std::hex
1420 0 : << *reinterpret_cast<const uint64_t*>(bufStart) << std::dec;
1421 : // Dump first 8 qwords of continuation buffer so we can verify what's there
1422 : {
1423 0 : std::stringstream ss;
1424 0 : ss << "===== CROSSOVER: continuation buffer first 8 qwords: ";
1425 0 : for (int i = 0; i < 8 && (size_t)(i * 8) < payloadBytes; ++i)
1426 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1427 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1428 0 : DTC_TLOG(crossoverLogLevel) << ss.str();
1429 0 : }
1430 :
1431 0 : pendingPrefixConsumed_ = false; // consumed
1432 :
1433 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: completing pending subevent"
1434 0 : << " have=" << pendingSubEventBytes_.size()
1435 0 : << " pendingTotal=" << pendingSubEventTotalBytes_;
1436 :
1437 : // If we stored a partial header last time (pendingSubEventTotalBytes_==0),
1438 : // try to complete the header first using bytes from the new buffer.
1439 0 : if (pendingSubEventTotalBytes_ == 0)
1440 : {
1441 0 : const size_t have = pendingSubEventBytes_.size();
1442 0 : const size_t headerBytesNeeded = sizeof(DTC_SubEventHeader) - have;
1443 0 : const size_t avail = payloadBytes - bufOffset;
1444 0 : if (avail < headerBytesNeeded)
1445 : {
1446 : // Still not enough for header — absorb and wait
1447 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: still can't complete header,"
1448 0 : << " have=" << have << " need " << headerBytesNeeded
1449 0 : << " more but only " << avail << " available";
1450 0 : pendingSubEventBytes_.insert(pendingSubEventBytes_.end(),
1451 0 : bufStart + bufOffset, bufStart + payloadBytes);
1452 0 : saveBufferTail();
1453 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
1454 0 : return output;
1455 : }
1456 : // Complete the header, extract total byte count
1457 0 : pendingSubEventBytes_.insert(pendingSubEventBytes_.end(),
1458 0 : bufStart + bufOffset, bufStart + bufOffset + headerBytesNeeded);
1459 0 : pendingSubEventTotalBytes_ = static_cast<size_t>(
1460 0 : *reinterpret_cast<const uint32_t*>(pendingSubEventBytes_.data()) & 0x1FFFFFF);
1461 0 : bufOffset += headerBytesNeeded;
1462 0 : DTC_TLOG(TLVL_Crossover) << "===== CROSSOVER: header assembled from partial bytes ====="
1463 0 : << " subEventByteCount=" << pendingSubEventTotalBytes_
1464 0 : << " (0x" << std::hex << pendingSubEventTotalBytes_ << std::dec << ")"
1465 0 : << " bufOffset=" << bufOffset;
1466 : // Dump assembled header for verification
1467 : {
1468 0 : std::stringstream ss;
1469 0 : ss << "===== CROSSOVER: assembled header (" << pendingSubEventBytes_.size() << " bytes): ";
1470 0 : for (size_t i = 0; i < pendingSubEventBytes_.size() && i < sizeof(DTC_SubEventHeader); i += 4)
1471 0 : ss << std::hex << std::setw(8) << std::setfill('0')
1472 0 : << *reinterpret_cast<const uint32_t*>(&pendingSubEventBytes_[i]) << " ";
1473 : // Show format version field location for quick visual check
1474 0 : if (pendingSubEventBytes_.size() >= sizeof(DTC_SubEventHeader))
1475 : {
1476 0 : auto* hdr = reinterpret_cast<const DTC_SubEventHeader*>(pendingSubEventBytes_.data());
1477 0 : ss << " | fmt_ver=" << hdr->subevent_format_version
1478 0 : << " num_rocs=" << hdr->num_rocs
1479 0 : << " tag=" << (hdr->event_tag_low | (static_cast<uint64_t>(hdr->event_tag_high) << 32));
1480 : }
1481 0 : DTC_TLOG(TLVL_Crossover) << ss.str();
1482 0 : }
1483 :
1484 : // Sanity check: the inclusive byte count must be at least the header size
1485 0 : if (pendingSubEventTotalBytes_ < sizeof(DTC_SubEventHeader))
1486 : {
1487 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: completed pending header has invalid"
1488 0 : << " subEventByteCount=" << pendingSubEventTotalBytes_
1489 0 : << " < sizeof(DTC_SubEventHeader)=" << sizeof(DTC_SubEventHeader)
1490 0 : << " -- discarding corrupt pending data"
1491 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1492 0 : pendingSubEventBytes_.clear();
1493 0 : pendingSubEventTotalBytes_ = 0;
1494 : // Fall through to Step 3 to continue parsing the rest of this buffer
1495 : }
1496 : }
1497 :
1498 : // If the sanity check above cleared pendingSubEventBytes_, skip assembly
1499 0 : if (!pendingSubEventBytes_.empty())
1500 : {
1501 0 : const size_t have = pendingSubEventBytes_.size();
1502 0 : const size_t need = pendingSubEventTotalBytes_;
1503 0 : const size_t still = (need > have) ? need - have : 0;
1504 :
1505 0 : if (still > (payloadBytes - bufOffset))
1506 : {
1507 : // Still not enough — absorb rest of new buffer and keep pending
1508 0 : const size_t absorb = payloadBytes - bufOffset;
1509 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: pending subevent still incomplete after new buffer"
1510 0 : << " still=" << still << " absorbing=" << absorb;
1511 0 : pendingSubEventBytes_.insert(pendingSubEventBytes_.end(),
1512 0 : bufStart + bufOffset, bufStart + bufOffset + absorb);
1513 : // Release this buffer immediately — data is copied
1514 0 : saveBufferTail();
1515 0 : ReleaseBuffers(DTC_DMA_Engine_DAQ);
1516 0 : return output; // come back next call
1517 : }
1518 :
1519 : // We have enough — copy the remainder from this buffer
1520 0 : pendingSubEventBytes_.insert(pendingSubEventBytes_.end(),
1521 0 : bufStart + bufOffset, bufStart + bufOffset + still);
1522 0 : bufOffset += still;
1523 :
1524 0 : DTC_TLOG(crossoverLogLevel) << "===== CROSSOVER: pending subevent fully assembled ====="
1525 0 : << " totalBytes=" << need
1526 0 : << " (0x" << std::hex << need << std::dec << ")"
1527 0 : << " bufOffset now=" << bufOffset;
1528 : // Dump assembled subevent header for final verification before construction
1529 : {
1530 0 : std::stringstream ss;
1531 0 : ss << "===== CROSSOVER: assembled subevent header: ";
1532 0 : size_t dumpLen = std::min(need, sizeof(DTC_SubEventHeader));
1533 0 : for (size_t i = 0; i < dumpLen; i += 4)
1534 0 : ss << std::hex << std::setw(8) << std::setfill('0')
1535 0 : << *reinterpret_cast<const uint32_t*>(&pendingSubEventBytes_[i]) << " ";
1536 0 : if (need >= sizeof(DTC_SubEventHeader))
1537 : {
1538 0 : auto* hdr = reinterpret_cast<const DTC_SubEventHeader*>(pendingSubEventBytes_.data());
1539 0 : ss << " | fmt_ver=" << hdr->subevent_format_version
1540 0 : << " num_rocs=" << hdr->num_rocs
1541 0 : << " tag=" << (hdr->event_tag_low | (static_cast<uint64_t>(hdr->event_tag_high) << 32));
1542 : }
1543 0 : DTC_TLOG(crossoverLogLevel) << ss.str();
1544 0 : }
1545 :
1546 : // Snapshot assembled header for diagnostics on failure
1547 : std::vector<uint8_t> assembledSnapshot(pendingSubEventBytes_.begin(),
1548 0 : pendingSubEventBytes_.begin() + std::min(pendingSubEventBytes_.size(), sizeof(DTC_SubEventHeader)));
1549 :
1550 : // Construct the completed subevent from the assembled bytes
1551 0 : auto inmem = std::make_unique<DTC_SubEvent>(need);
1552 0 : memcpy(const_cast<void*>(inmem->GetRawBufferPointer()),
1553 0 : pendingSubEventBytes_.data(), need);
1554 0 : pendingSubEventBytes_.clear();
1555 0 : pendingSubEventTotalBytes_ = 0;
1556 :
1557 : try
1558 : {
1559 0 : std::string errs;
1560 0 : if (!inmem->SetupSubEvent(errs))
1561 : {
1562 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: corrupt pending subevent: " << errs << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1563 : // don't throw — log and continue parsing rest of buffer
1564 : }
1565 : else
1566 : {
1567 : // Track last good subevent header for diagnostics
1568 0 : lastGoodSubEventHeader_ = *inmem->GetHeader();
1569 0 : hasLastGoodSubEventHeader_ = true;
1570 0 : ++totalSubEventsParsed_;
1571 :
1572 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: completed pending subevent tag="
1573 0 : << inmem->GetEventWindowTag().GetEventWindowTag(true);
1574 0 : output.push_back(std::move(inmem));
1575 : }
1576 0 : }
1577 0 : catch (...)
1578 : {
1579 : // Dump full diagnostic context before re-throwing
1580 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: EXCEPTION setting up pending (cross-buffer) subevent!"
1581 0 : << " assembled totalBytes=" << need << " (0x" << std::hex << need << std::dec << ")"
1582 0 : << " prevBufferWasFull=" << prevBufferWasFull
1583 0 : << " pendingPrefixConsumed(was)=" << pendingPrefixConsumed_
1584 0 : << " bufOffset=" << bufOffset
1585 0 : << " payloadBytes=" << payloadBytes
1586 0 : << " firstQword=0x" << std::hex
1587 0 : << *reinterpret_cast<const uint64_t*>(bufStart) << std::dec << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1588 : // Dump assembled header bytes
1589 : {
1590 0 : std::stringstream ss;
1591 0 : ss << " Assembled header bytes: ";
1592 0 : for (size_t i = 0; i < assembledSnapshot.size(); i += 4)
1593 0 : ss << std::hex << std::setw(8) << std::setfill('0')
1594 0 : << *reinterpret_cast<const uint32_t*>(&assembledSnapshot[i]) << " ";
1595 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1596 0 : }
1597 : // Dump last good subevent header if available
1598 0 : if (hasLastGoodSubEventHeader_)
1599 : {
1600 0 : std::stringstream ss;
1601 0 : ss << " Last GOOD subevent header: ";
1602 0 : auto* p = reinterpret_cast<const uint32_t*>(&lastGoodSubEventHeader_);
1603 0 : for (size_t i = 0; i < sizeof(DTC_SubEventHeader) / 4; ++i)
1604 0 : ss << std::hex << std::setw(8) << std::setfill('0') << p[i] << " ";
1605 0 : ss << " | fmt_ver=" << lastGoodSubEventHeader_.subevent_format_version
1606 0 : << " num_rocs=" << lastGoodSubEventHeader_.num_rocs
1607 0 : << " tag=" << (lastGoodSubEventHeader_.event_tag_low | (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32));
1608 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1609 0 : }
1610 : else
1611 0 : DTC_TLOG(TLVL_ERROR) << " No last good subevent header available";
1612 : // Dump previous buffer tail (last up to 8 qwords before the crossover boundary)
1613 0 : if (lastBufferTailCount_ > 0)
1614 : {
1615 0 : std::stringstream ss;
1616 0 : ss << " Previous buffer last " << lastBufferTailCount_ << " qwords: ";
1617 0 : for (size_t i = 0; i < lastBufferTailCount_; ++i)
1618 0 : ss << std::hex << std::setw(16) << std::setfill('0') << lastBufferTailQwords_[i] << " ";
1619 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1620 0 : }
1621 : else
1622 0 : DTC_TLOG(TLVL_ERROR) << " No previous buffer tail available";
1623 : // Dump continuation buffer context around the boundary
1624 : {
1625 0 : std::stringstream ss;
1626 0 : ss << " Continuation buffer first 16 qwords (raw): ";
1627 0 : for (int i = 0; i < 16 && (size_t)(i * 8) < payloadBytes; ++i)
1628 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1629 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1630 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1631 0 : }
1632 : // Dump DMA buffers via spy for full hardware state
1633 0 : DTC_TLOG(TLVL_ERROR) << " Calling spy() for DMA buffer dump..."
1634 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1635 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */ | 16 /* stack trace */);
1636 : // Re-throw; if it's a DTC_WrongPacketTypeException, augment with last good event tag
1637 : try
1638 : {
1639 0 : throw;
1640 : }
1641 0 : catch (const DTC_WrongPacketTypeException& e)
1642 : {
1643 0 : std::string augMsg = std::string(e.what());
1644 0 : if (hasLastGoodSubEventHeader_)
1645 0 : augMsg += " [last good EWT=" + std::to_string(lastGoodSubEventHeader_.event_tag_low | (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32)) + "]";
1646 0 : throw std::runtime_error(augMsg);
1647 0 : }
1648 0 : catch (...)
1649 : {
1650 0 : throw;
1651 0 : } // all other exception types: propagate unchanged
1652 0 : }
1653 0 : } // end if pendingSubEventBytes_ not empty after header validation
1654 : }
1655 :
1656 : // ------------------------------------------------------------------
1657 : // Step 3: walk the rest of this buffer extracting complete subevents
1658 : // ------------------------------------------------------------------
1659 : // Buffer layout (repeating):
1660 : // [8 bytes] DTC per-sub-transfer framing prefix -- skipped, value NOT used
1661 : // [N bytes] subevent: header (48 bytes) + ROC payload
1662 : // Traversal is driven by two things only:
1663 : // 1. payloadBytes (from sts) -- authoritative outer bound
1664 : // 2. subevent header byte count (bits 24:0 of header word 0) -- advance within buffer
1665 :
1666 0 : while (bufOffset + sizeof(uint64_t) <= payloadBytes)
1667 : {
1668 : // Skip the 8-byte DTC framing prefix — do not use its value
1669 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: skipping 8-byte prefix @ bufOffset=" << bufOffset
1670 0 : << " prefixVal=" << *reinterpret_cast<const uint64_t*>(bufStart + bufOffset)
1671 0 : << " remaining=(" << payloadBytes - bufOffset << ")";
1672 0 : bufOffset += sizeof(uint64_t);
1673 :
1674 : // The subevent inclusive byte count lives in the first 4 bytes of the header
1675 : // (bits 24:0 = inclusive byte count incl. the header itself)
1676 0 : const uint8_t* sePtr = bufStart + bufOffset;
1677 0 : const size_t seAvail = payloadBytes - bufOffset; // bytes remaining in this buffer
1678 :
1679 : // Print first 8 qwords at this subevent boundary for diagnostics
1680 0 : if (TTEST(TLVL_GetData_2 - TLVL_DEBUG))
1681 : {
1682 0 : std::stringstream ss;
1683 0 : ss << "GetSubEventData: subevent boundary @ bufOffset=" << bufOffset
1684 0 : << " seAvail(dist to payload end)=" << seAvail
1685 0 : << " distToMaxBufEnd=" << (sizeof(mu2e_databuff_t) - bufOffset)
1686 0 : << " first 8 qwords: ";
1687 0 : for (int i = 0; i < 8 && (size_t)(i * 8) < seAvail; ++i)
1688 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1689 0 : << *reinterpret_cast<const uint64_t*>(sePtr + i * 8) << " ";
1690 0 : DTC_TLOG(TLVL_GetData_2) << ss.str();
1691 0 : }
1692 :
1693 0 : size_t subEventByteCount = 0;
1694 0 : if (seAvail >= sizeof(DTC_SubEventHeader))
1695 : {
1696 0 : subEventByteCount = static_cast<size_t>(
1697 0 : *reinterpret_cast<const uint32_t*>(sePtr) & 0x1FFFFFF);
1698 :
1699 : // Consistency check: the 8-byte framing prefix we just skipped is an
1700 : // inclusive byte count for this sub-transfer (prefix + header + payload),
1701 : // while the first 4 bytes of the SubEvent header are the subevent inclusive
1702 : // byte count (header + payload, NO prefix). Therefore
1703 : // prefix_low_32 - subEventByteCount == 8
1704 : // for any well-formed pair. If this fails the parser is desynced and
1705 : // continuing would absorb hundreds of KB of unrelated data into a bogus
1706 : // "pending" subevent (see the 0x230023 incident on 2026-05-18). Throw
1707 : // immediately so the caller learns at first sight of corruption.
1708 0 : const uint32_t prefixLow32 =
1709 0 : *reinterpret_cast<const uint32_t*>(bufStart + bufOffset - sizeof(uint64_t));
1710 0 : if (prefixLow32 < 8 || (prefixLow32 - 8) != subEventByteCount)
1711 : {
1712 0 : std::ostringstream oss;
1713 : oss << "GetSubEventData: subevent header consistency check FAILED"
1714 0 : << " -- prefix_low32=0x" << std::hex << prefixLow32
1715 0 : << " subEventByteCount=0x" << subEventByteCount << std::dec
1716 : << " (expected prefix - byteCount == 8)"
1717 0 : << " at bufOffset=" << bufOffset
1718 0 : << " payloadBytes=" << payloadBytes
1719 0 : << " [last EWT=" << ewtTag()
1720 0 : << " totalParsed=" << totalSubEventsParsed_ << "]";
1721 0 : DTC_TLOG(TLVL_ERROR) << oss.str();
1722 : // Dump 16 qwords of context around the boundary so post-mortem is possible
1723 : {
1724 0 : std::stringstream ss;
1725 0 : ss << " Context (prefix-8 .. +16 qwords): ";
1726 0 : const size_t ctxStart =
1727 0 : (bufOffset >= sizeof(uint64_t)) ? bufOffset - sizeof(uint64_t) : 0;
1728 0 : for (size_t i = ctxStart;
1729 0 : i + sizeof(uint64_t) <= payloadBytes && i < ctxStart + 17 * sizeof(uint64_t);
1730 0 : i += sizeof(uint64_t))
1731 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1732 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i) << " ";
1733 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1734 0 : }
1735 : // Dump buffer start (first 16 qwords) to check for the 0xdeadbeef
1736 : // release_all marker at qword[0].
1737 : {
1738 0 : std::stringstream ss;
1739 0 : ss << " Buffer FIRST 16 qwords (from bufStart, check qword[0] for 0xdeadbeef): ";
1740 0 : for (int i = 0; i < 16 && (size_t)(i * 8) < payloadBytes; ++i)
1741 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1742 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1743 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1744 0 : }
1745 0 : device_.spy(DTC_DMA_Engine_DAQ,
1746 : 3 /* once */ | 8 /* wide */ | 16 /* stack trace */);
1747 0 : throw std::runtime_error(oss.str());
1748 0 : }
1749 : }
1750 0 : else if (seAvail > 0)
1751 : {
1752 : // Partial header in this buffer — need to read from next buffer
1753 : // We have fewer than 48 header bytes. Store what we have and return.
1754 0 : DTC_TLOG(TLVL_Crossover) << "===== SUBEVENT HEADER CROSSOVER DETECTED ====="
1755 0 : << " partial header at bufOffset=" << bufOffset
1756 0 : << " seAvail=" << seAvail << " < sizeof(DTC_SubEventHeader)=" << sizeof(DTC_SubEventHeader)
1757 0 : << " -- saving " << seAvail << " partial bytes, will complete on next buffer";
1758 : // Dump the partial header bytes being cached
1759 : {
1760 0 : std::stringstream ss;
1761 0 : ss << "===== CROSSOVER: caching partial header bytes: ";
1762 0 : for (size_t i = 0; i < seAvail; i += 4)
1763 : {
1764 0 : if (i + 4 <= seAvail)
1765 0 : ss << std::hex << std::setw(8) << std::setfill('0')
1766 0 : << *reinterpret_cast<const uint32_t*>(sePtr + i) << " ";
1767 : else
1768 0 : for (size_t j = i; j < seAvail; ++j)
1769 0 : ss << std::hex << std::setw(2) << std::setfill('0')
1770 0 : << static_cast<unsigned>(sePtr[j]) << " ";
1771 : }
1772 0 : DTC_TLOG(TLVL_Crossover) << ss.str();
1773 0 : }
1774 0 : pendingSubEventBytes_.assign(sePtr, sePtr + seAvail);
1775 :
1776 : // If we have at least 4 bytes, we can extract and validate the byte count early
1777 0 : if (seAvail >= sizeof(uint32_t))
1778 : {
1779 0 : pendingSubEventTotalBytes_ = static_cast<size_t>(
1780 0 : *reinterpret_cast<const uint32_t*>(sePtr) & 0x1FFFFFF);
1781 0 : if (pendingSubEventTotalBytes_ < sizeof(DTC_SubEventHeader))
1782 : {
1783 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: partial header has invalid"
1784 0 : << " subEventByteCount=" << pendingSubEventTotalBytes_
1785 0 : << " < sizeof(DTC_SubEventHeader)=" << sizeof(DTC_SubEventHeader)
1786 0 : << " -- discarding corrupt partial header"
1787 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1788 0 : pendingSubEventBytes_.clear();
1789 0 : pendingSubEventTotalBytes_ = 0;
1790 0 : break;
1791 : }
1792 :
1793 : // Same prefix/byteCount consistency check as the full-header path:
1794 : // prefix_low_32 - subEventByteCount must equal 8. This is the only place
1795 : // we get a chance to validate before storing pendingSubEventTotalBytes_
1796 : // and letting Step 2 absorb potentially many KB of unrelated data on the
1797 : // next calls. Throw immediately if it fails.
1798 0 : const uint32_t prefixLow32 =
1799 0 : *reinterpret_cast<const uint32_t*>(bufStart + bufOffset - sizeof(uint64_t));
1800 0 : if (prefixLow32 < 8 || (prefixLow32 - 8) != pendingSubEventTotalBytes_)
1801 : {
1802 0 : std::ostringstream oss;
1803 : oss << "GetSubEventData: partial-header consistency check FAILED"
1804 0 : << " -- prefix_low32=0x" << std::hex << prefixLow32
1805 0 : << " pendingSubEventTotalBytes=0x" << pendingSubEventTotalBytes_ << std::dec
1806 : << " (expected prefix - byteCount == 8)"
1807 0 : << " at bufOffset=" << bufOffset
1808 0 : << " seAvail=" << seAvail
1809 0 : << " payloadBytes=" << payloadBytes
1810 0 : << " [last EWT=" << ewtTag()
1811 0 : << " totalParsed=" << totalSubEventsParsed_ << "]";
1812 0 : DTC_TLOG(TLVL_ERROR) << oss.str();
1813 : // Dump context around the boundary (prefix + whatever partial bytes we have)
1814 : {
1815 0 : std::stringstream ss;
1816 0 : ss << " Context (prefix-8 .. end-of-buffer, up to 17 qwords): ";
1817 0 : const size_t ctxStart =
1818 0 : (bufOffset >= sizeof(uint64_t)) ? bufOffset - sizeof(uint64_t) : 0;
1819 0 : for (size_t i = ctxStart;
1820 0 : i + sizeof(uint64_t) <= payloadBytes && i < ctxStart + 17 * sizeof(uint64_t);
1821 0 : i += sizeof(uint64_t))
1822 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1823 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i) << " ";
1824 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1825 0 : }
1826 : // Dump buffer start (first 16 qwords) to check for the 0xdeadbeef
1827 : // release_all marker at qword[0].
1828 : {
1829 0 : std::stringstream ss;
1830 0 : ss << " Buffer FIRST 16 qwords (from bufStart, check qword[0] for 0xdeadbeef): ";
1831 0 : for (int i = 0; i < 16 && (size_t)(i * 8) < payloadBytes; ++i)
1832 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1833 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1834 0 : DTC_TLOG(TLVL_ERROR) << ss.str();
1835 0 : }
1836 0 : device_.spy(DTC_DMA_Engine_DAQ,
1837 : 3 /* once */ | 8 /* wide */ | 16 /* stack trace */);
1838 : // Clear the pending state so we do not absorb anything on the next call
1839 0 : pendingSubEventBytes_.clear();
1840 0 : pendingSubEventTotalBytes_ = 0;
1841 0 : throw std::runtime_error(oss.str());
1842 0 : }
1843 :
1844 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: partial header early byteCount="
1845 0 : << pendingSubEventTotalBytes_;
1846 : // Note: pendingSubEventTotalBytes_ is set but header is still incomplete
1847 : // (pendingSubEventBytes_.size() < sizeof(DTC_SubEventHeader)).
1848 : // Step 2 on next call will complete the header before assembling payload.
1849 : }
1850 : else
1851 : {
1852 : // Fewer than 4 bytes — can't extract byte count yet
1853 0 : pendingSubEventTotalBytes_ = 0;
1854 : }
1855 0 : break; // done with this buffer
1856 : }
1857 : else
1858 : {
1859 : // Exactly 0 bytes remain after the prefix — subevent fully in next buffer.
1860 : // The prefix was consumed here, so the next buffer is a continuation buffer
1861 : // that starts with raw subevent header data at offset 0 (no prefix).
1862 : // Mark this so the next call's Step 2 knows to treat it as a continuation.
1863 0 : DTC_TLOG(TLVL_Crossover) << "===== SUBEVENT PREFIX-ONLY CROSSOVER DETECTED ====="
1864 0 : << " prefix at end of buffer, 0 subevent bytes here"
1865 0 : << " -- next buffer is a continuation with no prefix"
1866 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1867 0 : pendingSubEventBytes_.resize(0); // empty but see note below
1868 0 : pendingSubEventTotalBytes_ = 0;
1869 : // Set pendingPrefixConsumed_ so the next call enters Step 2
1870 : // (continuation path) instead of Step 3 (which would incorrectly skip a prefix).
1871 0 : pendingPrefixConsumed_ = true;
1872 0 : break;
1873 : }
1874 :
1875 0 : if (subEventByteCount < sizeof(DTC_SubEventHeader))
1876 : {
1877 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: subEventByteCount=" << subEventByteCount
1878 0 : << " < sizeof(DTC_SubEventHeader)=" << sizeof(DTC_SubEventHeader)
1879 0 : << " at bufOffset=" << bufOffset << " -- stopping parse"
1880 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1881 0 : break;
1882 : }
1883 :
1884 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: subevent @ bufOffset=" << bufOffset
1885 0 : << " subEventByteCount=" << subEventByteCount
1886 0 : << " seAvail=" << seAvail;
1887 :
1888 0 : if (subEventByteCount <= seAvail)
1889 : {
1890 : // Entire subevent fits in this buffer — copy it out so the DMA buffer
1891 : // can be safely released without use-after-free. (The pointer-based
1892 : // DTC_SubEvent(sePtr) constructor stores a raw pointer into the DMA
1893 : // buffer, which becomes dangling after ReleaseBuffers.)
1894 : try
1895 : {
1896 0 : auto res = std::make_unique<DTC_SubEvent>(subEventByteCount);
1897 0 : memcpy(const_cast<void*>(res->GetRawBufferPointer()), sePtr, subEventByteCount);
1898 0 : std::string errs;
1899 0 : if (!res->SetupSubEvent(errs))
1900 : {
1901 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: corrupt subevent at bufOffset=" << bufOffset
1902 0 : << " errs=" << errs << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1903 : }
1904 : else
1905 : {
1906 : // Track last good subevent header for diagnostics
1907 0 : lastGoodSubEventHeader_ = *res->GetHeader();
1908 0 : hasLastGoodSubEventHeader_ = true;
1909 0 : ++totalSubEventsParsed_;
1910 :
1911 0 : DTC_TLOG(TLVL_GetData_2) << "GetSubEventData: subevent OK tag="
1912 0 : << res->GetEventWindowTag().GetEventWindowTag(true)
1913 0 : << " bytes=" << subEventByteCount;
1914 0 : output.push_back(std::move(res));
1915 : }
1916 0 : }
1917 0 : catch (...)
1918 : {
1919 0 : DTC_TLOG(TLVL_ERROR) << "GetSubEventData: EXCEPTION constructing/setting up in-place subevent!"
1920 0 : << " bufOffset=" << bufOffset
1921 0 : << " subEventByteCount=" << subEventByteCount
1922 0 : << " (0x" << std::hex << subEventByteCount << std::dec << ")"
1923 0 : << " seAvail=" << seAvail
1924 0 : << " prevBufferWasFull=" << prevBufferWasFull
1925 0 : << " payloadBytes=" << payloadBytes
1926 0 : << " firstQword=0x" << std::hex
1927 0 : << *reinterpret_cast<const uint64_t*>(bufStart) << std::dec << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1928 : // Dump the subevent header bytes at this location
1929 : {
1930 0 : std::stringstream ss;
1931 0 : ss << " Subevent header bytes at bufOffset=" << bufOffset << ": ";
1932 0 : size_t dumpLen = std::min(seAvail, sizeof(DTC_SubEventHeader));
1933 0 : for (size_t i = 0; i < dumpLen; i += 4)
1934 0 : ss << std::hex << std::setw(8) << std::setfill('0')
1935 0 : << *reinterpret_cast<const uint32_t*>(sePtr + i) << " ";
1936 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1937 0 : }
1938 : // Dump context: 8 qwords before and 8 qwords after the subevent start
1939 : {
1940 0 : std::stringstream ss;
1941 0 : ss << " Context around bufOffset=" << bufOffset << ": BEFORE: ";
1942 0 : size_t beforeStart = (bufOffset >= 64) ? bufOffset - 64 : 0;
1943 0 : for (size_t i = beforeStart; i + 8 <= bufOffset; i += 8)
1944 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1945 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i) << " ";
1946 0 : ss << " | AFTER: ";
1947 0 : for (size_t i = bufOffset; i + 8 <= payloadBytes && i < bufOffset + 64; i += 8)
1948 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1949 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i) << " ";
1950 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1951 0 : }
1952 : // Dump buffer start (first 16 qwords) so we can check for the 0xdeadbeef
1953 : // release_all marker at qword[0] -- if it's still there, hardware never
1954 : // wrote this buffer since the last ReleaseAllBuffers.
1955 : {
1956 0 : std::stringstream ss;
1957 0 : ss << " Buffer FIRST 16 qwords (from bufStart, check qword[0] for 0xdeadbeef): ";
1958 0 : for (int i = 0; i < 16 && (size_t)(i * 8) < payloadBytes; ++i)
1959 0 : ss << std::hex << std::setw(16) << std::setfill('0')
1960 0 : << *reinterpret_cast<const uint64_t*>(bufStart + i * 8) << " ";
1961 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1962 0 : }
1963 : // Dump last good subevent header if available
1964 0 : if (hasLastGoodSubEventHeader_)
1965 : {
1966 0 : std::stringstream ss;
1967 0 : ss << " Last GOOD subevent header: ";
1968 0 : auto* p = reinterpret_cast<const uint32_t*>(&lastGoodSubEventHeader_);
1969 0 : for (size_t i = 0; i < sizeof(DTC_SubEventHeader) / 4; ++i)
1970 0 : ss << std::hex << std::setw(8) << std::setfill('0') << p[i] << " ";
1971 0 : ss << " | fmt_ver=" << lastGoodSubEventHeader_.subevent_format_version
1972 0 : << " num_rocs=" << lastGoodSubEventHeader_.num_rocs
1973 0 : << " tag=" << (lastGoodSubEventHeader_.event_tag_low | (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32));
1974 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1975 0 : }
1976 : else
1977 0 : DTC_TLOG(TLVL_ERROR) << " No last good subevent header available";
1978 : // Dump previous buffer tail (last up to 8 qwords from the buffer before this one)
1979 0 : if (lastBufferTailCount_ > 0)
1980 : {
1981 0 : std::stringstream ss;
1982 0 : ss << " Previous buffer last " << lastBufferTailCount_ << " qwords: ";
1983 0 : for (size_t i = 0; i < lastBufferTailCount_; ++i)
1984 0 : ss << std::hex << std::setw(16) << std::setfill('0') << lastBufferTailQwords_[i] << " ";
1985 0 : DTC_TLOG(TLVL_ERROR) << ss.str() << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1986 0 : }
1987 : else
1988 0 : DTC_TLOG(TLVL_ERROR) << " No previous buffer tail available";
1989 : // Dump DMA buffers via spy for full hardware state
1990 0 : DTC_TLOG(TLVL_ERROR) << " Calling spy() for DMA buffer dump..."
1991 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
1992 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */ | 16 /* stack trace */);
1993 : // Re-throw; if it's a DTC_WrongPacketTypeException, augment with last good event tag
1994 : try
1995 : {
1996 0 : throw;
1997 : }
1998 0 : catch (const DTC_WrongPacketTypeException& e)
1999 : {
2000 0 : std::string augMsg = std::string(e.what());
2001 0 : if (hasLastGoodSubEventHeader_)
2002 0 : augMsg += " [last good EWT=" + std::to_string(lastGoodSubEventHeader_.event_tag_low | (static_cast<uint64_t>(lastGoodSubEventHeader_.event_tag_high) << 32)) + "]";
2003 0 : throw std::runtime_error(augMsg);
2004 0 : }
2005 0 : catch (...)
2006 : {
2007 0 : throw;
2008 0 : } // all other exception types: propagate unchanged
2009 0 : }
2010 0 : bufOffset += subEventByteCount;
2011 : }
2012 : else
2013 : {
2014 : // Subevent crosses buffer boundary — save what we have and return
2015 0 : DTC_TLOG(TLVL_GetData_2) << "===== SUBEVENT BODY CROSSOVER DETECTED ====="
2016 0 : << " have=" << seAvail
2017 0 : << " need=" << subEventByteCount
2018 0 : << " (0x" << std::hex << subEventByteCount << std::dec << ")"
2019 0 : << " missing=" << (subEventByteCount - seAvail)
2020 0 : << " -- saving partial subevent, will continue on next buffer";
2021 0 : pendingSubEventBytes_.assign(sePtr, sePtr + seAvail);
2022 0 : pendingSubEventTotalBytes_ = subEventByteCount;
2023 0 : break; // done with this buffer
2024 : }
2025 : }
2026 :
2027 : // ------------------------------------------------------------------
2028 : // Step 3b: check for leftover bytes that didn't fit in the while-loop
2029 : // ------------------------------------------------------------------
2030 : // The while-loop requires at least 8 bytes (sizeof(uint64_t)) to enter,
2031 : // since every subevent is preceded by an 8-byte framing prefix. If fewer
2032 : // than 8 bytes remain after the last complete subevent (or partial-header
2033 : // caching), they would otherwise be silently dropped.
2034 0 : if (pendingSubEventBytes_.empty() && !pendingPrefixConsumed_ && bufOffset < payloadBytes)
2035 : {
2036 0 : const size_t leftover = payloadBytes - bufOffset;
2037 : // For non-full buffers, leftover bytes < 8 are most likely a partial
2038 : // framing prefix or alignment padding — the next buffer starts fresh.
2039 : // Log at WARNING level so this is visible in diagnostics.
2040 0 : DTC_TLOG(TLVL_WARNING) << "GetSubEventData: " << leftover
2041 0 : << " leftover byte(s) at end of buffer (bufOffset=" << bufOffset
2042 0 : << " payloadBytes=" << payloadBytes
2043 0 : << " bufferFull=" << lastDMABufferWasFull_ << ")"
2044 0 : << " [last EWT=" << ewtTag() << " totalParsed=" << totalSubEventsParsed_ << "]";
2045 : // Dump the first 8 qwords of the leftover region so we can see what stopped the parse
2046 : {
2047 0 : std::stringstream ss;
2048 0 : ss << "GetSubEventData: leftover region first 8 qwords @ bufOffset=" << bufOffset << ": ";
2049 0 : for (int i = 0; i < 8 && bufOffset + (size_t)(i * 8) + 8 <= payloadBytes; ++i)
2050 0 : ss << std::hex << std::setw(16) << std::setfill('0')
2051 0 : << *reinterpret_cast<const uint64_t*>(bufStart + bufOffset + i * 8) << " ";
2052 : // Also show the raw 4-byte value that was (or would be) parsed as subEventByteCount
2053 0 : if (leftover >= sizeof(uint32_t))
2054 0 : ss << " | raw_byteCount_field=0x" << std::hex
2055 0 : << (*reinterpret_cast<const uint32_t*>(bufStart + bufOffset) & 0x1FFFFFF)
2056 0 : << std::dec;
2057 0 : DTC_TLOG(TLVL_WARNING) << ss.str();
2058 0 : }
2059 : }
2060 :
2061 : // ------------------------------------------------------------------
2062 : // Step 4: report and return
2063 : // ------------------------------------------------------------------
2064 0 : DTC_TLOG(TLVL_GetData) << "GetSubEventData RETURN output.size()=" << output.size()
2065 0 : << " pendingBytes=" << pendingSubEventBytes_.size()
2066 0 : << " pendingTotal=" << pendingSubEventTotalBytes_
2067 0 : << " pendingPrefixConsumed=" << pendingPrefixConsumed_;
2068 : // Always refresh lastGoodSubEventHeader_ from the last successfully returned subevent
2069 : // so the member is current even if the inline saves were skipped (e.g. early-exit paths).
2070 0 : if (!output.empty() && output.back() && output.back()->GetHeader())
2071 : {
2072 0 : lastGoodSubEventHeader_ = *output.back()->GetHeader();
2073 0 : hasLastGoodSubEventHeader_ = true;
2074 : }
2075 0 : saveBufferTail();
2076 0 : return output;
2077 0 : } // endGetSubEventData() v2
2078 :
2079 0 : void DTCLib::DTC::WriteSimFileToDTC(std::string file, bool /*goForever*/, bool overwriteEnvironment,
2080 : std::string outputFileName, bool skipVerify)
2081 : {
2082 0 : bool success = false;
2083 0 : int retryCount = 0;
2084 0 : while (!success && retryCount < 5)
2085 : {
2086 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC BEGIN";
2087 0 : auto writeOutput = outputFileName != "";
2088 0 : std::ofstream outputStream;
2089 0 : if (writeOutput)
2090 : {
2091 0 : outputStream.open(outputFileName, std::ios::out | std::ios::binary);
2092 : }
2093 0 : auto sim = getenv("DTCLIB_SIM_FILE");
2094 0 : if (!overwriteEnvironment && sim != nullptr)
2095 : {
2096 0 : file = std::string(sim);
2097 : }
2098 :
2099 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC file is " << file << ", Setting up DTC";
2100 0 : DisableDetectorEmulator();
2101 0 : DisableDetectorEmulatorMode();
2102 : // ResetDDR(); // this can take about a second
2103 0 : ResetDDRWriteAddress();
2104 0 : ResetDDRReadAddress();
2105 0 : SetDDRDataLocalStartAddress(0x0);
2106 0 : SetDDRDataLocalEndAddress(0xFFFFFFFF);
2107 0 : EnableDetectorEmulatorMode();
2108 0 : SetDetectorEmulationDMACount(1);
2109 0 : SetDetectorEmulationDMADelayCount(250); // 1 microseconds
2110 0 : uint64_t totalSize = 0;
2111 0 : auto n = 0;
2112 :
2113 0 : auto sizeCheck = true;
2114 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC Opening file";
2115 0 : std::ifstream is(file, std::ifstream::binary);
2116 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC Reading file";
2117 0 : while (is && is.good() && sizeCheck)
2118 : {
2119 0 : DTC_TLOG(TLVL_WriteSimFileToDTC2) << "WriteSimFileToDTC Reading a DMA from file..." << file;
2120 0 : auto buf = reinterpret_cast<mu2e_databuff_t*>(new char[0x10000]);
2121 0 : is.read(reinterpret_cast<char*>(buf), sizeof(uint64_t));
2122 0 : if (is.eof())
2123 : {
2124 0 : DTC_TLOG(TLVL_WriteSimFileToDTC2) << "WriteSimFileToDTC End of file reached.";
2125 0 : delete[] buf;
2126 0 : break;
2127 : }
2128 0 : auto sz = *reinterpret_cast<uint64_t*>(buf);
2129 0 : is.read(reinterpret_cast<char*>(buf) + 8, sz - sizeof(uint64_t));
2130 0 : if (sz < 80 && sz > 0)
2131 : {
2132 0 : auto oldSize = sz;
2133 0 : sz = 80;
2134 0 : memcpy(buf, &sz, sizeof(uint64_t));
2135 0 : uint64_t sixtyFour = 64;
2136 0 : memcpy(reinterpret_cast<uint64_t*>(buf) + 1, &sixtyFour, sizeof(uint64_t));
2137 0 : bzero(reinterpret_cast<uint64_t*>(buf) + 2, sz - oldSize);
2138 : }
2139 : // is.read((char*)buf + 8, sz - sizeof(uint64_t));
2140 0 : if (sz > 0 && (sz + totalSize < 0xFFFFFFFF || simMode_ == DTC_SimMode_LargeFile))
2141 : {
2142 0 : DTC_TLOG(TLVL_WriteSimFileToDTC2) << "WriteSimFileToDTC Size is " << sz << ", writing to device";
2143 0 : if (writeOutput)
2144 : {
2145 0 : DTC_TLOG(TLVL_WriteSimFileToDTC3)
2146 0 : << "WriteSimFileToDTC: Stripping off DMA header words and writing to binary file";
2147 0 : outputStream.write(reinterpret_cast<char*>(buf) + 16, sz - 16);
2148 : }
2149 :
2150 0 : auto dmaByteCount = *(reinterpret_cast<uint64_t*>(buf) + 1);
2151 0 : DTC_TLOG(TLVL_WriteSimFileToDTC3) << "WriteSimFileToDTC: Inclusive write byte count: " << sz
2152 0 : << ", DMA Byte count: " << dmaByteCount;
2153 0 : if (sz - 8 != dmaByteCount)
2154 : {
2155 0 : DTC_TLOG(TLVL_ERROR) << "WriteSimFileToDTC: ERROR: Inclusive write Byte count " << sz
2156 0 : << " is inconsistent with DMA byte count " << dmaByteCount << " for DMA at 0x"
2157 0 : << std::hex << totalSize << " (0x" << sz - 16 << " != 0x" << dmaByteCount << ")";
2158 0 : sizeCheck = false;
2159 : }
2160 :
2161 0 : totalSize += sz - 8;
2162 0 : n++;
2163 0 : DTC_TLOG(TLVL_WriteSimFileToDTC3) << "WriteSimFileToDTC: totalSize is now " << totalSize << ", n is now " << n;
2164 0 : WriteDetectorEmulatorData(buf, static_cast<size_t>(sz));
2165 0 : }
2166 0 : else if (sz > 0)
2167 : {
2168 0 : DTC_TLOG(TLVL_WriteSimFileToDTC2) << "WriteSimFileToDTC DTC memory is now full. Closing file.";
2169 0 : sizeCheck = false;
2170 : }
2171 0 : delete[] buf;
2172 : }
2173 :
2174 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC Closing file. sizecheck=" << sizeCheck << ", eof=" << is.eof()
2175 0 : << ", fail=" << is.fail() << ", bad=" << is.bad();
2176 0 : is.close();
2177 0 : if (writeOutput) outputStream.close();
2178 0 : SetDDRDataLocalEndAddress(static_cast<uint32_t>(totalSize - 1));
2179 0 : success = skipVerify || VerifySimFileInDTC(file, outputFileName);
2180 0 : retryCount++;
2181 0 : }
2182 :
2183 0 : if (retryCount == 5)
2184 : {
2185 0 : DTC_TLOG(TLVL_ERROR) << "WriteSimFileToDTC FAILED after 5 attempts! ABORTING!";
2186 0 : exit(4);
2187 : }
2188 : else
2189 : {
2190 0 : __COUT_INFO__ << "WriteSimFileToDTC Took " << retryCount << " attempts to write file";
2191 : }
2192 :
2193 0 : SetDetectorEmulatorInUse();
2194 0 : DTC_TLOG(TLVL_WriteSimFileToDTC) << "WriteSimFileToDTC END";
2195 0 : }
2196 :
2197 0 : bool DTCLib::DTC::VerifySimFileInDTC(std::string file, std::string rawOutputFilename)
2198 : {
2199 0 : uint64_t totalSize = 0;
2200 0 : auto n = 0;
2201 0 : auto sizeCheck = true;
2202 :
2203 0 : auto writeOutput = rawOutputFilename != "";
2204 0 : std::ofstream outputStream;
2205 0 : if (writeOutput)
2206 : {
2207 0 : outputStream.open(rawOutputFilename + ".verify", std::ios::out | std::ios::binary);
2208 : }
2209 :
2210 0 : auto sim = getenv("DTCLIB_SIM_FILE");
2211 0 : if (file.size() == 0 && sim != nullptr)
2212 : {
2213 0 : file = std::string(sim);
2214 : }
2215 :
2216 0 : ResetDDRReadAddress();
2217 0 : DTC_TLOG(TLVL_VerifySimFileInDTC) << "VerifySimFileInDTC Opening file";
2218 0 : std::ifstream is(file, std::ifstream::binary);
2219 0 : if (!is || !is.good())
2220 : {
2221 0 : DTC_TLOG(TLVL_ERROR) << "VerifySimFileInDTC Failed to open file " << file << "!";
2222 : }
2223 :
2224 0 : DTC_TLOG(TLVL_VerifySimFileInDTC) << "VerifySimFileInDTC Reading file";
2225 0 : while (is && is.good() && sizeCheck)
2226 : {
2227 0 : DTC_TLOG(TLVL_VerifySimFileInDTC2) << "VerifySimFileInDTC Reading a DMA from file..." << file;
2228 : uint64_t file_buffer_size;
2229 0 : auto buffer_from_file = reinterpret_cast<mu2e_databuff_t*>(new char[0x10000]);
2230 0 : is.read(reinterpret_cast<char*>(&file_buffer_size), sizeof(uint64_t));
2231 0 : if (is.eof())
2232 : {
2233 0 : DTC_TLOG(TLVL_VerifySimFileInDTC2) << "VerifySimFileInDTC End of file reached.";
2234 0 : delete[] buffer_from_file;
2235 0 : break;
2236 : }
2237 0 : is.read(reinterpret_cast<char*>(buffer_from_file), file_buffer_size - sizeof(uint64_t));
2238 0 : if (file_buffer_size < 80 && file_buffer_size > 0)
2239 : {
2240 : // auto oldSize = file_buffer_size;
2241 0 : file_buffer_size = 80;
2242 0 : uint64_t sixtyFour = 64;
2243 0 : memcpy(reinterpret_cast<uint64_t*>(buffer_from_file), &sixtyFour, sizeof(uint64_t));
2244 : // bzero(reinterpret_cast<uint64_t*>(buffer_from_file) + 2, sz - oldSize);
2245 : }
2246 :
2247 0 : if (file_buffer_size > 0 && (file_buffer_size + totalSize < 0xFFFFFFFF || simMode_ == DTC_SimMode_LargeFile))
2248 : {
2249 0 : DTC_TLOG(TLVL_VerifySimFileInDTC2) << "VerifySimFileInDTC Expected Size is " << file_buffer_size - sizeof(uint64_t) << ", reading from device";
2250 0 : auto inclusiveByteCount = *(reinterpret_cast<uint64_t*>(buffer_from_file));
2251 0 : DTC_TLOG(TLVL_VerifySimFileInDTC3) << "VerifySimFileInDTC: DMA Write size: " << file_buffer_size
2252 0 : << ", Inclusive byte count: " << inclusiveByteCount;
2253 0 : if (file_buffer_size - 8 != inclusiveByteCount)
2254 : {
2255 0 : DTC_TLOG(TLVL_ERROR) << "VerifySimFileInDTC: ERROR: DMA Write size " << file_buffer_size
2256 0 : << " is inconsistent with DMA byte count " << inclusiveByteCount << " for DMA at 0x"
2257 0 : << std::hex << totalSize << " (0x" << file_buffer_size - 8 << " != 0x" << inclusiveByteCount << ")";
2258 0 : sizeCheck = false;
2259 : }
2260 :
2261 0 : totalSize += file_buffer_size;
2262 0 : n++;
2263 0 : DTC_TLOG(TLVL_VerifySimFileInDTC3) << "VerifySimFileInDTC: totalSize is now " << totalSize << ", n is now " << n;
2264 : // WriteDetectorEmulatorData(buffer_from_file, static_cast<size_t>(sz));
2265 0 : DisableDetectorEmulator();
2266 0 : SetDetectorEmulationDMACount(1);
2267 0 : EnableDetectorEmulator();
2268 :
2269 : mu2e_databuff_t* buffer_from_device;
2270 0 : auto tmo_ms = 1500;
2271 0 : DTC_TLOG(TLVL_VerifySimFileInDTC) << "VerifySimFileInDTC - before read for DAQ ";
2272 0 : auto sts = device_.read_data(DTC_DMA_Engine_DAQ, reinterpret_cast<void**>(&buffer_from_device), tmo_ms);
2273 0 : if (writeOutput && sts > 8)
2274 : {
2275 0 : DTC_TLOG(TLVL_VerifySimFileInDTC3) << "VerifySimFileInDTC: Writing to binary file";
2276 0 : outputStream.write(reinterpret_cast<char*>(*buffer_from_device), sts);
2277 : }
2278 :
2279 0 : if (sts == 0)
2280 : {
2281 0 : DTC_TLOG(TLVL_ERROR) << "VerifySimFileInDTC Error reading buffer " << n << ", aborting!";
2282 0 : delete[] buffer_from_file;
2283 0 : is.close();
2284 0 : if (writeOutput) outputStream.close();
2285 0 : return false;
2286 : }
2287 0 : size_t readSz = *(reinterpret_cast<uint64_t*>(buffer_from_device));
2288 0 : DTC_TLOG(TLVL_VerifySimFileInDTC) << "VerifySimFileInDTC - after read, bc=" << inclusiveByteCount << " sts=" << sts
2289 0 : << " rdSz=" << readSz;
2290 :
2291 : // DMA engine strips off leading 64-bit word
2292 0 : DTC_TLOG(TLVL_VerifySimFileInDTC3) << "VerifySimFileInDTC - Checking buffer size";
2293 0 : if (static_cast<size_t>(sts) != inclusiveByteCount)
2294 : {
2295 0 : DTC_TLOG(TLVL_ERROR) << "VerifySimFileInDTC Buffer " << n << " has size 0x" << std::hex << sts
2296 0 : << " but the input file has size 0x" << std::hex << inclusiveByteCount
2297 0 : << " for that buffer!";
2298 :
2299 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2300 0 : delete[] buffer_from_file;
2301 0 : is.close();
2302 0 : if (writeOutput) outputStream.close();
2303 0 : return false;
2304 : }
2305 :
2306 0 : DTC_TLOG(TLVL_VerifySimFileInDTC2) << "VerifySimFileInDTC - Checking buffer contents";
2307 0 : size_t cnt = sts % sizeof(uint64_t) == 0 ? sts / sizeof(uint64_t) : 1 + (sts / sizeof(uint64_t));
2308 :
2309 0 : for (size_t ii = 0; ii < cnt; ++ii)
2310 : {
2311 0 : auto l = *(reinterpret_cast<uint64_t*>(*buffer_from_device) + ii);
2312 0 : auto r = *(reinterpret_cast<uint64_t*>(*buffer_from_file) + ii);
2313 0 : if (l != r)
2314 : {
2315 0 : size_t address = totalSize - file_buffer_size + ((ii + 1) * sizeof(uint64_t));
2316 0 : DTC_TLOG(TLVL_ERROR) << "VerifySimFileInDTC Buffer " << n << " word " << ii << " (Address in file 0x" << std::hex
2317 0 : << address << "):"
2318 0 : << " Expected 0x" << std::hex << r << ", but got 0x" << std::hex << l
2319 0 : << ". Returning False!";
2320 0 : DTC_TLOG(TLVL_VerifySimFileInDTC3) << "VerifySimFileInDTC Next words: "
2321 0 : "Expected 0x"
2322 0 : << std::hex << *(reinterpret_cast<uint64_t*>(*buffer_from_file) + ii + 1) << ", "
2323 0 : "DTC: 0x"
2324 0 : << std::hex << *(reinterpret_cast<uint64_t*>(*buffer_from_device) + ii + 1);
2325 0 : delete[] buffer_from_file;
2326 0 : is.close();
2327 0 : if (writeOutput) outputStream.close();
2328 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2329 0 : return false;
2330 : }
2331 : }
2332 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2333 0 : }
2334 0 : else if (file_buffer_size > 0)
2335 : {
2336 0 : DTC_TLOG(TLVL_VerifySimFileInDTC2) << "VerifySimFileInDTC DTC memory is now full. Closing file.";
2337 0 : sizeCheck = false;
2338 : }
2339 0 : delete[] buffer_from_file;
2340 : }
2341 :
2342 0 : DTC_TLOG(TLVL_VerifySimFileInDTC) << "VerifySimFileInDTC Closing file. sizecheck=" << sizeCheck << ", eof=" << is.eof()
2343 0 : << ", fail=" << is.fail() << ", bad=" << is.bad();
2344 0 : __COUT_INFO__ << "VerifySimFileInDTC: The Detector Emulation file was written correctly";
2345 0 : is.close();
2346 0 : if (writeOutput) outputStream.close();
2347 0 : return true;
2348 0 : }
2349 :
2350 : // ROC Register Functions
2351 0 : uint16_t DTCLib::DTC::ReadROCRegister(const DTC_Link_ID& link, const uint16_t address, int tmo_ms)
2352 : {
2353 0 : uint16_t retries = 0; // change to 1 to attempt reinitializing
2354 : do
2355 : {
2356 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2357 :
2358 0 : device_.begin_dcs_transaction();
2359 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2360 0 : SendDCSRequestPacket(link, DTC_DCSOperationType_Read, address,
2361 : 0x0 /*data*/, 0x0 /*address2*/, 0x0 /*data2*/,
2362 : false /*quiet*/);
2363 :
2364 0 : uint16_t data = 0xFFFF;
2365 :
2366 : try
2367 : {
2368 0 : auto reply = ReadNextDCSPacket(tmo_ms);
2369 0 : device_.end_dcs_transaction();
2370 :
2371 0 : if (reply != nullptr) // have data!
2372 : {
2373 0 : auto replytmp = reply->GetReply(false);
2374 0 : auto linktmp = reply->GetLinkID();
2375 0 : data = replytmp.second;
2376 :
2377 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2378 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2379 0 : << "address=" << static_cast<int>(replytmp.first) << " (expected " << static_cast<int>(address)
2380 0 : << "), "
2381 0 : << "data=" << data;
2382 0 : if (linktmp == link && replytmp.first == address)
2383 0 : return data;
2384 : else
2385 : {
2386 0 : __SS__ << "Mismatch identified in link=" << linktmp << " != " << link << " or "
2387 0 : << "address=" << replytmp.first << " != " << address << ". Corrupt ROC response?" << __E__;
2388 : ss << "\n\nIf interpreting as a DTC_DCSReplyPacket, here is the data: \n"
2389 0 : << reply->toJSON();
2390 0 : __SS_THROW__;
2391 0 : }
2392 : }
2393 0 : }
2394 0 : catch (const std::exception& e)
2395 : {
2396 0 : __SS__ << "Failure attempting to read a ROC register at link " << static_cast<int>(link) << " address 0x" << std::hex << static_cast<int>(address) << ". Exception caught: " << e.what() << __E__;
2397 0 : __SS_THROW__;
2398 0 : }
2399 :
2400 : // if here then software received no response from DTC, try a software re-init to realign DMA pointers
2401 0 : if (retries) // do not reinit on last try
2402 : {
2403 0 : __COUT__ << "Software received no response to the DCS request from the DTC, trying a DMA re-init. retries = " << retries << __E__;
2404 0 : device_.initDMAEngine();
2405 : }
2406 :
2407 0 : } while (retries--);
2408 :
2409 : // throw exception for no data after retries
2410 0 : __SS__ << "A timeout occurred attempting to read a ROC register at link " << static_cast<int>(link) << " address 0x" << std::hex << static_cast<int>(address) << ". No DCS reply packet received after " << std::dec << tmo_ms << " ms! "
2411 0 : << "Check the clocks and that the ROC link is enabled and locked. Restarting the DTC software instance may fix the problem and realign DMA pointers." << std::endl;
2412 :
2413 0 : if (TTEST(20))
2414 : {
2415 0 : __COUT_ERR__ << "\n"
2416 0 : << ss.str();
2417 0 : device_.spy(DTC_DMA_Engine_DCS, 3 /* for once */ | 8 /* for wide view */);
2418 0 : __COUT_ERR__ << otsStyleStackTrace();
2419 : }
2420 :
2421 0 : __SS_THROW__;
2422 0 : } // end ReadROCRegister()
2423 :
2424 0 : bool DTCLib::DTC::WriteROCRegister(const DTC_Link_ID& link, const uint16_t address, const uint16_t data, bool requestAck, int ack_tmo_ms)
2425 : {
2426 0 : device_.begin_dcs_transaction();
2427 0 : if (requestAck)
2428 : {
2429 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2430 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2431 : }
2432 0 : SendDCSRequestPacket(link, DTC_DCSOperationType_Write, address, data,
2433 : 0x0 /*address2*/, 0x0 /*data2*/,
2434 : false /*quiet*/, requestAck);
2435 :
2436 0 : bool ackReceived = false;
2437 0 : if (requestAck)
2438 : {
2439 0 : DTC_TLOG(TLVL_TRACE) << "WriteROCRegister: Checking for ack";
2440 0 : auto reply = ReadNextDCSPacket(ack_tmo_ms);
2441 0 : while (reply != nullptr)
2442 : {
2443 0 : auto reply1tmp = reply->GetReply(false);
2444 0 : auto linktmp = reply->GetLinkID();
2445 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2446 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2447 0 : << "address1=" << static_cast<int>(reply1tmp.first) << " (expected "
2448 0 : << static_cast<int>(address) << "), "
2449 0 : << "data1=" << static_cast<int>(reply1tmp.second);
2450 :
2451 0 : reply.reset(nullptr);
2452 0 : if (reply1tmp.first != address || linktmp != link || !reply->IsAckRequested())
2453 : {
2454 0 : DTC_TLOG(TLVL_TRACE) << "Address or link did not match, or ack bit was not set, reading next packet!";
2455 0 : reply = ReadNextDCSPacket(ack_tmo_ms); // Read the next packet
2456 : }
2457 : else
2458 : {
2459 0 : ackReceived = true;
2460 : }
2461 : }
2462 0 : }
2463 0 : device_.end_dcs_transaction();
2464 0 : return !requestAck || ackReceived;
2465 : }
2466 :
2467 0 : std::pair<uint16_t, uint16_t> DTCLib::DTC::ReadROCRegisters(const DTC_Link_ID& link, const uint16_t address1,
2468 : const uint16_t address2, int tmo_ms)
2469 : {
2470 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2471 :
2472 0 : device_.begin_dcs_transaction();
2473 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2474 0 : SendDCSRequestPacket(link, DTC_DCSOperationType_Read, address1, 0, address2);
2475 0 : usleep(2500);
2476 0 : uint16_t data1 = 0xFFFF;
2477 0 : uint16_t data2 = 0xFFFF;
2478 :
2479 0 : auto reply = ReadNextDCSPacket(tmo_ms);
2480 :
2481 0 : while (reply != nullptr)
2482 : {
2483 0 : auto reply1tmp = reply->GetReply(false);
2484 0 : auto reply2tmp = reply->GetReply(true);
2485 0 : auto linktmp = reply->GetLinkID();
2486 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2487 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2488 0 : << "address1=" << static_cast<int>(reply1tmp.first) << " (expected "
2489 0 : << static_cast<int>(address1) << "), "
2490 0 : << "data1=" << static_cast<int>(reply1tmp.second)
2491 0 : << "address2=" << static_cast<int>(reply2tmp.first) << " (expected "
2492 0 : << static_cast<int>(address2) << "), "
2493 0 : << "data2=" << static_cast<int>(reply2tmp.second);
2494 :
2495 0 : reply.reset(nullptr);
2496 0 : if (reply1tmp.first != address1 || reply2tmp.first != address2 || linktmp != link)
2497 : {
2498 0 : DTC_TLOG(TLVL_TRACE) << "Address or link did not match, reading next packet!";
2499 0 : reply = ReadNextDCSPacket(tmo_ms); // Read the next packet
2500 0 : continue;
2501 0 : }
2502 : else
2503 : {
2504 0 : data1 = reply1tmp.second;
2505 0 : data2 = reply2tmp.second;
2506 : }
2507 : }
2508 0 : device_.end_dcs_transaction();
2509 0 : DTC_TLOG(TLVL_TRACE) << "ReadROCRegisters returning " << static_cast<int>(data1) << " for link " << static_cast<int>(link)
2510 0 : << ", address " << static_cast<int>(address1) << ", " << static_cast<int>(data2) << ", address "
2511 0 : << static_cast<int>(address2);
2512 0 : return std::make_pair(data1, data2);
2513 0 : }
2514 :
2515 0 : bool DTCLib::DTC::WriteROCRegisters(const DTC_Link_ID& link, const uint16_t address1, const uint16_t data1,
2516 : const uint16_t address2, const uint16_t data2, bool requestAck, int ack_tmo_ms)
2517 : {
2518 0 : device_.begin_dcs_transaction();
2519 0 : if (requestAck)
2520 : {
2521 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2522 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2523 : }
2524 0 : SendDCSRequestPacket(link, DTC_DCSOperationType_Write, address1, data1, address2, data2, false /*quiet*/, requestAck);
2525 :
2526 0 : bool ackReceived = false;
2527 0 : if (requestAck)
2528 : {
2529 0 : DTC_TLOG(TLVL_TRACE) << "WriteROCRegisters: Checking for ack";
2530 0 : auto reply = ReadNextDCSPacket(ack_tmo_ms);
2531 0 : while (reply != nullptr)
2532 : {
2533 0 : auto reply1tmp = reply->GetReply(false);
2534 0 : auto reply2tmp = reply->GetReply(true);
2535 0 : auto linktmp = reply->GetLinkID();
2536 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2537 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2538 0 : << "address1=" << static_cast<int>(reply1tmp.first) << " (expected "
2539 0 : << static_cast<int>(address1) << "), "
2540 0 : << "data1=" << static_cast<int>(reply1tmp.second)
2541 0 : << "address2=" << static_cast<int>(reply2tmp.first) << " (expected "
2542 0 : << static_cast<int>(address2) << "), "
2543 0 : << "data2=" << static_cast<int>(reply2tmp.second);
2544 :
2545 0 : reply.reset(nullptr);
2546 0 : if (reply1tmp.first != address1 || reply2tmp.first != address2 || linktmp != link || !reply->IsAckRequested())
2547 : {
2548 0 : DTC_TLOG(TLVL_TRACE) << "Address or link did not match, or ack bit was not set, reading next packet!";
2549 0 : reply = ReadNextDCSPacket(ack_tmo_ms); // Read the next packet
2550 : }
2551 : else
2552 : {
2553 0 : ackReceived = true;
2554 : }
2555 : }
2556 0 : }
2557 0 : device_.end_dcs_transaction();
2558 0 : return !requestAck || ackReceived;
2559 : }
2560 :
2561 0 : void DTCLib::DTC::ReadROCBlock(
2562 : std::vector<uint16_t>& data,
2563 : const DTC_Link_ID& link, const uint16_t address,
2564 : const uint16_t wordCount, bool incrementAddress, int tmo_ms)
2565 : {
2566 0 : DTC_DCSRequestPacket req(link, DTC_DCSOperationType_BlockRead, false, incrementAddress, address, wordCount);
2567 :
2568 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "ReadROCBlock before WriteDMADCSPacket - DTC_DCSRequestPacket";
2569 :
2570 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2571 :
2572 0 : if (!ReadDCSReception()) EnableDCSReception();
2573 :
2574 0 : device_.begin_dcs_transaction();
2575 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2576 0 : WriteDMAPacket(req);
2577 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "ReadROCBlock after WriteDMADCSPacket - DTC_DCSRequestPacket";
2578 :
2579 0 : usleep(2500);
2580 :
2581 0 : auto reply = ReadNextDCSPacket(tmo_ms);
2582 0 : while (reply != nullptr)
2583 : {
2584 0 : auto replytmp = reply->GetReply(false);
2585 0 : auto linktmp = reply->GetLinkID();
2586 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2587 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2588 0 : << "address=" << static_cast<int>(replytmp.first) << " (expected " << static_cast<int>(address)
2589 0 : << "), "
2590 0 : << "wordCount=" << static_cast<int>(replytmp.second);
2591 :
2592 0 : data = reply->GetBlockReadData();
2593 0 : auto packetCount = reply->GetBlockPacketCount();
2594 0 : reply.reset(nullptr);
2595 0 : if (replytmp.first != address || linktmp != link)
2596 : {
2597 0 : DTC_TLOG(TLVL_TRACE) << "Address or link did not match, reading next packet!";
2598 0 : reply = ReadNextDCSPacket(tmo_ms); // Read the next packet
2599 0 : continue;
2600 0 : }
2601 :
2602 0 : auto wordCount = replytmp.second;
2603 0 : auto processedWords = 3;
2604 :
2605 0 : while (packetCount > 0)
2606 : {
2607 0 : dcsDMAInfo_.lastReadPtr = reinterpret_cast<uint8_t*>(dcsDMAInfo_.lastReadPtr) + 16;
2608 0 : auto dataPacket = new DTC_DataPacket(dcsDMAInfo_.lastReadPtr);
2609 0 : if (dataPacket == nullptr) break;
2610 :
2611 0 : DTC_TLOG(TLVL_TRACE) << "ReadROCBlock: next data packet: " << dataPacket->toJSON();
2612 0 : auto byteInPacket = 0;
2613 :
2614 0 : while (wordCount - processedWords > 0 && byteInPacket < 16)
2615 : {
2616 0 : uint16_t thisWord = dataPacket->GetByte(byteInPacket) + (dataPacket->GetByte(byteInPacket + 1) << 8);
2617 0 : byteInPacket += 2;
2618 0 : data.push_back(thisWord);
2619 0 : processedWords++;
2620 : }
2621 :
2622 0 : packetCount--;
2623 : }
2624 : }
2625 0 : device_.end_dcs_transaction();
2626 :
2627 0 : DTC_TLOG(TLVL_TRACE) << "ReadROCBlock returning " << static_cast<int>(data.size()) << " words for link " << static_cast<int>(link)
2628 0 : << ", address " << static_cast<int>(address);
2629 0 : }
2630 :
2631 0 : bool DTCLib::DTC::WriteROCBlock(const DTC_Link_ID& link, const uint16_t address,
2632 : const std::vector<uint16_t>& blockData, bool requestAck, bool incrementAddress, int ack_tmo_ms)
2633 : {
2634 0 : DTC_DCSRequestPacket req(link, DTC_DCSOperationType_BlockWrite, requestAck, incrementAddress, address);
2635 0 : req.SetBlockWriteData(blockData);
2636 :
2637 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "WriteROCBlock before WriteDMADCSPacket - DTC_DCSRequestPacket";
2638 :
2639 0 : if (!ReadDCSReception()) EnableDCSReception();
2640 :
2641 0 : device_.begin_dcs_transaction();
2642 0 : if (requestAck)
2643 : {
2644 0 : dcsDMAInfo_.currentReadPtr = nullptr;
2645 0 : ReleaseAllBuffers(DTC_DMA_Engine_DCS);
2646 : }
2647 0 : WriteDMAPacket(req);
2648 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "WriteROCBlock after WriteDMADCSPacket - DTC_DCSRequestPacket";
2649 :
2650 0 : bool ackReceived = false;
2651 0 : if (requestAck)
2652 : {
2653 0 : DTC_TLOG(TLVL_TRACE) << "WriteROCBlock: Checking for ack";
2654 0 : auto reply = ReadNextDCSPacket(ack_tmo_ms);
2655 0 : while (reply != nullptr)
2656 : {
2657 0 : auto reply1tmp = reply->GetReply(false);
2658 0 : auto linktmp = reply->GetLinkID();
2659 0 : DTC_TLOG(TLVL_TRACE) << "Got packet, "
2660 0 : << "link=" << static_cast<int>(linktmp) << " (expected " << static_cast<int>(link) << "), "
2661 0 : << "address1=" << static_cast<int>(reply1tmp.first) << " (expected "
2662 0 : << static_cast<int>(address) << "), "
2663 0 : << "data1=" << static_cast<int>(reply1tmp.second);
2664 :
2665 0 : reply.reset(nullptr);
2666 0 : if (reply1tmp.first != address || linktmp != link || !reply->IsAckRequested())
2667 : {
2668 0 : DTC_TLOG(TLVL_TRACE) << "Address or link did not match, or ack bit was not set, reading next packet!";
2669 0 : reply = ReadNextDCSPacket(ack_tmo_ms); // Read the next packet
2670 : }
2671 : else
2672 : {
2673 0 : ackReceived = true;
2674 : }
2675 : }
2676 0 : }
2677 0 : device_.end_dcs_transaction();
2678 0 : return !requestAck || ackReceived;
2679 0 : }
2680 :
2681 0 : uint16_t DTCLib::DTC::ReadExtROCRegister(const DTC_Link_ID& link, const uint16_t block,
2682 : const uint16_t address, int tmo_ms)
2683 : {
2684 0 : uint16_t addressT = address & 0x7FFF;
2685 0 : WriteROCRegister(link, 12, block, false, tmo_ms);
2686 0 : WriteROCRegister(link, 13, addressT, false, tmo_ms);
2687 0 : WriteROCRegister(link, 13, addressT | 0x8000, false, tmo_ms);
2688 0 : return ReadROCRegister(link, 22, tmo_ms);
2689 : }
2690 :
2691 0 : bool DTCLib::DTC::WriteExtROCRegister(const DTC_Link_ID& link, const uint16_t block,
2692 : const uint16_t address,
2693 : const uint16_t data, bool requestAck, int ack_tmo_ms)
2694 : {
2695 0 : uint16_t dataT = data & 0x7FFF;
2696 0 : bool success = true;
2697 0 : success &= WriteROCRegister(link, 12, block + (address << 8), requestAck, ack_tmo_ms);
2698 0 : success &= WriteROCRegister(link, 13, dataT, requestAck, ack_tmo_ms);
2699 0 : success &= WriteROCRegister(link, 13, dataT | 0x8000, requestAck, ack_tmo_ms);
2700 0 : return success;
2701 : }
2702 :
2703 0 : std::string DTCLib::DTC::ROCRegDump(const DTC_Link_ID& link)
2704 : {
2705 0 : std::ostringstream o;
2706 0 : o.setf(std::ios_base::boolalpha);
2707 0 : o << "{";
2708 0 : o << "\"Forward Detector 0 Status\": " << ReadExtROCRegister(link, 8, 0) << ",\n";
2709 0 : o << "\"Forward Detector 1 Status\": " << ReadExtROCRegister(link, 9, 0) << ",\n";
2710 0 : o << "\"Command Handler Status\": " << ReadExtROCRegister(link, 10, 0) << ",\n";
2711 0 : o << "\"Packet Sender 0 Status\": " << ReadExtROCRegister(link, 11, 0) << ",\n";
2712 0 : o << "\"Packet Sender 1 Status\": " << ReadExtROCRegister(link, 12, 0) << ",\n";
2713 0 : o << "\"Forward Detector 0 Errors\": " << ReadExtROCRegister(link, 8, 1) << ",\n";
2714 0 : o << "\"Forward Detector 1 Errors\": " << ReadExtROCRegister(link, 9, 1) << ",\n";
2715 0 : o << "\"Command Handler Errors\": " << ReadExtROCRegister(link, 10, 1) << ",\n";
2716 0 : o << "\"Packet Sender 0 Errors\": " << ReadExtROCRegister(link, 11, 1) << ",\n";
2717 0 : o << "\"Packet Sender 1 Errors\": " << ReadExtROCRegister(link, 12, 1) << "\n";
2718 0 : o << "}";
2719 :
2720 0 : return o.str();
2721 0 : }
2722 :
2723 0 : void DTCLib::DTC::SendHeartbeatPacket(const DTC_Link_ID& link, const DTC_EventWindowTag& when, bool quiet)
2724 : {
2725 0 : DTC_HeartbeatPacket req(link, when);
2726 0 : DTC_TLOG(TLVL_SendHeartbeatPacket) << "SendHeartbeatPacket before WriteDMADAQPacket - DTC_HeartbeatPacket";
2727 0 : if (!quiet) DTC_TLOG(TLVL_SendHeartbeatPacket) << req.toJSON();
2728 0 : WriteDMAPacket(req);
2729 0 : DTC_TLOG(TLVL_SendHeartbeatPacket) << "SendHeartbeatPacket after WriteDMADAQPacket - DTC_HeartbeatPacket";
2730 0 : }
2731 :
2732 : // Note! Before calling this function SendDCSRequestPacket(), the device must be locked with device_.begin_dcs_transaction();
2733 0 : void DTCLib::DTC::SendDCSRequestPacket(const DTC_Link_ID& link, const DTC_DCSOperationType type, const uint16_t address,
2734 : const uint16_t data, const uint16_t address2, const uint16_t data2, bool quiet, bool requestAck)
2735 : {
2736 0 : DTC_DCSRequestPacket req(link, type, requestAck, false /*incrementAddress*/, address, data);
2737 :
2738 0 : if (!quiet) DTC_TLOG(TLVL_SendDCSRequestPacket) << "Init DCS Packet: \n"
2739 0 : << req.toJSON();
2740 :
2741 0 : if (type == DTC_DCSOperationType_DoubleRead ||
2742 : type == DTC_DCSOperationType_DoubleWrite)
2743 : {
2744 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "Double operation enabled!";
2745 0 : req.AddRequest(address2, data2);
2746 : }
2747 :
2748 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "SendDCSRequestPacket before WriteDMADCSPacket - DTC_DCSRequestPacket";
2749 :
2750 0 : if (!quiet) DTC_TLOG(TLVL_SendDCSRequestPacket) << "Sending DCS Packet: \n"
2751 0 : << req.toJSON();
2752 :
2753 0 : if (!ReadDCSReception()) EnableDCSReception();
2754 :
2755 0 : WriteDMAPacket(req);
2756 0 : DTC_TLOG(TLVL_SendDCSRequestPacket) << "SendDCSRequestPacket after WriteDMADCSPacket - DTC_DCSRequestPacket";
2757 0 : }
2758 :
2759 0 : std::unique_ptr<DTCLib::DTC_Event> DTCLib::DTC::ReadNextDAQDMA(int tmo_ms)
2760 : {
2761 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA BEGIN";
2762 :
2763 0 : if (daqDMAInfo_.currentReadPtr != nullptr)
2764 : {
2765 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA BEFORE BUFFER CHECK daqDMAInfo_.currentReadPtr="
2766 0 : << (void*)daqDMAInfo_.currentReadPtr << " *nextReadPtr_=0x" << std::hex
2767 0 : << *(uint16_t*)daqDMAInfo_.currentReadPtr;
2768 : }
2769 : else
2770 : {
2771 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA BEFORE BUFFER CHECK daqDMAInfo_.currentReadPtr=nullptr";
2772 : }
2773 :
2774 0 : auto index = CFOandDTC_DMAs::GetCurrentBuffer(&daqDMAInfo_);
2775 :
2776 : // Need new buffer if GetCurrentBuffer returns -1 (no buffers) or -2 (done with all held buffers)
2777 0 : if (index < 0)
2778 : {
2779 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA Obtaining new DAQ Buffer";
2780 :
2781 0 : void* oldBufferPtr = nullptr;
2782 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
2783 0 : auto sts = ReadBuffer(DTC_DMA_Engine_DAQ, tmo_ms); // does return code
2784 0 : if (sts <= 0)
2785 : {
2786 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA: ReadBuffer returned " << sts << ", returning nullptr";
2787 0 : return nullptr;
2788 : }
2789 : // MUST BE ABLE TO HANDLE daqbuffer_==nullptr OR retry forever?
2790 0 : daqDMAInfo_.currentReadPtr = &daqDMAInfo_.buffer.back()[0];
2791 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA daqDMAInfo_.currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr
2792 0 : << " *daqDMAInfo_.currentReadPtr=0x" << std::hex << *(unsigned*)daqDMAInfo_.currentReadPtr
2793 0 : << " lastReadPtr_=" << (void*)daqDMAInfo_.lastReadPtr;
2794 0 : void* bufferIndexPointer = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2795 0 : if (daqDMAInfo_.currentReadPtr == oldBufferPtr && daqDMAInfo_.bufferIndex == *static_cast<uint32_t*>(bufferIndexPointer))
2796 : {
2797 0 : DTC_TLOG(TLVL_WARN) << "ReadNextDAQDMA: New buffer is the same as old. Releasing buffer and returning nullptr";
2798 0 : daqDMAInfo_.currentReadPtr = nullptr;
2799 : // We didn't actually get a new buffer...this probably means there's no more data
2800 : // Try and see if we're merely stuck...hopefully, all the data is out of the buffers...
2801 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2802 0 : return nullptr;
2803 : }
2804 0 : daqDMAInfo_.bufferIndex++;
2805 :
2806 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2807 0 : *static_cast<uint32_t*>(daqDMAInfo_.currentReadPtr) = daqDMAInfo_.bufferIndex;
2808 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2809 :
2810 0 : index = daqDMAInfo_.buffer.size() - 1;
2811 : }
2812 :
2813 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "Creating DTC_Event from current DMA Buffer";
2814 : // Utilities::PrintBuffer(daqDMAInfo_.currentReadPtr, 128, TLVL_ReadNextDAQPacket);
2815 0 : auto res = std::make_unique<DTC_Event>(daqDMAInfo_.currentReadPtr); // only does setup of Event Header
2816 :
2817 0 : auto eventByteCount = res->GetEventByteCount();
2818 0 : if (eventByteCount == 0)
2819 : {
2820 0 : __SS__ << "Event inclusive byte count cannot be zero!" << __E__;
2821 0 : __SS_THROW__;
2822 0 : }
2823 0 : size_t remainingBufferSize = CFOandDTC_DMAs::GetBufferByteCount(&daqDMAInfo_, index) - sizeof(uint64_t);
2824 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "eventByteCount: " << eventByteCount << ", remainingBufferSize: " << remainingBufferSize;
2825 : // Check for continued DMA
2826 0 : if (eventByteCount > remainingBufferSize)
2827 : {
2828 : // We're going to set lastReadPtr here, so that if this buffer isn't used by GetData, we start at the beginning of this event next time
2829 0 : daqDMAInfo_.lastReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr);
2830 :
2831 0 : auto inmem = std::make_unique<DTC_Event>(eventByteCount);
2832 0 : memcpy(const_cast<void*>(inmem->GetRawBufferPointer()), res->GetRawBufferPointer(), remainingBufferSize);
2833 :
2834 0 : auto bytes_read = remainingBufferSize;
2835 0 : while (bytes_read < eventByteCount)
2836 : {
2837 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA Obtaining new DAQ Buffer, bytes_read=" << bytes_read << ", eventByteCount=" << eventByteCount;
2838 :
2839 0 : void* oldBufferPtr = nullptr;
2840 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
2841 0 : auto sts = ReadBuffer(DTC_DMA_Engine_DAQ, tmo_ms); // does return code
2842 0 : if (sts <= 0)
2843 : {
2844 0 : DTC_TLOG(TLVL_WARN) << "ReadNextDAQDMA: ReadBuffer returned " << sts << ", returning nullptr";
2845 0 : return nullptr;
2846 : }
2847 : // MUST BE ABLE TO HANDLE daqbuffer_==nullptr OR retry forever?
2848 0 : daqDMAInfo_.currentReadPtr = &daqDMAInfo_.buffer.back()[0];
2849 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA daqDMAInfo_.currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr
2850 0 : << " *daqDMAInfo_.currentReadPtr=0x" << std::hex << *(unsigned*)daqDMAInfo_.currentReadPtr
2851 0 : << " lastReadPtr_=" << (void*)daqDMAInfo_.lastReadPtr;
2852 0 : void* bufferIndexPointer = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2853 0 : if (daqDMAInfo_.currentReadPtr == oldBufferPtr && daqDMAInfo_.bufferIndex == *static_cast<uint32_t*>(bufferIndexPointer))
2854 : {
2855 0 : DTC_TLOG(TLVL_WARN) << "ReadNextDAQDMA: New buffer is the same as old. Releasing buffer and returning nullptr";
2856 0 : daqDMAInfo_.currentReadPtr = nullptr;
2857 : // We didn't actually get a new buffer...this probably means there's no more data
2858 : // Try and see if we're merely stuck...hopefully, all the data is out of the buffers...
2859 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2860 0 : return nullptr;
2861 : }
2862 0 : daqDMAInfo_.bufferIndex++;
2863 :
2864 0 : size_t buffer_size = *static_cast<uint16_t*>(daqDMAInfo_.currentReadPtr);
2865 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2866 0 : *static_cast<uint32_t*>(daqDMAInfo_.currentReadPtr) = daqDMAInfo_.bufferIndex;
2867 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2868 :
2869 0 : size_t remainingEventSize = eventByteCount - bytes_read;
2870 0 : size_t copySize = remainingEventSize < buffer_size - 8 ? remainingEventSize : buffer_size - 8;
2871 0 : memcpy(const_cast<uint8_t*>(static_cast<const uint8_t*>(inmem->GetRawBufferPointer()) + bytes_read), daqDMAInfo_.currentReadPtr, copySize);
2872 0 : bytes_read += buffer_size - 8;
2873 :
2874 : // Increment by the size of the data block
2875 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + copySize;
2876 : }
2877 :
2878 0 : res.swap(inmem);
2879 0 : }
2880 : else // Event not split over multiple DMAs
2881 : {
2882 : // Update the packet pointers
2883 :
2884 : // lastReadPtr_ is easy...
2885 0 : daqDMAInfo_.lastReadPtr = daqDMAInfo_.currentReadPtr;
2886 :
2887 : // Increment by the size of the data block
2888 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + res->GetEventByteCount();
2889 : }
2890 0 : res->SetupEvent(); // does setup of Event header + all payload
2891 :
2892 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQDMA: RETURN";
2893 0 : return res;
2894 0 : } // end ReadNextDAQDMA()
2895 :
2896 0 : bool DTCLib::DTC::ReadNextDAQSubEventDMA(std::vector<std::unique_ptr<DTC_SubEvent>>& output, int tmo_ms)
2897 : {
2898 0 : TRACE_EXIT
2899 : {
2900 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA EXIT"
2901 0 : << " currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr
2902 0 : << " lastReadPtr=" << (void*)daqDMAInfo_.lastReadPtr
2903 0 : << " buffer.size()=" << daqDMAInfo_.buffer.size();
2904 0 : };
2905 :
2906 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA BEGIN";
2907 :
2908 0 : if (daqDMAInfo_.currentReadPtr != nullptr)
2909 : {
2910 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA BEFORE BUFFER CHECK daqDMAInfo_.currentReadPtr="
2911 0 : << (void*)daqDMAInfo_.currentReadPtr << " currentBufferTransferSize=0x" << std::hex
2912 0 : << *(uint16_t*)daqDMAInfo_.currentReadPtr;
2913 : }
2914 : else
2915 : {
2916 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA BEFORE BUFFER CHECK daqDMAInfo_.currentReadPtr=nullptr";
2917 : }
2918 :
2919 0 : auto index = CFOandDTC_DMAs::GetCurrentBuffer(&daqDMAInfo_); // if buffers onhand, returns daqDMAInfo_.buffer.size().. which is count used by ReleaseBuffers()
2920 :
2921 0 : size_t metaBufferSize = 0;
2922 :
2923 : // Need new starting subevent buffer if GetCurrentBuffer returns -1 (no buffers) or -2 (done with all held buffers)
2924 0 : if (index < 0)
2925 : {
2926 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA Obtaining new DAQ Buffer";
2927 :
2928 0 : void* oldBufferPtr = nullptr;
2929 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
2930 0 : auto sts = ReadBuffer(DTC_DMA_Engine_DAQ, tmo_ms); // does return code
2931 0 : if (sts <= 0)
2932 : {
2933 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA: ReadBuffer returned " << sts << ", returning false";
2934 0 : return false;
2935 : }
2936 0 : metaBufferSize = sts;
2937 : // MUST BE ABLE TO HANDLE daqbuffer_==nullptr OR retry forever?
2938 0 : daqDMAInfo_.currentReadPtr = &daqDMAInfo_.buffer.back()[0];
2939 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA daqDMAInfo_.currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr
2940 0 : << " currentBufferTransferSize=0x" << std::hex << *(unsigned*)daqDMAInfo_.currentReadPtr
2941 0 : << " lastReadPtr=" << (void*)daqDMAInfo_.lastReadPtr;
2942 0 : void* bufferIndexPointer = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2943 0 : if (daqDMAInfo_.currentReadPtr == oldBufferPtr && daqDMAInfo_.bufferIndex == *static_cast<uint32_t*>(bufferIndexPointer))
2944 : {
2945 0 : daqDMAInfo_.currentReadPtr = nullptr;
2946 : // We didn't actually get a new buffer...this probably means there's no more data
2947 : // Try and see if we're merely stuck...hopefully, all the data is out of the buffers...
2948 0 : device_.read_release(DTC_DMA_Engine_DAQ, 1);
2949 0 : DTC_TLOG(TLVL_WARN)
2950 0 : << "ReadNextDAQSubEventDMA: New buffer was the same as old. Released buffer and returning false";
2951 0 : return false;
2952 : }
2953 0 : daqDMAInfo_.bufferIndex++;
2954 :
2955 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2956 0 : *static_cast<uint32_t*>(daqDMAInfo_.currentReadPtr) = daqDMAInfo_.bufferIndex;
2957 0 : daqDMAInfo_.currentReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) + 4;
2958 :
2959 0 : index = daqDMAInfo_.buffer.size() - 1;
2960 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "Creating DTC_SubEvent from new DMA Buffer, index=" << index << " in " << daqDMAInfo_.buffer.size() << " buffers.";
2961 : }
2962 : else // buffer already onhand
2963 : {
2964 0 : __SS__ << "Impossible buffer already onhand!" << __E__;
2965 0 : __SS_THROW__;
2966 :
2967 : // DTC_TLOG(TLVL_ReadNextDAQPacket) << "Creating DTC_SubEvent from current DMA Buffer, index=" << index <<
2968 : // " in " << daqDMAInfo_.buffer.size() << " buffers.";
2969 0 : }
2970 :
2971 0 : if ( // current read ptr sanity check
2972 0 : (void*)(static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) - 8) !=
2973 0 : (void*)(&daqDMAInfo_.buffer[index][0]))
2974 : {
2975 0 : __SS__ << "Impossible current buffer pointer for index " << index << ".. " << (void*)(static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr) - 8) << " != " << (void*)&daqDMAInfo_.buffer[index - 1][0] << __E__;
2976 0 : __SS_THROW__;
2977 0 : }
2978 :
2979 : // Utilities::PrintBuffer(daqDMAInfo_.currentReadPtr, 128, TLVL_ReadNextDAQPacket);
2980 :
2981 0 : size_t inBufferByteCount = CFOandDTC_DMAs::GetBufferByteCount(&daqDMAInfo_, index);
2982 : // Use DMA descriptor byte count from ReadBuffer (dont use the in-buffer DMA transfer size count because the DTC stacks sub-transfers!)
2983 : // Subtract 1 for tlast byte, but NOT when buffer is completely full (no room for tlast)
2984 0 : size_t remainingBufferSize = metaBufferSize < sizeof(mu2e_databuff_t) ? metaBufferSize - 1 : metaBufferSize;
2985 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "sizeof(DTC_SubEventHeader) = " << sizeof(DTC_SubEventHeader) << " GetBufferByteCount=" << inBufferByteCount
2986 0 : << " metaBufferSize=" << metaBufferSize;
2987 :
2988 : // Extract multiple subevents from buffer
2989 : // Note: the while loop condition allows entry when there is at least the
2990 : // per-subevent DMA transfer size (8 bytes) remaining. This covers three cases:
2991 : // 1) Only the 8-byte per-subevent prefix at the buffer tail (0 header bytes) -- entire subevent is in the next buffer
2992 : // 2) Partial subevent header straddles the buffer boundary (1..47 header bytes)
2993 : // 3) Full subevent header (and possibly payload) fits in the current buffer
2994 : // Cases 1 and 2 are handled by reading continuation buffer(s) and assembling contiguously.
2995 0 : while (remainingBufferSize >= sizeof(uint64_t))
2996 : {
2997 0 : remainingBufferSize -= sizeof(uint64_t); // remove per-subevent DMA transfer size from remaining byte count
2998 :
2999 : // Check if the subevent header is fully contained in the current buffer
3000 0 : bool headerSplitAcrossBuffers = (remainingBufferSize < sizeof(DTC_SubEventHeader));
3001 :
3002 0 : if (headerSplitAcrossBuffers)
3003 : {
3004 : // The subevent header straddles the DMA buffer boundary (or is entirely in the next buffer).
3005 : // We must read the continuation buffer to assemble the full header before we can
3006 : // determine the subevent byte count and proceed with normal processing.
3007 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA: subevent header split across DMA buffer boundary!"
3008 0 : << " partialBytes(remainingBufferSize)=" << remainingBufferSize << " < sizeof(DTC_SubEventHeader)=" << sizeof(DTC_SubEventHeader)
3009 0 : << " at currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr;
3010 :
3011 0 : daqDMAInfo_.lastReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr);
3012 :
3013 : // Save partial data from end of current buffer (may be 0 if only the per-subevent prefix was here)
3014 0 : size_t partialBytes = remainingBufferSize;
3015 :
3016 : // Read continuation buffer to get the rest
3017 0 : void* oldBufferPtr = nullptr;
3018 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
3019 :
3020 : int sts;
3021 0 : int retry = 10;
3022 0 : while ((sts = ReadBuffer(DTC_DMA_Engine_DAQ, 10 /* retries */)) <= 0 && --retry > 0)
3023 0 : usleep(1000);
3024 :
3025 0 : if (sts <= 0)
3026 : {
3027 0 : __SS__ << "Timeout after receiving only partial subevent header (" << partialBytes << "/" << sizeof(DTC_SubEventHeader) << " bytes)!"
3028 0 : << " currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr;
3029 0 : __SS_THROW__;
3030 0 : }
3031 :
3032 0 : auto* contBufferStart = &daqDMAInfo_.buffer.back()[0];
3033 0 : if (contBufferStart == oldBufferPtr)
3034 : {
3035 0 : __SS__ << "Received same buffer twice, only received partial subevent header!! partialBytes=" << partialBytes;
3036 0 : __SS_THROW__;
3037 0 : }
3038 0 : daqDMAInfo_.bufferIndex++;
3039 :
3040 : // Assemble a temporary buffer with at least the full header so we can read the subevent byte count.
3041 : // NOTE: continuation buffers do NOT have a per-sub-transfer DMA byte count header at offset 0.
3042 0 : size_t contBufferPayload = sts;
3043 0 : size_t headerBytesNeeded = sizeof(DTC_SubEventHeader) - partialBytes;
3044 :
3045 0 : if (contBufferPayload < headerBytesNeeded)
3046 : {
3047 0 : __SS__ << "Continuation buffer too small to complete subevent header! contBufferPayload=" << contBufferPayload
3048 0 : << " headerBytesNeeded=" << headerBytesNeeded;
3049 0 : __SS_THROW__;
3050 0 : }
3051 :
3052 : // Assemble the complete header from the partial data in the old buffer
3053 : // (if any) and the beginning of the continuation buffer.
3054 : uint8_t headerBuf[sizeof(DTC_SubEventHeader)];
3055 0 : if (partialBytes > 0)
3056 0 : memcpy(headerBuf, daqDMAInfo_.currentReadPtr, partialBytes);
3057 0 : memcpy(headerBuf + partialBytes, contBufferStart, headerBytesNeeded);
3058 :
3059 : // Extract subEventByteCount from the assembled header (first 25 bits of first 4 bytes)
3060 0 : auto subEventByteCount = static_cast<size_t>(
3061 0 : *reinterpret_cast<uint32_t*>(headerBuf) & 0x1FFFFFF);
3062 :
3063 0 : if (subEventByteCount < sizeof(DTC_SubEventHeader))
3064 : {
3065 0 : __SS__ << "SubEvent inclusive byte count (" << subEventByteCount << ") cannot be less than the size of the subevent header ("
3066 0 : << sizeof(DTC_SubEventHeader) << "-bytes)! (detected during split-header handling)" << __E__;
3067 0 : __SS_THROW__;
3068 0 : }
3069 :
3070 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA split-header: assembled subEventByteCount=" << subEventByteCount
3071 0 : << " (0x" << std::hex << subEventByteCount << ")"
3072 0 : << " partialBytes=" << std::dec << partialBytes << " contBufferPayload=" << contBufferPayload;
3073 :
3074 : // Allocate contiguous memory for the full subevent and copy data in
3075 0 : auto inmem = std::make_unique<DTC_SubEvent>(subEventByteCount);
3076 :
3077 : // Copy partial data from end of previous buffer (if any)
3078 0 : if (partialBytes > 0)
3079 0 : memcpy(const_cast<void*>(inmem->GetRawBufferPointer()), daqDMAInfo_.currentReadPtr, partialBytes);
3080 :
3081 : // Copy data from continuation buffer
3082 0 : size_t contBytesForSubEvent = subEventByteCount - partialBytes;
3083 0 : size_t contCopySize = contBytesForSubEvent < contBufferPayload ? contBytesForSubEvent : contBufferPayload;
3084 0 : memcpy(const_cast<uint8_t*>(static_cast<const uint8_t*>(inmem->GetRawBufferPointer()) + partialBytes),
3085 : contBufferStart, contCopySize);
3086 :
3087 0 : auto bytes_read = partialBytes + contCopySize;
3088 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(contBufferStart) + contCopySize;
3089 :
3090 0 : size_t lastBufferPayloadSize = contBufferPayload;
3091 0 : size_t lastCopySize = contCopySize;
3092 :
3093 : // If the subevent still needs more data from additional buffers, continue reading
3094 0 : while (bytes_read < subEventByteCount)
3095 : {
3096 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA split-header continuation: bytes_read=" << bytes_read
3097 0 : << " subEventByteCount=" << subEventByteCount;
3098 :
3099 0 : oldBufferPtr = nullptr;
3100 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
3101 :
3102 0 : retry = 10;
3103 0 : while ((sts = ReadBuffer(DTC_DMA_Engine_DAQ, 10 /* retries */)) <= 0 && --retry > 0)
3104 0 : usleep(1000);
3105 :
3106 0 : if (sts <= 0)
3107 : {
3108 0 : __SS__ << "Timeout after receiving only partial subevent (split-header)! bytes_read=" << bytes_read
3109 0 : << " subEventByteCount=" << subEventByteCount;
3110 0 : __SS_THROW__;
3111 0 : }
3112 :
3113 0 : daqDMAInfo_.currentReadPtr = &daqDMAInfo_.buffer.back()[0];
3114 0 : if (daqDMAInfo_.currentReadPtr == oldBufferPtr)
3115 : {
3116 0 : __SS__ << "Received same buffer twice during split-header subevent continuation! bytes_read=" << bytes_read;
3117 0 : __SS_THROW__;
3118 0 : }
3119 0 : daqDMAInfo_.bufferIndex++;
3120 :
3121 0 : size_t buffer_size = sts;
3122 0 : size_t remainingEventSize = subEventByteCount - bytes_read;
3123 0 : size_t copySize = remainingEventSize < buffer_size ? remainingEventSize : buffer_size;
3124 :
3125 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA split-header continuation buffer: buffer_size=" << buffer_size
3126 0 : << " copySize=" << copySize << " remainingEventSize=" << remainingEventSize;
3127 :
3128 0 : memcpy(const_cast<uint8_t*>(static_cast<const uint8_t*>(inmem->GetRawBufferPointer()) + bytes_read),
3129 0 : daqDMAInfo_.currentReadPtr, copySize);
3130 0 : bytes_read += buffer_size;
3131 :
3132 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + copySize;
3133 0 : lastBufferPayloadSize = buffer_size;
3134 0 : lastCopySize = copySize;
3135 : }
3136 :
3137 : // Now construct the SubEvent with the fully assembled data
3138 0 : auto res = std::make_unique<DTC_SubEvent>(inmem->GetRawBufferPointer());
3139 :
3140 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA split-header: tag=" << res->GetEventWindowTag().GetEventWindowTag(true)
3141 0 : << std::hex << "(0x" << res->GetEventWindowTag().GetEventWindowTag(true) << ")"
3142 0 : << " subEventByteCount=" << std::dec << subEventByteCount;
3143 :
3144 0 : res.swap(inmem);
3145 :
3146 : try
3147 : {
3148 0 : std::string accumulatedErrors = "";
3149 0 : auto ok = res->SetupSubEvent(accumulatedErrors);
3150 0 : if (!ok)
3151 : {
3152 0 : __SS__ << "SubEvent is corrupt (split-header)! EWT=" << res->GetEventWindowTag() << ".\n\nAccumulated Errors: " << accumulatedErrors << __E__;
3153 0 : __SS_THROW__;
3154 0 : }
3155 0 : }
3156 0 : catch (...)
3157 : {
3158 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */);
3159 0 : DTC_TLOG(TLVL_ERROR) << otsStyleStackTrace();
3160 0 : throw;
3161 0 : }
3162 :
3163 0 : output.push_back(std::move(res));
3164 :
3165 : // Update remaining buffer size for the last-read buffer to check for more subevents
3166 0 : remainingBufferSize = lastBufferPayloadSize - lastCopySize;
3167 :
3168 : // Skip past the next sub-transfer DMA byte count header (same as single-buffer path)
3169 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + sizeof(uint64_t);
3170 0 : }
3171 : else // subevent header fully in current buffer
3172 : {
3173 0 : auto res = std::make_unique<DTC_SubEvent>(daqDMAInfo_.currentReadPtr); // only does setup of SubEvent header!
3174 :
3175 0 : auto subEventByteCount = res->GetSubEventByteCount();
3176 0 : if (subEventByteCount < sizeof(DTC_SubEventHeader))
3177 : {
3178 0 : __SS__ << "SubEvent inclusive byte count cannot be less than the size of the subevent header (" << sizeof(DTC_SubEventHeader) << "-bytes)!" << __E__;
3179 0 : __SS_THROW__;
3180 0 : }
3181 :
3182 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "subevent tag=" << res->GetEventWindowTag().GetEventWindowTag(true) << std::hex << "(0x" << res->GetEventWindowTag().GetEventWindowTag(true) << ")"
3183 0 : << " inclusive byte count: 0x" << std::hex << subEventByteCount << " (" << std::dec << subEventByteCount << ") inclusive packets " << subEventByteCount / 16 << ", remaining buffer size: 0x" << std::hex << remainingBufferSize << " (" << std::dec << remainingBufferSize << ") this buffer in packets = " << (remainingBufferSize - sizeof(DTC_SubEventHeader)) / 16 << ". "
3184 0 : << "Total subevent packet count: " << (subEventByteCount - sizeof(DTC_SubEventHeader)) / 16;
3185 :
3186 : // Check for continued DMA
3187 0 : if (subEventByteCount > remainingBufferSize)
3188 : {
3189 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "subevent needs more data by bytes " << std::hex << subEventByteCount - remainingBufferSize << " (" << std::dec << subEventByteCount - remainingBufferSize << ") packets " << (subEventByteCount - remainingBufferSize) / 16 << ". subEventByteCount=" << subEventByteCount << " remainingBufferSize=" << remainingBufferSize;
3190 :
3191 : // We're going to set lastReadPtr here, so that if this buffer isn't used by GetData, we start at the beginning of this event next time
3192 0 : daqDMAInfo_.lastReadPtr = static_cast<uint8_t*>(daqDMAInfo_.currentReadPtr);
3193 :
3194 0 : auto inmem = std::make_unique<DTC_SubEvent>(subEventByteCount);
3195 :
3196 0 : if (TTEST(TLVL_NextSubEventCout)) // for debugging
3197 : {
3198 0 : std::cout << "1st DMA buffer res size=" << remainingBufferSize << "\n";
3199 0 : auto ptr = reinterpret_cast<const uint8_t*>(res->GetRawBufferPointer());
3200 0 : for (size_t i = 0; i < remainingBufferSize /* + 16 */; i += 4)
3201 0 : std::cout << std::dec << "res#" << i << "/" << remainingBufferSize << "(" << i / 16 << "/" << remainingBufferSize / 16 << ")" << std::hex << std::setw(8) << std::setfill('0') << *((uint32_t*)(&(ptr[i]))) << std::endl;
3202 : }
3203 :
3204 0 : memcpy(const_cast<void*>(inmem->GetRawBufferPointer()), res->GetRawBufferPointer(), remainingBufferSize);
3205 :
3206 0 : if (TTEST(TLVL_NextSubEventCout)) // for debugging
3207 : {
3208 0 : std::cout << "1st DMA buffer inmem size=" << remainingBufferSize << "\n";
3209 0 : auto ptr = reinterpret_cast<const uint8_t*>(inmem->GetRawBufferPointer());
3210 0 : for (size_t i = 0; i < remainingBufferSize + 16; i += 4)
3211 0 : std::cout << std::dec << "inmem#" << i << "(" << i / 16 << ")" << std::hex << std::setw(8) << std::setfill('0') << *((uint32_t*)(&(ptr[i]))) << std::endl;
3212 : }
3213 :
3214 0 : auto bytes_read = remainingBufferSize;
3215 0 : size_t lastBufferPayloadSize = 0;
3216 0 : size_t lastCopySize = 0;
3217 :
3218 0 : while (bytes_read < subEventByteCount)
3219 : {
3220 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA tag=" << inmem->GetEventWindowTag().GetEventWindowTag(true) << std::hex << "(0x" << inmem->GetEventWindowTag().GetEventWindowTag(true) << ")"
3221 0 : << " Obtaining new DAQ Buffer, bytes_read=" << bytes_read << ", subEventByteCount=" << subEventByteCount;
3222 :
3223 0 : void* oldBufferPtr = nullptr;
3224 0 : if (daqDMAInfo_.buffer.size() > 0) oldBufferPtr = &daqDMAInfo_.buffer.back()[0];
3225 :
3226 0 : if (TTEST(TLVL_NextSubEventCout)) // for debugging
3227 : {
3228 0 : std::cout << "1st DMA buffer\n";
3229 0 : auto ptr = reinterpret_cast<const uint8_t*>(&daqDMAInfo_.buffer.back()[0]);
3230 0 : for (size_t i = 0; i < bytes_read + sizeof(DTCLib::DTC_SubEventHeader); i += 4)
3231 0 : std::cout << std::dec << "#" << i << "(" << i / 16 << ")" << std::hex << std::setw(8) << std::setfill('0') << *((uint32_t*)(&(ptr[i]))) << std::endl;
3232 : }
3233 :
3234 : int sts;
3235 0 : int retry = 10;
3236 : // timeout is an exception at this point because no way to resolve partial subevent record!
3237 0 : while ((sts = ReadBuffer(DTC_DMA_Engine_DAQ, 10 /* retries */)) // does return code
3238 0 : <= 0 &&
3239 0 : --retry > 0) usleep(1000);
3240 :
3241 0 : if (sts <= 0)
3242 : {
3243 0 : __SS__ << "Timeout of " << tmo_ms << " ms after receiving only partial subevent! Subevent tag=" << inmem->GetEventWindowTag().GetEventWindowTag(true) << std::hex << "(0x" << inmem->GetEventWindowTag().GetEventWindowTag(true) << ")";
3244 0 : __SS_THROW__;
3245 0 : }
3246 :
3247 0 : daqDMAInfo_.currentReadPtr = &daqDMAInfo_.buffer.back()[0];
3248 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA daqDMAInfo_.currentReadPtr=" << (void*)daqDMAInfo_.currentReadPtr
3249 0 : << " *daqDMAInfo_.currentReadPtr=0x" << std::hex << *(unsigned*)daqDMAInfo_.currentReadPtr
3250 0 : << " lastReadPtr=" << (void*)daqDMAInfo_.lastReadPtr;
3251 :
3252 0 : if (TTEST(TLVL_NextSubEventCout)) // for debugging
3253 : {
3254 0 : std::cout << "1st buffer\n";
3255 0 : auto ptr = reinterpret_cast<const uint8_t*>(inmem->GetRawBufferPointer());
3256 0 : for (size_t i = 0; i < bytes_read + 16; i += 4)
3257 0 : std::cout << std::dec << "#" << i << "(" << i / 16 << ")" << std::hex << std::setw(8) << std::setfill('0') << *((uint32_t*)(&(ptr[i]))) << std::endl;
3258 : }
3259 :
3260 0 : if (daqDMAInfo_.currentReadPtr == oldBufferPtr)
3261 : {
3262 : // We didn't actually get a new buffer...this probably means there's no more data
3263 : // timeout is an exception at this point because no way to resolve partial subevent record!
3264 0 : __SS__ << "Received same buffer twice, only received partial subevent!! Subevent tag=" << inmem->GetEventWindowTag().GetEventWindowTag(true) << std::hex << "(0x" << inmem->GetEventWindowTag().GetEventWindowTag(true) << ")";
3265 0 : __SS_THROW__;
3266 0 : }
3267 0 : daqDMAInfo_.bufferIndex++;
3268 :
3269 : // NOTE: continuation buffers do NOT have a per-sub-transfer DMA byte count header at offset 0
3270 : // (unlike the initial buffer). The raw continuation data starts at offset 0.
3271 : // Use the full sts (DMA descriptor byte count) as payload size.
3272 0 : size_t buffer_size = sts;
3273 :
3274 0 : size_t remainingEventSize = subEventByteCount - bytes_read;
3275 0 : size_t copySize = remainingEventSize < buffer_size ? remainingEventSize : buffer_size;
3276 :
3277 : if (0) // for debugging
3278 : {
3279 : std::cout << "2nd buffer\n";
3280 : auto ptr = reinterpret_cast<const uint8_t*>(daqDMAInfo_.currentReadPtr);
3281 : for (size_t i = 0; i < copySize; i += 4)
3282 : std::cout << std::dec << "#" << i << "(" << i / 16 << "/" << copySize / 16 << ")" << std::hex << std::setw(8) << std::setfill('0') << *((uint32_t*)(&(ptr[i]))) << std::endl;
3283 : }
3284 :
3285 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA got new buffer for subevent continuation, buffer_size=" << buffer_size << " copySize=" << copySize << " remainingEventSize=" << remainingEventSize;
3286 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA so far bytes_read = " << bytes_read << " packets = " << bytes_read / 16 - sizeof(DTC_SubEventHeader) / 16;
3287 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA copySize = " << copySize << " packets = " << copySize / 16;
3288 0 : memcpy(const_cast<uint8_t*>(static_cast<const uint8_t*>(inmem->GetRawBufferPointer()) + bytes_read), daqDMAInfo_.currentReadPtr, copySize);
3289 0 : bytes_read += buffer_size;
3290 :
3291 : // Increment by the size of the data block
3292 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + copySize;
3293 :
3294 0 : lastBufferPayloadSize = buffer_size;
3295 0 : lastCopySize = copySize;
3296 : } // end primary continuation of multi-DMA subevent transfers
3297 :
3298 0 : res.swap(inmem);
3299 :
3300 : try
3301 : {
3302 0 : std::string accumulatedErrors = "";
3303 0 : auto ok = res->SetupSubEvent(accumulatedErrors); // does setup of SubEvent header + all payload
3304 0 : if (!ok)
3305 : {
3306 0 : __SS__ << "SubEvent is corrupt! EWT=" << res->GetEventWindowTag() << ".\n\nAccumulated Errors: " << accumulatedErrors << __E__;
3307 0 : __SS_THROW__;
3308 0 : }
3309 0 : }
3310 0 : catch (...)
3311 : {
3312 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */);
3313 0 : DTC_TLOG(TLVL_ERROR) << otsStyleStackTrace();
3314 0 : throw;
3315 0 : }
3316 :
3317 0 : output.push_back(std::move(res));
3318 :
3319 : // Update remaining buffer size for the last-read buffer to check for more subevents
3320 0 : remainingBufferSize = lastBufferPayloadSize - lastCopySize;
3321 :
3322 : // Skip past the next sub-transfer DMA byte count header (same as single-buffer path)
3323 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + sizeof(uint64_t);
3324 0 : }
3325 : else // SubEvent not split over multiple DMAs
3326 : {
3327 : // Update the packet pointers
3328 :
3329 : // lastReadPtr_ is easy...
3330 0 : daqDMAInfo_.lastReadPtr = daqDMAInfo_.currentReadPtr;
3331 :
3332 : // Increment by the size of the data block + skip past next sub-transfer DMA byte count
3333 0 : daqDMAInfo_.currentReadPtr = reinterpret_cast<char*>(daqDMAInfo_.currentReadPtr) + subEventByteCount + sizeof(uint64_t);
3334 0 : remainingBufferSize -= subEventByteCount;
3335 :
3336 : try
3337 : {
3338 0 : std::string accumulatedErrors = "";
3339 0 : auto ok = res->SetupSubEvent(accumulatedErrors); // does setup of SubEvent header + all payload
3340 0 : if (!ok)
3341 : {
3342 0 : __SS__ << "SubEvent is corrupt! EWT=" << res->GetEventWindowTag() << ".\n\nAccumulated Errors: " << accumulatedErrors << __E__;
3343 0 : __SS_THROW__;
3344 0 : }
3345 0 : }
3346 0 : catch (...)
3347 : {
3348 0 : device_.spy(DTC_DMA_Engine_DAQ, 3 /* for once */ | 8 /* for wide view */);
3349 0 : DTC_TLOG(TLVL_ERROR) << otsStyleStackTrace();
3350 0 : throw;
3351 0 : }
3352 :
3353 0 : output.push_back(std::move(res));
3354 : }
3355 0 : } // end subevent header fully in current buffer
3356 : } // end primary subevent extraction loop
3357 :
3358 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextDAQSubEventDMA: RETURN " << output.size() << " SubEvents";
3359 0 : return output.size() > 0;
3360 0 : } // end ReadNextDAQSubEventDMA()
3361 :
3362 0 : std::unique_ptr<DTCLib::DTC_DCSReplyPacket> DTCLib::DTC::ReadNextDCSPacket(int tmo_ms)
3363 : {
3364 : try
3365 : {
3366 0 : auto test = ReadNextPacket(DTC_DMA_Engine_DCS, tmo_ms);
3367 0 : if (test == nullptr) return nullptr; // Couldn't read new block
3368 :
3369 0 : __COUTT__ << "If interpreting as a DTC_DataPacket, here is the data: " << test->toJSON();
3370 :
3371 0 : auto output = std::make_unique<DTC_DCSReplyPacket>(*test.get());
3372 0 : if (output->ROCIsCorrupt())
3373 : {
3374 0 : __SS__ << "ROC has set its DCS corrupt flag (check the ROC error bit details)!" << __E__;
3375 0 : __SS_THROW__;
3376 0 : }
3377 0 : if (output->GetType() == DTC_DCSOperationType_InvalidS2C)
3378 : {
3379 0 : __SS__ << "DTC identifed an invalid DCS request from software!" << __E__;
3380 0 : __SS_THROW__;
3381 0 : }
3382 0 : if (output->GetType() == DTC_DCSOperationType_Timeout)
3383 : {
3384 0 : __SS__ << "No response from the ROC at link " << output->GetLinkID() << " to the DCS request! The DTC identifed a ROC response timeout!" << __E__;
3385 0 : __SS_THROW__;
3386 0 : }
3387 0 : if (lastDTCErrorBitsValue_ != output->GetDTCErrorBits()) // Note: DTC Error bits are only included in DCS reply packets
3388 : {
3389 0 : __COUTV__((int)output->GetDTCErrorBits());
3390 0 : __COUTV__((int)lastDTCErrorBitsValue_);
3391 0 : lastDTCErrorBitsValue_ = output->GetDTCErrorBits();
3392 :
3393 0 : __SS__ << "There was one or more errors identified in DCS handling of its ROC (a DTC Soft Reset will clear these errors):" << __E__;
3394 0 : if ((lastDTCErrorBitsValue_ >> 0) & 0x1)
3395 0 : ss << "\t* bit-0 is set: SERDES PLL associated with the ROC has lost lock." << __E__;
3396 0 : if ((lastDTCErrorBitsValue_ >> 1) & 0x1)
3397 0 : ss << "\t* bit-1 is set: SERDES clock-data-recovery associated with the ROC has lost lock." << __E__;
3398 0 : if ((lastDTCErrorBitsValue_ >> 2) & 0x1)
3399 0 : ss << "\t* bit-2 is set: Invalid packet (i.e., CRC mismatch) has been received from ROC." << __E__;
3400 0 : if ((lastDTCErrorBitsValue_ >> 3) & 0x1)
3401 0 : ss << "\t* bit-3 is set: Error in DTC handling of this ROC’s DCS requests has occurred (check the DTC error bit details)." << __E__;
3402 :
3403 0 : if (lastDTCErrorBitsValue_) // throw exception if error
3404 : {
3405 : ss << "\n\nIf interpreting as a DTC_DataPacket, here is the data: \n"
3406 0 : << test->toJSON();
3407 0 : __SS_THROW__;
3408 : }
3409 0 : }
3410 0 : if ((lastDTCErrorBitsValue_ >> 2) & 0x1) // Make sure CRC bit errors are always reported
3411 : {
3412 0 : __SS__ << "bit-2 is set: Invalid packet (i.e., CRC mismatch) has been received in response to the DCS request!" << __E__;
3413 : ss << "\n\nIf interpreting as a DTC_DataPacket, here is the data: \n"
3414 0 : << test->toJSON();
3415 0 : __SS_THROW__;
3416 0 : }
3417 :
3418 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << output->toJSON();
3419 0 : return output;
3420 0 : }
3421 0 : catch (...) // make sure the dcs transaction is ended on exception
3422 : {
3423 0 : device_.end_dcs_transaction();
3424 :
3425 0 : __COUT_ERR__ << "Error reading the next DCS packet!" << __E__;
3426 0 : throw;
3427 0 : }
3428 : }
3429 :
3430 0 : std::unique_ptr<DTCLib::DTC_DataPacket> DTCLib::DTC::ReadNextPacket(const DTC_DMA_Engine& engine, int tmo_ms)
3431 : {
3432 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket BEGIN";
3433 : CFOandDTC_DMAs::DMAInfo* info;
3434 0 : if (engine == DTC_DMA_Engine_DAQ)
3435 0 : info = &daqDMAInfo_;
3436 0 : else if (engine == DTC_DMA_Engine_DCS)
3437 0 : info = &dcsDMAInfo_;
3438 : else
3439 : {
3440 0 : DTC_TLOG(TLVL_ERROR) << "ReadNextPacket: Invalid DMA Engine specified!";
3441 0 : throw new DTC_DataCorruptionException();
3442 : }
3443 :
3444 0 : if (info->currentReadPtr != nullptr)
3445 : {
3446 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket BEFORE BUFFER CHECK info->currentReadPtr="
3447 0 : << (void*)info->currentReadPtr << " *nextReadPtr_=0x" << std::hex
3448 0 : << *(uint16_t*)info->currentReadPtr;
3449 : }
3450 : else
3451 : {
3452 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket BEFORE BUFFER CHECK info->currentReadPtr=nullptr";
3453 : }
3454 :
3455 0 : auto index = CFOandDTC_DMAs::GetCurrentBuffer(info);
3456 :
3457 : // Need new buffer if GetCurrentBuffer returns -1 (no buffers) or -2 (done with all held buffers)
3458 0 : if (index < 0)
3459 : {
3460 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket Obtaining new " << (engine == DTC_DMA_Engine_DAQ ? "DAQ" : "DCS")
3461 0 : << " Buffer";
3462 :
3463 0 : void* oldBufferPtr = nullptr;
3464 0 : if (info->buffer.size() > 0) oldBufferPtr = &info->buffer.back()[0];
3465 0 : auto sts = ReadBuffer(engine, tmo_ms); // does return code
3466 0 : if (sts <= 0)
3467 : {
3468 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: ReadBuffer returned " << sts << ", returning nullptr";
3469 0 : return nullptr;
3470 : }
3471 : // MUST BE ABLE TO HANDLE daqbuffer_==nullptr OR retry forever?
3472 0 : info->currentReadPtr = &info->buffer.back()[0];
3473 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket info->currentReadPtr=" << (void*)info->currentReadPtr
3474 0 : << " *info->currentReadPtr=0x" << std::hex << *(unsigned*)info->currentReadPtr
3475 0 : << " lastReadPtr_=" << (void*)info->lastReadPtr;
3476 0 : void* bufferIndexPointer = static_cast<uint8_t*>(info->currentReadPtr) + 4;
3477 0 : if (info->currentReadPtr == oldBufferPtr && info->bufferIndex == *static_cast<uint32_t*>(bufferIndexPointer))
3478 : {
3479 0 : DTC_TLOG(TLVL_ReadNextDAQPacket)
3480 0 : << "ReadNextPacket: New buffer is the same as old. Releasing buffer and returning nullptr";
3481 0 : info->currentReadPtr = nullptr;
3482 : // We didn't actually get a new buffer...this probably means there's no more data
3483 : // Try and see if we're merely stuck...hopefully, all the data is out of the buffers...
3484 0 : device_.read_release(engine, 1);
3485 0 : return nullptr;
3486 : }
3487 0 : info->bufferIndex++;
3488 :
3489 0 : info->currentReadPtr = reinterpret_cast<uint8_t*>(info->currentReadPtr) + 4;
3490 0 : *static_cast<uint32_t*>(info->currentReadPtr) = info->bufferIndex;
3491 0 : info->currentReadPtr = reinterpret_cast<uint8_t*>(info->currentReadPtr) + 4;
3492 :
3493 0 : index = info->buffer.size() - 1;
3494 : }
3495 :
3496 : // Read the next packet
3497 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket reading next packet from buffer: info->currentReadPtr="
3498 0 : << (void*)info->currentReadPtr;
3499 :
3500 0 : auto blockByteCount = *reinterpret_cast<uint16_t*>(info->currentReadPtr);
3501 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: blockByteCount=" << blockByteCount
3502 0 : << ", info->currentReadPtr=" << (void*)info->currentReadPtr
3503 0 : << ", *nextReadPtr=" << (int)*((uint16_t*)info->currentReadPtr);
3504 0 : if (blockByteCount == 0 || blockByteCount == 0xcafe)
3505 : {
3506 0 : auto test = std::make_unique<DTC_DataPacket>(info->currentReadPtr);
3507 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "Check bad data (interpreting as DTC_DataPacket): " << test->toJSON();
3508 :
3509 0 : if (static_cast<size_t>(index) < info->buffer.size() - 1)
3510 : {
3511 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: blockByteCount=" << blockByteCount << " (the first 16-bits) is invalid, moving to next buffer";
3512 0 : auto nextBufferPtr = *info->buffer[index + 1];
3513 0 : info->currentReadPtr = nextBufferPtr + 8; // Offset past DMA header
3514 0 : return ReadNextPacket(engine, tmo_ms); // Recursion
3515 : }
3516 : else
3517 : {
3518 0 : DTC_TLOG(TLVL_ReadNextDAQPacket)
3519 0 : << "ReadNextPacket: blockByteCount is invalid=" << blockByteCount << " (the first 16-bits), and this is the last buffer! Returning nullptr!";
3520 0 : info->currentReadPtr = nullptr;
3521 : // This buffer is invalid, release it!
3522 : // Try and see if we're merely stuck...hopefully, all the data is out of the buffers...
3523 0 : device_.read_release(engine, 1);
3524 :
3525 0 : __SS__ << "The DTC returned a packet with an invalid BlockByteCount=" << blockByteCount << " (the first 16-bits of the packet).\n\n";
3526 : ss << "\n\nIf interpreting as a DTC_DataPacket, here is the data: \n"
3527 0 : << test->toJSON();
3528 0 : __SS_THROW__;
3529 : // return nullptr;
3530 0 : }
3531 0 : }
3532 :
3533 0 : auto test = std::make_unique<DTC_DataPacket>(info->currentReadPtr);
3534 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: current+blockByteCount="
3535 0 : << (void*)(reinterpret_cast<uint8_t*>(info->currentReadPtr) + blockByteCount)
3536 0 : << ", end of dma buffer="
3537 0 : << (void*)(info->buffer[index][0] + CFOandDTC_DMAs::GetBufferByteCount(info, index) +
3538 0 : 8); // +8 because first 8 bytes are not included in byte count
3539 0 : if (reinterpret_cast<uint8_t*>(info->currentReadPtr) + blockByteCount >
3540 0 : info->buffer[index][0] + CFOandDTC_DMAs::GetBufferByteCount(info, index) + 8)
3541 : {
3542 0 : blockByteCount = static_cast<uint16_t>(
3543 0 : info->buffer[index][0] + CFOandDTC_DMAs::GetBufferByteCount(info, index) + 8 -
3544 0 : reinterpret_cast<uint8_t*>(info->currentReadPtr)); // +8 because first 8 bytes are not included in byte count
3545 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: Adjusting blockByteCount to " << blockByteCount
3546 0 : << " due to end-of-DMA condition";
3547 0 : test->SetByte(0, blockByteCount & 0xFF);
3548 0 : test->SetByte(1, (blockByteCount >> 8));
3549 : }
3550 :
3551 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << test->toJSON();
3552 :
3553 : // Update the packet pointers
3554 :
3555 : // lastReadPtr_ is easy...
3556 0 : info->lastReadPtr = info->currentReadPtr;
3557 :
3558 : // Increment by the size of the data block
3559 0 : info->currentReadPtr = reinterpret_cast<char*>(info->currentReadPtr) + blockByteCount;
3560 :
3561 0 : DTC_TLOG(TLVL_ReadNextDAQPacket) << "ReadNextPacket: RETURN";
3562 0 : return test;
3563 0 : }
3564 :
3565 0 : void DTCLib::DTC::WriteDetectorEmulatorData(mu2e_databuff_t* buf, size_t sz)
3566 : {
3567 0 : if (sz < dmaSize_)
3568 : {
3569 0 : sz = dmaSize_;
3570 : }
3571 0 : auto retry = 3;
3572 : int errorCode;
3573 : do
3574 : {
3575 0 : DTC_TLOG(TLVL_WriteDetectorEmulatorData) << "WriteDetectorEmulatorData: Writing buffer of size " << sz;
3576 0 : errorCode = device_.write_data(DTC_DMA_Engine_DAQ, buf, sz);
3577 0 : retry--;
3578 0 : } while (retry > 0 && errorCode != 0);
3579 0 : if (errorCode != 0)
3580 : {
3581 0 : DTC_TLOG(TLVL_ERROR) << "WriteDetectorEmulatorData: write_data returned " << errorCode
3582 0 : << ", throwing DTC_IOErrorException!";
3583 0 : throw DTC_IOErrorException(errorCode);
3584 : }
3585 0 : }
3586 :
3587 : //
3588 : // Private Functions.
3589 : // On success, returns number of bytes read.
3590 0 : int DTCLib::DTC::ReadBuffer(const DTC_DMA_Engine& channel, int retries /* = 10 */)
3591 : {
3592 : mu2e_databuff_t* buffer;
3593 :
3594 0 : int retry = 1;
3595 0 : if (retries > 0) retry = retries;
3596 :
3597 : int errorCode;
3598 0 : TRACE_EXIT
3599 : {
3600 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer found " << ((errorCode > 0) ? "DATA" : "NO Data") << ". There are now " << (channel == DTC_DMA_Engine_DAQ ? daqDMAInfo_.buffer.size() : dcsDMAInfo_.buffer.size()) << " DAQ buffers held in the DTC Library";
3601 0 : };
3602 :
3603 : do
3604 : {
3605 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer before device_.read_data retries=" << retries << " retry=" << retry;
3606 : // WARNING NOTE: if there is existing data still sitting in unreleased buffer, the timeout will not add any delay
3607 : // read_data() on success, returns number of bytes read.
3608 0 : errorCode = device_.read_data(channel, reinterpret_cast<void**>(&buffer), 1 /* tmo_ms */);
3609 : // NOTE: Adding delay here significantly impacts data rates!
3610 : // if (errorCode == 0) usleep(tmo_ms*1000); //create timeout delay here to match tmo (tmo not used by read_data())
3611 :
3612 0 : } while (retry-- > 0 && errorCode == 0); // error code of 0 is timeout
3613 :
3614 0 : if (errorCode == 0)
3615 : {
3616 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer: Device timeout occurred! ec=" << errorCode << ", rt=" << retry;
3617 : }
3618 0 : else if (errorCode < 0)
3619 : {
3620 0 : DTC_TLOG(TLVL_ERROR) << "ReadBuffer: read_data returned error code " << errorCode << ", throwing DTC_IOErrorException!";
3621 0 : throw DTC_IOErrorException(errorCode);
3622 : }
3623 : else
3624 : {
3625 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer buffer_=" << (void*)buffer << " errorCode=" << errorCode << " *buffer_=0x"
3626 0 : << std::hex << *(unsigned*)buffer;
3627 0 : if (channel == DTC_DMA_Engine_DAQ)
3628 : {
3629 0 : daqDMAInfo_.buffer.push_back(buffer);
3630 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer: There are now " << daqDMAInfo_.buffer.size()
3631 0 : << " DAQ buffers held in the DTC Library";
3632 : }
3633 0 : else if (channel == DTC_DMA_Engine_DCS)
3634 : {
3635 0 : dcsDMAInfo_.buffer.push_back(buffer);
3636 0 : DTC_TLOG(TLVL_ReadBuffer) << "ReadBuffer: There are now " << dcsDMAInfo_.buffer.size()
3637 0 : << " DCS buffers held in the DTC Library";
3638 : }
3639 : }
3640 0 : return errorCode;
3641 0 : } // ReadBuffer
3642 :
3643 0 : void DTCLib::DTC::ReleaseAllBuffers(const DTC_DMA_Engine& channel)
3644 : {
3645 0 : TLOG_ENTEX(1) << "ReleaseAllBuffers - channel=" << channel << "\n"
3646 0 : << otsStyleStackTrace();
3647 :
3648 0 : if (channel == DTC_DMA_Engine_DAQ)
3649 : {
3650 0 : daqDMAInfo_.buffer.clear();
3651 0 : device_.release_all(channel);
3652 :
3653 : // clear (sub)event extraction members
3654 0 : lastDMABufferWasFull_ = false;
3655 0 : lastDMABufferWasMax_ = false;
3656 0 : lastBufferTailCount_ = 0;
3657 0 : hasLastGoodSubEventHeader_ = false;
3658 0 : totalEventsParsed_ = 0;
3659 0 : subEventHeaderQwsFilled_ = 0;
3660 0 : needToFinishEvent_ = false;
3661 0 : currentEventSize_ = 0;
3662 0 : subEventByteCount_ = 0;
3663 0 : extractedSubeventBytes_ = 0;
3664 0 : extractedEvents_.clear();
3665 :
3666 0 : ResetEVBAssembly();
3667 : }
3668 0 : else if (channel == DTC_DMA_Engine_DCS)
3669 : {
3670 0 : bool lock_taken_locally = false;
3671 0 : if (!device_.thread_owns_dcs_lock())
3672 : {
3673 0 : device_.begin_dcs_transaction();
3674 0 : lock_taken_locally = true;
3675 : }
3676 :
3677 0 : dcsDMAInfo_.buffer.clear();
3678 0 : device_.release_all(channel);
3679 :
3680 0 : if (lock_taken_locally) { device_.end_dcs_transaction(); }
3681 : }
3682 0 : }
3683 :
3684 : // ReleaseBuffers releases the buffers that are held by ReadBuffer,
3685 : // to release all DMA buffers and force hw and sw to align (for DCS) use ReleaseAllBuffers
3686 0 : void DTCLib::DTC::ReleaseBuffers(const DTC_DMA_Engine& channel) //, int count)//count==0 means all
3687 : {
3688 0 : DTC_TLOG(TLVL_ReleaseBuffers) << "ReleaseBuffers BEGIN";
3689 : CFOandDTC_DMAs::DMAInfo* info;
3690 0 : if (channel == DTC_DMA_Engine_DAQ)
3691 0 : info = &daqDMAInfo_;
3692 0 : else if (channel == DTC_DMA_Engine_DCS)
3693 0 : info = &dcsDMAInfo_;
3694 : else
3695 : {
3696 0 : DTC_TLOG(TLVL_ERROR) << "ReleaseBuffers: Invalid DMA Engine specified!";
3697 0 : throw new DTC_DataCorruptionException();
3698 : }
3699 :
3700 0 : auto releaseBufferCount = CFOandDTC_DMAs::GetCurrentBuffer(info); // not GetCurrentBuffer(info), but rather Count!!!!
3701 0 : if (releaseBufferCount > 0)
3702 : {
3703 0 : DTC_TLOG(TLVL_ReleaseBuffers) << "ReleaseBuffers releasing " << releaseBufferCount << " "
3704 0 : << (channel == DTC_DMA_Engine_DAQ ? "DAQ" : "DCS") << " buffers.";
3705 :
3706 0 : if (channel == DTC_DMA_Engine_DCS)
3707 0 : device_.begin_dcs_transaction();
3708 0 : device_.read_release(channel, releaseBufferCount);
3709 0 : if (channel == DTC_DMA_Engine_DCS)
3710 0 : device_.end_dcs_transaction();
3711 :
3712 0 : for (int ii = 0; ii < releaseBufferCount; ++ii)
3713 : {
3714 0 : info->buffer.pop_front();
3715 : }
3716 : }
3717 : // else
3718 : // {
3719 : // DTC_TLOG(TLVL_ReleaseBuffers) << "ReleaseBuffers releasing ALL " << (channel == DTC_DMA_Engine_DAQ ? "DAQ" : "DCS")
3720 : // << " buffers.";
3721 : // ReleaseAllBuffers(channel);
3722 : // }
3723 0 : DTC_TLOG(TLVL_ReleaseBuffers) << "ReleaseBuffers END";
3724 0 : }
3725 :
3726 : // int DTCLib::DTC::GetCurrentBuffer(DMAInfo* info)
3727 : // {
3728 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "GetCurrentBuffer BEGIN currentReadPtr=" << (void*)info->currentReadPtr
3729 : // << " buffer.size()=" << info->buffer.size();
3730 :
3731 : // if (info->buffer.size()) // might crash in ReleaseBuffers if currentReadPtr = nullptr ????????????????????????????????
3732 : // {
3733 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "GetCurrentBuffer returning info->buffer.size()=" << info->buffer.size();
3734 : // return info->buffer.size();
3735 : // }
3736 :
3737 : // if (info->currentReadPtr == nullptr || info->buffer.size() == 0)
3738 : // {
3739 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "GetCurrentBuffer returning -1 because not currently reading a buffer";
3740 : // return -1;
3741 : // }
3742 :
3743 : // #if 0
3744 : // for (size_t ii = 0; ii < info->buffer.size(); ++ii)
3745 : // {
3746 : // auto bufferptr = *info->buffer[ii];
3747 : // uint16_t bufferSize = *reinterpret_cast<uint16_t*>(bufferptr);
3748 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "GetCurrentBuffer bufferptr="<<(void*)bufferptr<<" bufferSize="<<bufferSize;
3749 : // if (info->currentReadPtr > bufferptr &&
3750 : // info->currentReadPtr < bufferptr + bufferSize)
3751 : // {
3752 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "Found matching buffer at index " << ii << ".";
3753 : // return ii;
3754 : // }
3755 : // }
3756 : // #endif
3757 : // DTC_TLOG(TLVL_GetCurrentBuffer) << "GetCurrentBuffer returning -2: Have buffers but none match read ptr position, need new";
3758 : // return -2;
3759 : // }
3760 :
3761 : // uint16_t DTCLib::DTC::GetBufferByteCount(DMAInfo* info, size_t index)
3762 : // {
3763 : // if (index >= info->buffer.size()) return 0;
3764 : // auto bufferptr = *info->buffer[index];
3765 : // uint16_t bufferSize = *reinterpret_cast<uint16_t*>(bufferptr);
3766 : // return bufferSize;
3767 : // }
3768 :
3769 : // This is on DMA Channel 1 (i.e., DCS)
3770 0 : void DTCLib::DTC::WriteDataPacket(const DTC_DataPacket& packet)
3771 : {
3772 0 : DTC_TLOG(TLVL_WriteDataPacket) << "WriteDataPacket: Writing DCS DMA packet: " << packet.toJSON();
3773 : mu2e_databuff_t buf;
3774 0 : uint64_t size = packet.GetSize() + sizeof(uint64_t);
3775 : // uint64_t packetSize = packet.GetSize();
3776 0 : if (size < static_cast<uint64_t>(dmaSize_)) size = dmaSize_;
3777 :
3778 0 : bzero(&buf[0], size);
3779 0 : memcpy(&buf[0], &size, sizeof(uint64_t));
3780 0 : memcpy(&buf[8], packet.GetData(), packet.GetSize() * sizeof(uint8_t));
3781 :
3782 0 : Utilities::PrintBuffer(buf, size, 0, TLVL_TRACE + 30);
3783 :
3784 0 : bool lock_taken_locally = false;
3785 0 : if (!device_.thread_owns_dcs_lock())
3786 : {
3787 0 : device_.begin_dcs_transaction();
3788 0 : lock_taken_locally = true;
3789 : }
3790 :
3791 0 : auto retry = 3;
3792 : int errorCode;
3793 : do
3794 : {
3795 0 : DTC_TLOG(TLVL_WriteDataPacket) << "Attempting to write DCS DMA data...";
3796 0 : errorCode = device_.write_data(DTC_DMA_Engine_DCS, &buf, size);
3797 0 : DTC_TLOG(TLVL_WriteDataPacket) << "Attempted to write DCS DMA data, errorCode=" << errorCode << ", retries=" << retry;
3798 0 : retry--;
3799 0 : } while (retry > 0 && errorCode != 0);
3800 :
3801 0 : if (lock_taken_locally)
3802 0 : device_.end_dcs_transaction();
3803 :
3804 0 : if (errorCode != 0)
3805 : {
3806 0 : DTC_TLOG(TLVL_ERROR) << "WriteDataPacket: write_data DCS DMA returned " << errorCode << ", throwing DTC_IOErrorException! lock_taken_locally=" << lock_taken_locally;
3807 0 : throw DTC_IOErrorException(errorCode);
3808 : }
3809 0 : }
3810 :
3811 0 : void DTCLib::DTC::WriteDMAPacket(const DTC_DMAPacket& packet)
3812 : {
3813 0 : WriteDataPacket(packet.ConvertToDataPacket());
3814 0 : }
|