LCOV - code coverage report
Current view: top level - mu2e-pcie-utils/dtcInterfaceLib - EVBErrorStatus.h (source / functions) Coverage Total Hit
Test: mu2edaq.info.cleaned Lines: 30.9 % 68 21
Test Date: 2026-10-29 01:38:59 Functions: 37.5 % 8 3

            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
        

Generated by: LCOV version 2.0-1