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