LCOV - code coverage report
Current view: top level - mu2e-pcie-utils/dtcInterfaceLib - DTC.cpp (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 0.0 % 1880 0
Test Date: 2026-07-30 01:56:44 Functions: 0.0 % 330 0

            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 : }
        

Generated by: LCOV version 2.0-1