Line data Source code
1 : #ifndef DTC_H
2 : #define DTC_H
3 :
4 : #include <array>
5 : #include <chrono>
6 : #include <list>
7 : #include <map>
8 : #include <memory>
9 : #include <vector>
10 :
11 : // #include "artdaq-core-mu2e/Overlays/DTC_Packets.h"
12 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DCSReplyPacket.h"
13 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DCSRequestPacket.h"
14 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DMAPacket.h"
15 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataBlock.h"
16 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataHeaderPacket.h"
17 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataPacket.h"
18 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataRequestPacket.h"
19 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_DataStatus.h"
20 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_Event.h"
21 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_EventHeader.h"
22 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_HeartbeatPacket.h"
23 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_PacketType.h"
24 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_SubEvent.h"
25 : #include "artdaq-core-mu2e/Overlays/DTC_Packets/DTC_SubEventHeader.h"
26 :
27 : #include "CFOandDTC_DMAs.h"
28 : #include "DTC_Registers.h"
29 :
30 : // #include "artdaq-core-mu2e/Overlays/DTC_Types.h"
31 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_CharacterNotInTableError.h"
32 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_DCSOperationType.h"
33 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_DDRFlags.h"
34 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_DebugType.h"
35 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_EVBStatsType.h"
36 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_EVBStatus.h"
37 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_EventMode.h"
38 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_EventWindowTag.h"
39 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_FIFOFullErrorFlags.h"
40 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_IICDDRBusAddress.h"
41 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_IICSERDESBusAddress.h"
42 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_LinkEnableMode.h"
43 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_LinkStatus.h"
44 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_Link_ID.h"
45 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_OscillatorType.h"
46 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_PLL_ID.h"
47 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_PRBSMode.h"
48 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_ROC_Emulation_Type.h"
49 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_RXBufferStatus.h"
50 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_RXStatus.h"
51 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_SERDESLoopbackMode.h"
52 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_SERDESRXDisparityError.h"
53 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_SerdesClockSpeed.h"
54 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_SimMode.h"
55 : #include "artdaq-core-mu2e/Overlays/DTC_Types/DTC_Subsystem.h"
56 : #include "artdaq-core-mu2e/Overlays/DTC_Types/Exceptions.h"
57 : #include "artdaq-core-mu2e/Overlays/DTC_Types/Utilities.h"
58 :
59 : namespace DTCLib
60 : {
61 :
62 : typedef uint16_t roc_data_t;
63 : typedef uint16_t roc_address_t;
64 :
65 : /// <summary>
66 : /// The DTC class implements the data transfers to the DTC card. It derives from DTC_Registers, the class representing
67 : /// the DTC register space.
68 : /// </summary>
69 : class DTC : public DTC_Registers
70 : {
71 : public:
72 : /// <summary>
73 : /// Construct an instance of the DTC class
74 : /// </summary>
75 : /// <param name="mode">The desired simulation mode for the DTC (Default: Disabled)</param>
76 : /// <param name="dtc">The DTC card to use (default: -1: Use environment variable or 0 if env. var. unset)</param>
77 : /// <param name="rocMask">Which ROCs should be active. Each hex digit corresponds to a Link, and the number indicates
78 : /// how many ROCs are active on that link. (Default: 0x1)</param> <param name="expectedDesignVersion">Expected DTC
79 : /// Firmware Design Version. If set, will throw an exception if the DTC firmware does not match (Default: "")</param>
80 : /// <param name="skipInit">Whether to skip full initialization of the DTC</param>
81 : /// <param name="simMemoryFile">Name of the simulated DDR memory file if mu2esim is used</param>
82 : explicit DTC(DTC_SimMode mode = DTC_SimMode_Disabled, int dtc = -1, unsigned rocMask = 0x1, std::string expectedDesignVersion = "", bool skipInit = false, std::string simMemoryFile = "mu2esim.bin", const std::string& uid = "");
83 : virtual ~DTC();
84 :
85 : //
86 : // DMA Functions
87 : //
88 : // Data read-out
89 : /// <summary>
90 : /// Reads data from the DTC, and returns all data blocks with the same event window tag. If event window tag is specified, will look
91 : /// for data with that event window tag.
92 : /// </summary>
93 : /// <param name="when">Desired event window tag for readout. Default means use whatever event window tag is next</param>
94 : /// <returns>A vector of DTC_Event objects, but only one DTC_Event is expected</returns>
95 : std::vector<std::unique_ptr<DTC_Event>> GetData(DTC_EventWindowTag when = DTC_EventWindowTag(), bool matchEventWindowTag = false);
96 :
97 : /// <summary>
98 : /// Reads data from the DTC, and returns the first Sub Event found. If event window tag is specified, will look
99 : /// to match the found Sub Event with the specified event window tag.
100 : /// </summary>
101 : /// <param name="when">Desired event window tag for readout. Default means use whatever event window tag is next</param>
102 : /// <returns>A vector of DTC_SubEvent objects, but only one DTC_SubEvent is expected</returns>
103 : std::vector<std::unique_ptr<DTC_SubEvent>> GetSubEventData(DTC_EventWindowTag when = DTC_EventWindowTag(), bool matchEventWindowTag = false);
104 : std::vector<std::shared_ptr<DTC_Event>> GetSubEventDataAsEvents(DTC_EventWindowTag when = DTC_EventWindowTag(), bool matchEventWindowTag = false, const size_t vectorBundleTarget = 1, const size_t retries = 3);
105 :
106 : std::vector<std::shared_ptr<DTC_Event>> GetEVBDataAsEvents(DTC_EventWindowTag when = DTC_EventWindowTag(), bool matchEventWindowTag = false, const size_t retries = 3);
107 0 : uint64_t GetEVBChunksParsed() const { return evbChunksParsed_; } ///< cumulative FAFA chunks parsed by GetEVBDataAsEvents
108 0 : uint64_t GetEVBFramingErrors() const { return evbFramingErrors_; } ///< cumulative FAFA framing / record errors in GetEVBDataAsEvents
109 0 : uint64_t GetEVBSplitSubeventsJoined() const { return evbSplitSubeventsJoined_; } ///< subevents reassembled from more than one record (> ~64 KB)
110 : /// FAFA chunk word-count histogram, remote sources only (chunk_src != local MAC): index = words, 0..8191
111 : const std::vector<uint64_t>& GetEVBRemoteChunkSizeHistogram() const { return evbRemoteChunkSizeHistogram_; }
112 : std::string FormatEVBRemoteChunkSizes() const; ///< count, median, mean, share under 64 words, top sizes
113 : // EVB event assembly (see otsdaq-mu2e/docs/EVB3_software_DMA_parsing.md section 4)
114 : /// Drain-only: GetEVBDataAsEvents walks the FAFA chunks (counting bytes and chunk sizes) but skips
115 : /// reassembly, subevent validation and event completion, and returns no events. Measures how much
116 : /// of the EVB-mode throughput limit is the software assembly path.
117 0 : void SetEVBDrainOnly(bool drainOnly) { evbDrainOnly_ = drainOnly; }
118 : bool GetEVBDrainOnly() const { return evbDrainOnly_; }
119 0 : uint64_t GetEVBDrainedBytes() const { return evbDrainedBytes_; } ///< chunk payload bytes seen (both modes)
120 0 : uint64_t GetEVBDMABuffersRead() const { return evbDMABuffersRead_; }
121 : /// Step profile of GetEVBDataAsEvents: wall time (steady_clock ns) accumulated per step over all
122 : /// calls since ResetEVBAssembly, plus call/record counts. Always on; one clock read per step
123 : /// boundary (about 25 ns each, ~0.1 us per call), negligible against the ~60 us a DMA buffer takes.
124 : struct EVBReadProfile
125 : {
126 : uint64_t calls{0}; ///< GetEVBDataAsEvents calls
127 : uint64_t callsWithData{0}; ///< calls where read_data returned a buffer
128 : uint64_t recordsExtracted{0}; ///< subevent records validated and staged (Step 4)
129 : uint64_t timeoutScanNs{0}; ///< Step 0: open-tag timeout / overflow scan
130 : uint64_t readDataNs{0}; ///< Step 1: device read_data (includes waiting up to 1 ms when idle)
131 : uint64_t chunkWalkNs{0}; ///< Step 3: FAFA chunk walk incl. copy into per-source reassembly
132 : uint64_t registerReadNs{0}; ///< Step 2b: dest-node count and local MAC reads, once per run
133 : uint64_t recordScanNs{0}; ///< Step 4b: record framing, split join, header consistency, pre-parse checks
134 : uint64_t subeventSetupNs{0}; ///< Step 4c: per-record DTC_Event alloc + memcpy + SetupEvent + IsCorrupt
135 : uint64_t stagingNs{0}; ///< Step 4d: last-good copy, per-tag staging copy, reassembly erase
136 : uint64_t eventReleaseNs{0}; ///< Step 5: assemble N-subevent event, SetupEvent, release
137 : uint64_t releaseBufferNs{0}; ///< read_release of the DMA buffer (RAII on return)
138 : uint64_t totalNs{0}; ///< whole call, entry to return
139 : };
140 : const EVBReadProfile& GetEVBReadProfile() const { return evbReadProfile_; }
141 : std::string FormatEVBReadProfile() const; ///< one line per step: ms, share of call time, per-call / per-record cost
142 : void SetEVBEventTimeout(std::chrono::milliseconds t) { evbEventTimeout_ = t; } ///< max age of an incomplete event, first subevent arrival to now, before GetEVBDataAsEvents throws (default 2000 ms)
143 0 : std::chrono::milliseconds GetEVBEventTimeout() const { return evbEventTimeout_; }
144 0 : size_t GetEVBOpenTagCount() const { return evbPendingTags_.size(); } ///< tags holding at least one but not yet all N subevents
145 0 : uint64_t GetEVBEventsReleased() const { return evbEventsReleased_; } ///< complete N-subevent events returned so far
146 0 : uint8_t GetEVBNumSources() const { return evbNumSources_; } ///< N used for completion (EVB destination-node count)
147 : /// Reset all EVB assembly state and counters. Call at run start.
148 0 : void ResetEVBAssembly()
149 : {
150 0 : evbPerSourceReassembly_.clear();
151 0 : evbLastGoodRecord_.clear();
152 0 : evbPendingTags_.clear();
153 0 : evbHaveReleasedTag_ = false;
154 0 : evbLastReleasedTag_ = 0;
155 0 : evbEventsReleased_ = 0;
156 0 : evbChunksParsed_ = 0;
157 0 : evbFramingErrors_ = 0;
158 0 : evbSplitSubeventsJoined_ = 0;
159 0 : evbRemoteChunkSizeHistogram_.assign(8192, 0);
160 0 : evbDrainedBytes_ = 0;
161 0 : evbDMABuffersRead_ = 0;
162 0 : evbReadProfile_ = EVBReadProfile{};
163 0 : evbSourcesKnown_ = false;
164 0 : }
165 :
166 : /// <summary>
167 : /// Read a file into the DTC memory. Will truncate the file so that it fits in the DTC memory.
168 : /// </summary>
169 : /// <param name="file">File name to read into DTC memory</param>
170 : /// <param name="goForever">Whether readout should loop through the file</param>
171 : /// <param name="overwriteEnvrionment">Whether to use file instead of DTCLIB_SIM_FILE</param>
172 : /// <param name="outputFileName">Name of binary file to write expected output (Default: "", no file created)</param>
173 : /// <param name="skipVerify">Skip the verify stage of WriteSimFileToDTC</param>
174 : void WriteSimFileToDTC(std::string file, bool goForever, bool overwriteEnvrionment = false, std::string outputFileName = "", bool skipVerify = false);
175 : /// <summary>
176 : /// Read the DTC memory and determine whether the file was written correctly.
177 : /// </summary>
178 : /// <param name="file">File to verify against</param>
179 : /// <param name="rawOutputFilename">Default: "". If set, file to write data retrieved from the DTC to</param>
180 : /// <returns>True if file is in DTC memory without errors</returns>
181 : bool VerifySimFileInDTC(std::string file, std::string rawOutputFilename = "");
182 :
183 : // DCS Register R/W
184 : /// <summary>
185 : /// Sends a DCS Request Packet with fields filled in such that the given ROC register will be read out.
186 : /// This function reads from the main ROC register space, use ReadExtROCRegister to access other firmware blocks'
187 : /// register spaces.
188 : /// </summary>
189 : /// <param name="link">Link of the ROC to read</param>
190 : /// <param name="address">Address of the register</param>
191 : /// <param name="tmo_ms">Timeout, in milliseconds, for read (will retry until timeout is expired or data received)</param>
192 : /// <returns>Value of the ROC register from a DCS Reply packet</returns>
193 : roc_data_t ReadROCRegister(const DTC_Link_ID& link, const roc_address_t address, int tmo_ms);
194 : /// <summary>
195 : /// Sends a DCS Request Packet with the fields filled in such that the given ROC register will be written.
196 : /// This function writes to the main ROC register space, use WriteExtROCRegister to access other firmware blocks'
197 : /// register spaces.
198 : /// </summary>
199 : /// <param name="link">Link of the ROC to write to</param>
200 : /// <param name="address">Address of the register</param>
201 : /// <param name="data">Value to write</param>
202 : /// <param name="requestAck">Whether to request acknowledement of this operation</param>
203 : /// <param name="ack_tmo_ms">Timeout, in milliseconds, for ack (will retry until timeout is expired or ack received)</param>
204 : bool WriteROCRegister(const DTC_Link_ID& link, const roc_address_t address, const roc_data_t data, bool requestAck, int ack_tmo_ms);
205 :
206 : /// <summary>
207 : /// Perform a "double operation" read of ROC registers
208 : /// </summary>
209 : /// <param name="link">Link of the ROC to read</param>
210 : /// <param name="address1">First address to read</param>
211 : /// <param name="address2">Second address to read</param>
212 : /// <param name="tmo_ms">Timeout, in milliseconds, for read (will retry until timeout is expired or data received)</param>
213 : /// <returns>Pair of register values, first is from the first address, second from the second</returns>
214 : std::pair<roc_data_t, roc_data_t> ReadROCRegisters(const DTC_Link_ID& link, const roc_address_t address1, const roc_address_t address2, int tmo_ms);
215 : /// <summary>
216 : /// Perform a "double operation" write to ROC registers
217 : /// </summary>
218 : /// <param name="link">Link of the ROC to write to</param>
219 : /// <param name="address1">First address to write</param>
220 : /// <param name="data1">Value to write to first register</param>
221 : /// <param name="address2">Second address to write</param>
222 : /// <param name="data2">Value to write to second register</param>
223 : /// <param name="requestAck">Whether to request acknowledement of this operation</param>
224 : /// <param name="ack_tmo_ms">Timeout, in milliseconds, for ack (will retry until timeout is expired or ack received)</param>
225 : bool WriteROCRegisters(const DTC_Link_ID& link, const roc_address_t address1, const roc_data_t data1, const roc_address_t address2, const roc_data_t data2, bool requestAck, int ack_tmo_ms);
226 : /// <summary>
227 : /// Perform a ROC block read
228 : /// </summary>
229 : /// <param name="data">vector of data by reference to read</param>
230 : /// <param name="link">Link of the ROC to read</param>
231 : /// <param name="address">Address of the block</param>
232 : /// <param name="wordCount">Number of words to read</param>
233 : /// <returns>Vector of words returned by block read</returns>
234 : /// <param name="incrementAddress">Whether to increment the address pointer for block reads/writes</param>
235 : /// <param name="tmo_ms">Timeout, in milliseconds, for read (will retry until timeout is expired or data received)</param>
236 : void ReadROCBlock(std::vector<roc_data_t>& data, const DTC_Link_ID& link, const roc_address_t address, const uint16_t wordCount, bool incrementAddress, int tmo_ms);
237 : /// <summary>
238 : /// Perform a ROC block write
239 : /// </summary>
240 : /// <param name="link">Link of the ROC to write</param>
241 : /// <param name="address">Address of the block</param>
242 : /// <param name="blockData">Vector of words to write</param>
243 : /// <param name="requestAck">Whether to request acknowledement of this operation</param>
244 : /// <param name="incrementAddress">Whether to increment the address pointer for block reads/writes</param>
245 : /// <param name="ack_tmo_ms">Timeout, in milliseconds, for ack (will retry until timeout is expired or ack received)</param>
246 : bool WriteROCBlock(const DTC_Link_ID& link, const roc_address_t address, const std::vector<roc_data_t>& blockData, bool requestAck, bool incrementAddress, int ack_tmo_ms);
247 :
248 : /// <summary>
249 : /// Sends a DCS Request Packet with fields filled in such that the given ROC firmware block register will be read out.
250 : /// This funcion reads from firmware blocks' register spaces.
251 : /// </summary>
252 : /// <param name="link">Link of the ROC to read</param>
253 : /// <param name="block">Block ID to read from</param>
254 : /// <param name="address">Address of the register</param>
255 : /// <param name="tmo_ms">Timeout, in milliseconds, for read (will retry until timeout is expired or data received)</param>
256 : /// <returns>Value of the ROC register from a DCS Reply packet</returns>
257 : uint16_t ReadExtROCRegister(const DTC_Link_ID& link, const roc_address_t block, const roc_address_t address, int tmo_ms = 0);
258 : /// <summary>
259 : /// Sends a DCS Request Packet with fields filled in such that the given ROC firmware block register will be written.
260 : /// This funcion writes to firmware blocks' register spaces.
261 : /// </summary>
262 : /// <param name="link">Link of the ROC to write to</param>
263 : /// <param name="block">Block ID to write to</param>
264 : /// <param name="address">Address of the register</param>
265 : /// <param name="data">Value to write</param>
266 : /// <param name="requestAck">Whether to request acknowledement of this operation</param>
267 : /// <param name="ack_tmo_ms">Timeout, in milliseconds, for ack (will retry until timeout is expired or ack received)</param>
268 : bool WriteExtROCRegister(const DTC_Link_ID& link, const roc_address_t block, const roc_address_t address, const roc_data_t data, bool requestAck, int ack_tmo_ms);
269 : /// <summary>
270 : /// Dump all known registers from the given ROC, via DCS Request packets.
271 : /// </summary>
272 : /// <param name="link">Link of the ROC</param>
273 : /// <returns>JSON-formatted register dump</returns>
274 : std::string ROCRegDump(const DTC_Link_ID& link);
275 :
276 : // Broadcast Readout
277 : /// <summary>
278 : /// DEPRECATED
279 : /// Sends a Readout Request broadcast to given Link.
280 : /// </summary>
281 : /// <param name="link">Link to send to</param>
282 : /// <param name="when">Timestamp for the Readout Request</param>
283 : /// <param name="quiet">Whether to not print the JSON representation of the Readout Request (Default: true, no JSON
284 : /// printed)</param>
285 : void SendHeartbeatPacket(const DTC_Link_ID& link, const DTC_EventWindowTag& when, bool quiet = true);
286 : /// <summary>
287 : /// Send a DCS Request Packet to the given ROC. Use the Read/Write ROC Register functions for more convinient register
288 : /// access.
289 : /// </summary>
290 : /// <param name="link">Link of the ROC</param>
291 : /// <param name="type">Operation to perform</param>
292 : /// <param name="address">Target address</param>
293 : /// <param name="data">Data to write, if operation is write</param>
294 : /// <param name="address2">Second Target address</param>
295 : /// <param name="data2">Data to write to second address, if operation is write</param>
296 : /// <param name="quiet">Whether to not print the JSON representation of the Readout Request (Default: true, no JSON
297 : /// printed)</param>
298 : /// <param name="requestAck">Whether to request acknowledement of this operation</param>
299 : void SendDCSRequestPacket(const DTC_Link_ID& link, const DTC_DCSOperationType type, const roc_address_t address, const roc_data_t data = 0x0, const roc_address_t address2 = 0x0, const roc_data_t data2 = 0, bool quiet = true, bool requestAck = false);
300 :
301 : /// <summary>
302 : /// Writes a packet to the DTC on the DCS channel
303 : /// </summary>
304 : /// <param name="packet">Packet to write</param>
305 : void WriteDMAPacket(const DTC_DMAPacket& packet);
306 : /// <summary>
307 : /// Writes the given data buffer to the DTC's DDR memory, via the DAQ channel.
308 : /// </summary>
309 : /// <param name="buf">DMA buffer to write. Must have an inclusive 64-bit byte count at the beginning, followed by an
310 : /// exclusive 64-bit block count.</param> <param name="sz">Size ofthe data in the buffer</param>
311 : void WriteDetectorEmulatorData(mu2e_databuff_t* buf, size_t sz);
312 : /**
313 : * @brief Read the next DMA from the DAQ channel. If no data is present, will return nullptr
314 : * @param tmo_ms Timeout
315 : * @return A DTC_Event representing the data in a single DMA, or nullptr if no data/timeout
316 : */
317 : std::unique_ptr<DTC_Event> ReadNextDAQDMA(int tmo_ms);
318 : /**
319 : * @brief Read SubEvents from the next DMA buffer(s) from the DAQ channel. Multiple subevents may be returned
320 : * from a single buffer. If a subevent spans multiple DMA buffers, this function blocks until complete.
321 : * @param output Vector to append extracted SubEvents to
322 : * @param tmo_ms Timeout
323 : * @return True if one or more SubEvents were extracted, false if no data/timeout
324 : */
325 : bool ReadNextDAQSubEventDMA(std::vector<std::unique_ptr<DTC_SubEvent>>& output, int tmo_ms);
326 : /// <summary>
327 : /// DCS packets are read one-at-a-time, this function reads the next one from the DTC
328 : /// </summary>
329 : /// <param name="tmo_ms">Timeout, in milliseconds, for read (will retry until timeout is expired or data received)</param>
330 : /// <returns>Pointer to read DCSReplyPacket. Will be nullptr if no data available.</returns>
331 : std::unique_ptr<DTC_DCSReplyPacket> ReadNextDCSPacket(int tmo_ms);
332 :
333 : /// <summary>
334 : // ------------
335 : /// As of May 2024 RAR, force user to decide which type of buffers this process owns
336 : // DCS buffers is generally safer for multiple processes to access because of the lockout
337 : // DAQ/DATA buffers should only be released by the 'owner' of the DMA channel.
338 : // -------------
339 : // Releases all buffers to the hardware, from both the DAQ and DCS channels
340 : /// </summary>
341 : // void ReleaseAllBuffers()
342 : // {
343 : // ReleaseAllBuffers(DTC_DMA_Engine_DAQ);
344 : // ReleaseAllBuffers(DTC_DMA_Engine_DCS);
345 : // }
346 :
347 : /// <summary>
348 : /// Release all buffers to the hardware on the given channel
349 : /// </summary>
350 : /// <param name="channel">Channel to release</param>
351 : void ReleaseAllBuffers(const DTC_DMA_Engine& channel);
352 :
353 : private:
354 : std::unique_ptr<DTC_DataPacket> ReadNextPacket(const DTC_DMA_Engine& channel, int tmo_ms);
355 : int ReadBuffer(const DTC_DMA_Engine& channel, int retries = 10);
356 : /// <summary>
357 : /// This function releases all buffers except for the one containing currentReadPtr. Should only be called when done
358 : /// with data in other buffers!
359 : /// </summary>
360 : /// <param name="channel">Channel to release</param>
361 : void ReleaseBuffers(const DTC_DMA_Engine& channel);
362 : void WriteDataPacket(const DTC_DataPacket& packet);
363 :
364 : // struct DMAInfo
365 : // {
366 : // std::deque<mu2e_databuff_t*> buffer;
367 : // uint32_t bufferIndex;
368 : // void* currentReadPtr;
369 : // void* lastReadPtr;
370 : // DMAInfo()
371 : // : buffer(), bufferIndex(0), currentReadPtr(nullptr), lastReadPtr(nullptr) {}
372 : // ~DMAInfo()
373 : // {
374 : // buffer.clear();
375 : // currentReadPtr = nullptr;
376 : // lastReadPtr = nullptr;
377 : // }
378 : // };
379 : // int GetCurrentBuffer(DMAInfo* info);
380 : // uint16_t GetBufferByteCount(DMAInfo* info, size_t index);
381 : CFOandDTC_DMAs::DMAInfo daqDMAInfo_;
382 : CFOandDTC_DMAs::DMAInfo dcsDMAInfo_;
383 :
384 : // State for GetSubEventDataAsEvents v3: extracted events buffer
385 : std::vector<std::shared_ptr<DTCLib::DTC_Event>> extractedEvents_{};
386 :
387 : bool needToFinishEvent_{false};
388 : size_t currentEventSize_{0};
389 : size_t subEventByteCount_{0};
390 : size_t extractedSubeventBytes_{0};
391 :
392 : DTC_SubEventHeader lastGoodSubEventHeader_{};
393 : bool hasLastGoodSubEventHeader_{false};
394 : uint64_t totalEventsParsed_{0}; ///< Lifetime count of successfully-parsed subevents; included in diagnostic prints so we can tell whether parsing ever made progress
395 :
396 : // Standing buffer to assemble the on-wire header prefix one QW at a time.
397 : // On-wire layout: [EventHeader 1 QW][SubEventHeader 6 QWs] = 7 QWs total.
398 : // QW0 = event header; QW1..6 = subevent header.
399 : static constexpr size_t kSubEventHeaderQws = sizeof(DTC_SubEventHeader) / sizeof(uint64_t) + 1;
400 : std::array<uint64_t, kSubEventHeaderQws> subEventHeaderBuf_{};
401 : size_t subEventHeaderQwsFilled_{0}; ///< Number of QWs currently filled in subEventHeaderBuf_
402 :
403 : std::array<uint64_t, 8> lastBufferTailQwords_{}; ///< Last up to 8 qwords of the most recently processed DMA buffer; saved at every return path for cross-buffer exception diagnostics
404 : size_t lastBufferTailCount_{0}; ///< Valid entry count in lastBufferTailQwords_
405 :
406 : bool lastDMABufferWasFull_{false}; ///< True when dmaBytes == MAX_TRANSFER_SIZE (max sub-transfer with tlast); next buffer starts a new sub-transfer with prefix
407 : bool lastDMABufferWasMax_{false}; ///< True when dmaBytes == sizeof(mu2e_databuff_t) (buffer physically full, no tlast); next buffer is raw continuation (no prefix)
408 :
409 : std::string lastDMABufferIndex_{}; ///< String representation of the index of the last DMA buffer processed, for diagnostics
410 :
411 : // // State for GetSubEventData v2: cross-buffer pending subevent assembly
412 : uint64_t totalSubEventsParsed_{0}; ///< Lifetime count of successfully-parsed subevents; included in diagnostic prints so we can tell whether parsing ever made progress
413 : std::vector<uint8_t> pendingSubEventBytes_{}; ///< Partial subevent bytes carried over from the previous DMA buffer
414 : size_t pendingSubEventTotalBytes_{0}; ///< Expected total byte count of the pending subevent (0 = header not yet complete)
415 : // bool lastDMABufferWasFull_{false}; ///< True when the last DMA buffer was completely full (dmaBytes==sizeof(mu2e_databuff_t)); used for payloadBytes/tlast calculation
416 : bool pendingPrefixConsumed_{false}; ///< True when the previous buffer ended with a prefix-only (0 subevent bytes after the prefix); next buffer starts with raw subevent header data at offset 0
417 :
418 : // State for GetEVBDataAsEvents: per-source FAFA chunk reassembly
419 : std::map<uint8_t /*chunk_src*/, std::vector<uint8_t>> evbPerSourceReassembly_;
420 : std::map<uint8_t /*chunk_src*/, std::vector<uint8_t>> evbLastGoodRecord_; ///< last cleanly-parsed record per source, dumped alongside a bad one for comparison
421 : // Per-tag event assembly: a tag is complete when all N source DTCs have delivered their subevent
422 : struct EVBPendingTag
423 : {
424 : std::chrono::steady_clock::time_point firstArrival; ///< when the first subevent for this tag was staged
425 : std::map<uint8_t /*source_dtc_id*/, std::vector<uint8_t>> subevents; ///< raw subevent bytes (record header stripped), keyed by source
426 : };
427 : std::map<uint64_t /*event window tag*/, EVBPendingTag> evbPendingTags_;
428 0 : std::chrono::milliseconds evbEventTimeout_{2000};
429 : size_t evbMaxOpenTags_{1024};
430 : bool evbDrainOnly_{false};
431 : uint64_t evbDrainedBytes_{0};
432 : uint64_t evbDMABuffersRead_{0};
433 : EVBReadProfile evbReadProfile_;
434 : uint8_t evbNumSources_{1};
435 : bool evbSourcesKnown_{false};
436 : uint8_t evbLocalMac_{0};
437 : bool evbHaveReleasedTag_{false};
438 : uint64_t evbLastReleasedTag_{0};
439 : uint64_t evbEventsReleased_{0};
440 : uint64_t evbChunksParsed_{0};
441 0 : std::vector<uint64_t> evbRemoteChunkSizeHistogram_ = std::vector<uint64_t>(8192, 0);
442 : uint64_t evbFramingErrors_{0};
443 : uint64_t evbSplitSubeventsJoined_{0};
444 :
445 : uint8_t lastDTCErrorBitsValue_ = 0;
446 : };
447 : } // namespace DTCLib
448 : #endif
|