Line data Source code
1 : #ifndef MU2E_PCIE_UTILS_EVBERRORSTATUS_H
2 : #define MU2E_PCIE_UTILS_EVBERRORSTATUS_H
3 :
4 : #include <cstddef>
5 : #include <cstdint>
6 : #include <iomanip>
7 : #include <sstream>
8 : #include <string>
9 : #include <vector>
10 :
11 : namespace DTCLib
12 : {
13 : // All 16 low bits are defined errors (bits 14-15 ROC_TAG_SLIP / ROC_RECORD_SHAPE added
14 : // 2026-09-16); any nonzero [15:0] invalidates the run. B3 mask corrected to 0xFFFF
15 : // by the hw agent 2026-09-21.
16 : constexpr uint32_t EVBDefinedErrorMask = 0xFFFFu;
17 :
18 : // Retransmission (2026-09-28 protocol): bit 1 still latches on every lost frame, but the
19 : // link re-sends it, so on a retransmission bitfile bit 1 alone is not data loss. Status
20 : // bit 27 is live while a source is in discard or this TX is serving a rewind; still set
21 : // once traffic has stopped means a resend never landed.
22 : constexpr uint32_t EVBRxSequenceGapBit = 1u << 1;
23 : constexpr uint32_t EVBResendActiveBit = 1u << 27;
24 :
25 : // RX FCS-bad event count, [31:28] since bit 27 was taken (was [31:27]); saturates at 15
26 29 : inline uint32_t EVBFCSBadCount(uint32_t value) { return value >> 28; }
27 :
28 : // Stats BRAM row 0xA (RX_ACKPOS), by source: that peer's feedback about OUR stream to it,
29 : // as carried in the last header received from it. A snapshot, not a counter.
30 : struct EVBAckPosition
31 : {
32 : bool resendRequested; // [31] peer is discarding our frames and wants a resend
33 : uint8_t lastSequenceSeen; // [27:20] seq of our last frame the peer had when it sent
34 : uint32_t ackPosition; // [19:0] stream offset of the first word of ours not stored
35 : };
36 :
37 0 : inline EVBAckPosition DecodeEVBAckPosition(uint32_t value)
38 : {
39 0 : return {(value >> 31) != 0, static_cast<uint8_t>((value >> 20) & 0xFFu), value & 0xFFFFFu};
40 : }
41 :
42 : struct EVBStatusCheck
43 : {
44 : uint32_t stickyErrors;
45 : bool ddrCalibrated;
46 : bool missingFrontier;
47 : bool resendActive;
48 :
49 0 : bool readyToStart() const { return stickyErrors == 0 && ddrCalibrated; }
50 : };
51 :
52 : // Evaluate one caller-owned snapshot; never read or clear hardware here.
53 0 : inline EVBStatusCheck CheckEVBStatus(uint32_t value, bool trafficStarted = false, bool hasPeers = true)
54 : {
55 0 : return {value & EVBDefinedErrorMask, (value & (1u << 25)) != 0, trafficStarted && hasPeers && !(value & (1u << 24)), (value & EVBResendActiveBit) != 0};
56 : }
57 :
58 29 : inline std::vector<std::string> DecodeEVBErrorStatus(uint32_t value)
59 : {
60 58 : auto hexField = [](uint32_t field) {
61 58 : std::ostringstream output;
62 58 : output << "0x" << std::hex << std::setfill('0') << std::setw(4) << field;
63 116 : return output.str();
64 58 : };
65 29 : std::vector<std::string> lines;
66 29 : lines.push_back("Sticky errors [15:0] (any set invalidates run; SoftReset-only clear): " +
67 58 : hexField(value & 0xFFFFu));
68 : static const char* const errorNames[] = {
69 : "Rx source buffer write-while-full; words lost",
70 : "Rx sequence gap; packet loss indication, unreliable if bit 7 is set; confirm with word-count parity",
71 : "valid EVB frame rejected: addressed to this DTC but source offset > 31; check peer MAC and start node",
72 : "DDR write CDC FIFO empty inside burst; corrupt burst",
73 : "undefined TX state or destination offset > 31; or TX window overrun: this DTC's frame was still on the wire when its destination window closed (sender-side flag; through a switch the colliding frame is dropped uncounted)",
74 : "sent/drained counters disagree beyond credit; one-sided reset",
75 : "local stream misaligned; affected record corrupt, word forwarded as a 1-word chunk, not dropped",
76 : "Rx stats pipeline collision; bit 1 and Rx BRAM rows/rates untrustworthy, data intact",
77 : "DDR read desync; bad count quadword dropped, resync at next valid count",
78 : "DDR-to-TX staging FIFO write-while-full; words lost",
79 : "staging FIFO empty inside TX payload; wire repeated a word",
80 : "TX frame malformed; illegal start/terminate, idle inside frame, data outside frame, or > 200 blocks",
81 : "Rx frame had fewer/more data words than its header declared: the receiver-side print of a peer's bit 9 (same staging-FIFO fault on the sender); words lost",
82 : "Rx FCS bad on accepted 0x78 frame; corrupt data already stored; shifted 0x33 frames not checked",
83 : "ROC tag slip at EVB3 input: header EWT != first ROC fragment EWT; slip originated upstream in RingController/AXIMux",
84 : "ROC record shape at EVB3 input: accepted beats != count quadword, or header byte count < count quadword",
85 : };
86 493 : for(std::size_t bit = 0; bit < sizeof(errorNames) / sizeof(errorNames[0]); ++bit)
87 464 : if((value >> bit) & 1u)
88 16 : lines.push_back(" Bit " + std::to_string(bit) + " set: " + errorNames[bit]);
89 :
90 29 : lines.push_back("Status [31:16] (user_clk-synced; bit 26 sticky-informational, others live): " +
91 58 : hexField(value >> 16));
92 : static const char* const statusNames[] = {
93 : "ROC input held: tvalid and not tready; downstream back-pressure",
94 : "self-subevent throttle holding a subevent; a peer is behind",
95 : "window has data, destination has no credit; peer DMA not draining",
96 : "DDR channel almost-full back-pressure",
97 : "DDR write CDC FIFO full",
98 : "HEADER waiting; both staging FIFOs owned by other destinations",
99 : "any Rx source buffer >= 3/4 full; this DTC's DMA not draining",
100 : "DMA back-pressure: m_axis_tvalid and not tready; software reading too slowly",
101 : "peer frontier known: min event tag delivered by all peer DTCs; self-throttle armed",
102 : "DDR calibration complete",
103 : "frame not for this DTC: non-EVB start, or destination MAC DTC byte / partition byte not ours; sticky since SoftReset, informational; normal on a shared switch",
104 : "resend active: a source is in discard waiting for a resend, or this TX is serving a rewind; normal for a few rotations after a loss, still set once traffic has stopped means a resend never landed",
105 : };
106 377 : for(std::size_t index = 0; index < sizeof(statusNames) / sizeof(statusNames[0]); ++index)
107 : {
108 348 : const std::size_t bit = 16 + index;
109 348 : if((value >> bit) & 1u)
110 12 : lines.push_back(" Bit " + std::to_string(bit) + " set: " + statusNames[index]);
111 : }
112 29 : if(EVBFCSBadCount(value))
113 0 : lines.push_back(" RX FCS-bad count [31:28]: " + std::to_string(EVBFCSBadCount(value)) +
114 : " (bit-13 events since SoftReset or RX enable; 15 = saturated)");
115 58 : return lines;
116 0 : }
117 :
118 0 : inline std::string FormatEVBStatusCheck(uint32_t value, const std::string& dtc, bool trafficStarted = false, bool hasPeers = true, bool includeBitNames = true, uint32_t ignoreMask = 0)
119 : {
120 0 : const auto check = CheckEVBStatus(value & ~ignoreMask, trafficStarted, hasPeers);
121 0 : const uint32_t ignored = value & EVBDefinedErrorMask & ignoreMask;
122 0 : std::ostringstream output;
123 0 : output << "DTC " << dtc << " EVB error/status (0x9370): 0x" << std::hex
124 0 : << std::setw(8) << std::setfill('0') << value << std::dec << std::setfill(' ') << "\n";
125 0 : if(check.stickyErrors)
126 : {
127 0 : output << "ERROR: run INVALID. Stop traffic, then SoftReset all DTCs together before the next first event; never write 0x9370 to clear.\n";
128 : static const char* const names[] = {
129 : "RX_BUF_WRITE_FULL",
130 : "RX_SEQ_GAP",
131 : "RX_PKT_REJECTED",
132 : "DDR_WR_UNDERFLOW",
133 : "TX_FSM_FAULT",
134 : "CREDIT_VIOLATION",
135 : "LOCAL_BAD_HEADER",
136 : "RX_STATS_COLLISION",
137 : "DDR_RD_BAD_COUNT",
138 : "STAGING_WRITE_FULL",
139 : "TX_PAYLOAD_UNDERFLOW",
140 : "TX_FRAME_MALFORMED",
141 : "RX_FRAME_SIZE",
142 : "RX_FCS_BAD",
143 : "ROC_TAG_SLIP",
144 : "ROC_RECORD_SHAPE",
145 : };
146 0 : const auto group = [&](const char* label, uint32_t mask) {
147 0 : if(!(check.stickyErrors & mask))
148 0 : return;
149 0 : output << " " << label << ":";
150 0 : for(std::size_t bit = 0; bit < sizeof(names) / sizeof(names[0]); ++bit)
151 0 : if(check.stickyErrors & mask & (1u << bit))
152 : {
153 0 : output << " Bit " << bit;
154 0 : if(includeBitNames)
155 0 : output << " " << names[bit];
156 0 : output << ";";
157 : }
158 0 : output << "\n";
159 0 : };
160 0 : group("Data lost", (1u << 0) | (1u << 1) | (1u << 9) | (1u << 12));
161 : // bits 14-15 (ROC input checks, fault already present upstream of EVB3) are in the
162 : // "data corrupt" group per the corrected B3 text (hw agent, 2026-09-21)
163 0 : group("Data corrupt", (1u << 3) | (1u << 6) | (1u << 8) | (1u << 10) | (1u << 11) | (1u << 13) | (1u << 14) | (1u << 15));
164 0 : group("Protocol / config", (1u << 2) | (1u << 4) | (1u << 5) | (1u << 7));
165 0 : output << "Use end-of-run word-count parity as the authoritative loss check; 0x9370 identifies the stage.\n";
166 : }
167 : else
168 0 : output << "No defined sticky errors; this alone does not establish loss-free data.\n";
169 0 : if(ignored)
170 0 : output << "Informational (excluded from run validity): 0x" << std::hex << ignored << std::dec << "\n";
171 0 : if(ignored & EVBRxSequenceGapBit)
172 0 : output << " Bit 1 RX_SEQ_GAP: a frame was lost and the link re-sent it (0x9174 counts the resends); not a bad run unless bit 27 is still set after traffic stops.\n";
173 0 : if(check.resendActive)
174 0 : output << "Bit 27 RESEND_ACTIVE: a resend is in progress; normal right after a loss, but still set after traffic stops means a resend never landed.\n";
175 0 : if(!check.ddrCalibrated)
176 0 : output << "ERROR: Bit 25 clear: DDR not calibrated; do not start.\n";
177 0 : if(check.missingFrontier)
178 0 : output << "WARN: Bit 24 still clear after traffic began: no peer traffic accepted; check link, MAC, partition and start node.\n";
179 0 : output << "Back-pressure bits 16-23 and foreign-frame bit 26 are informational, not failures; blocked-stage hints matter only when levels stay high while event counts stop.\n";
180 0 : for(const auto& line : DecodeEVBErrorStatus(value))
181 0 : output << line << "\n";
182 0 : return output.str();
183 0 : }
184 : } // namespace DTCLib
185 :
186 : #endif
|