Skip to content
VLSI Mentor

Ethernet · Module 21

A Method for Debugging Ethernet

The receive path has twelve fault sites and the RMON-required counters separate them into seven classes, one holding five — so the method's ceiling is knowable from a datasheet.

A debugging method is a decision procedure over a space of candidate faults, and the first thing to know about it is how many of those candidates the instrumentation can actually tell apart.

Count
fault sites on the receive path12
counters a MAC could expose11
counters the RMON standard requires6
classes the RMON six separate the 12 sites into7
sites in the largest of those classes5
observations a perfect procedure needs7
information-theoretic floor4

Rows four and five are the chapter's subject. Five of the twelve sites — the address filter, VLAN membership, the receive FIFO, the DMA path and the host driverproduce identical readings on every counter the standard requires. A frame arrives, a frame does not reach the host, and no error counter moves, because none of those five is an error.

Rows six and seven are the chapter's second subject. Twelve candidates is 3.58 bits, so four binary observations should suffice. Seven are needed, because the counters do not partition the space evenly — four of them split off exactly one site each, and a question that eliminates one candidate in twelve carries 0.414 bits.

And the counter everybody looks at first carries none at all.


1. Scope, and a Fault Space With Twelve Members

Scope: the receive path, from the channel to host memory, as a space of fault sites; the counters that observe it; and what a procedure over the two can and cannot conclude.

Not in scope: the individual failure modes. Chapter 21.2 is the catalogue and Chapter 21.3 is the first entry in it. This chapter is the method, and a method is worth writing down separately because its quality is measurable — Sections 4, 6 and 8 measure it.

Start by enumerating the space, because everything after depends on it being the right size.

#SiteNamed in
1the channel — cable, connector, opticsChapter 3.1
2the PHY's lane alignmentChapter 3.4
3the xMII interface itselfChapter 4.2
4preamble and SFD detectionChapter 5.2
5the frame parser's offset arithmeticChapter 19.2 §3
6the CRC engine's own logicChapter 19.4
7the validity classifierChapter 7.3 §3
8the address filterChapter 7.4
9VLAN port membershipChapter 13.2
10the receive FIFOChapter 19.5
11the DMA and descriptor pathChapter 19.6
12the host driver and its buffersChapter 18.3

Twelve, and the number matters more than the list does. A procedure that starts from "the link is broken" is searching a space of twelve, which is 3.585 bitsso four well-chosen binary observations would be enough if the observations were well chosen, and Section 8 shows they cannot be.

Three things this list deliberately is and is not.

Choice
it is receive-onlythe transmit path is a different twelve
it is sites, not symptomsa symptom is what you see; a site is what you replace
it stops at the driverabove that is somebody else's decision procedure

Row two is the distinction the whole chapter turns on. "CRC errors are climbing" is a symptom and it is consistent with sites 1, 2, 3, 5 and 6 — five of the twelve. Chapter 21.2 is the catalogue of symptoms; this chapter is about getting from a symptom to a site, and the distance between those two is exactly what Section 4 measures.


2. The Observable Set, and What It Actually Separates

The receive path has twelve fault sites in order: the channel, the physical layer's lane alignment, the media independent interface itself, preamble and start frame delimiter detection, the frame parser's offset arithmetic, the check engine's own logic, the validity classifier, the address filter, virtual local area network port membership, the receive buffer, the direct memory access and descriptor path, and the host driver. Eleven counters observe them. Six of the eleven are required by the remote monitoring standard: crc errors, alignment errors, undersize, oversize, frames in and frames out. Those six all count errors. Five more are a vendor's choice, and every one of them observes a site whose frames are discarded legally rather than erroneously: symbol errors inside the physical layer, filtered frames, virtual network discards, buffer drops and descriptor errors. Because the standard specifies error counters and the last five sites produce no errors, those five sites have identical readings on every required counter: frames went in, fewer came out, and nothing else moved. They form one indistinguishable class of five, and the only thing that splits it is one of the optional counters, one site at a time.Sites 1 to 3channel, PHY, xMIISites 4 to 7preamble to validitySites 8 to 12filter, VLAN, FIFO,DMA, driverThe RMON sixall count errorsFive optionalall count non-errors7 classeswith the required sixClass G holdsfiveone signature12 classeswith all eleven12
Figure 1 — twelve sites, eleven counters, and the five that produce no error at all.

Eleven counters. Six are required by the RMON standard and five are a vendor's choice, and the split is not where anybody would put it.

CounterRequired?Sites it moves for
c_crc_errorsyes — Chapter 19.7 §25 of 12
c_alignment_errorsyes3
c_undersizeyes3
c_oversizeyes2
frames_inyes4
frames_outyes12
c_symbol_errorsno — in the PHY, over MDIO2
c_filteredno2
c_vlan_discardsno2
c_fifo_dropsno1
c_desc_errorsno — in the driver1

Every one of the five optional counters observes a site that produces no error. A filtered frame is not an error, a non-member VLAN frame is not an error, a FIFO drop under a stalled consumer is not an error, and a descriptor the driver did not post is not an error eitherChapter 20.3 §13 listed exactly this family. The standard requires counters for the error cases and leaves the non-error cases to the vendor, which is defensible and is the reason the largest indistinguishable class exists.

Now build the table that decides everything: which counters move for which sites.

Sitecrcalignunderoverinout
1 channelyesyesyesnoyesshort
2 PHY lanesyesyesnonoyesshort
3 xMIIyesyesnonoyesshort
4 preamblenonononoyesshort
5 parser offsetyesnoyesyesnoshort
6 CRC logicyesnonononoshort
7 validitynonoyesyesnoshort
8 filternononononoshort
9 VLANnononononoshort
10 FIFOnononononoshort
11 DMAnononononoshort
12 drivernononononoshort

Rows eight to twelve are identical. Five sites, one signature, and the signature is "frames went in, fewer came out, nothing else moved."

Rows two and three are identical as well, which is a smaller problem and a real one: a PHY lane-alignment fault and an xMII timing fault both produce CRC errors and alignment errors on frames that were counted at the port. The difference between them is a symbol-error counter inside the PHY, which is row seven of the previous table and is optional.

ClassesLargest class
with the RMON six75
with all eleven121

Five optional counters are the difference between a diagnosis and a shortlist of five.

And it is worth walking the twelve rows once, because each one is a chapter's failure mode reduced to a counter signature, and three of them are counter-intuitive.

SiteThe faultWhy the signature is what it is
1 channela marginal connector, a bent fibrethe frame is damaged in transit, so the port counts it and the check fails; short frames appear because a burst can truncate one
2 PHY lanesChapter 3.4's deskew is wrongoctets arrive in the wrong order, so the check fails and the frame is not whole octets — but the frame was seen, so frames_in moves
3 xMIIa setup violation on the interfaceidentical to row two from outside the chip; only a PHY-internal symbol counter tells them apart
4 preambleChapter 5.2's SFD is missedthe frame is never framed, so nothing downstream sees it — frames_in moves and nothing else can
5 parser offsetChapter 19.2 §3's barrel is offthe length field is read from the wrong octets, so undersize and oversize both move — and frames_in does NOT, because the port counted the frame correctly
6 CRC logicChapter 19.4's residue is wronggood frames are declared bad; only c_crc_errors moves, and the frames were perfect
7 validityChapter 7.3 §3's bounds are wrongundersize and oversize move on conformant frames; nothing else does
8 filterChapter 7.4's table is wronga legal discard — no error, so no required counter moves
9 VLANport membership is wronga legal discard, same signature
10 FIFOChapter 19.5 §14's overflowa legal drop under a stalled consumer, same signature
11 DMAChapter 19.6's descriptorsno descriptor, no delivery, no error
12 driverbuffers not reapedsame, one level up

Rows five and six are the two that surprise people and they surprise in opposite directions.

Row five's frames_in is the one entry in the table that reads "no" where instinct says yes. The port's frame counter increments when a frame is received — at the Chapter 5.2 delimiter, before anything is parsed — so a parser that misreads the length has not affected it at all. That single "no" is what separates site 5 from sites 1, 2 and 3, and it is the second-best question in the method precisely because it is counter-intuitive.

Row six is the case where the frames were never damaged. A fault in Chapter 19.4's residue comparison declares good frames bad, so c_crc_errors climbs on a perfect wire — and every instinct points outward at a cable. The distinguishing evidence is that nothing else moves: no alignment errors, no size errors, and frames_in unaffected. A CRC counter climbing alone is the signature of the check engine, not of the channel.


3. RTL 1 — The Debug Package and the Candidate Mask

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// debugm_pkg -- a fault space as a bit mask, and the observations that
// narrow it.
//
// The whole method is: start with twelve bits set, and clear bits.
// Writing it that way makes two things checkable that a prose
// procedure hides -- whether an observation narrowed anything, and
// whether the remaining set is a single site or a class.
// ---------------------------------------------------------------------
package debugm_pkg;

  // Section 1's twelve sites, in receive order. The ORDER matters:
  // Section 7's conservation checker cuts the space at a boundary,
  // and a boundary is only meaningful if the sites are ordered.
  typedef enum logic [3:0] {
    SITE_CHANNEL   = 4'd0,
    SITE_PHY_LANES = 4'd1,
    SITE_XMII      = 4'd2,
    SITE_PREAMBLE  = 4'd3,
    SITE_PARSER    = 4'd4,
    SITE_CRC_LOGIC = 4'd5,
    SITE_VALIDITY  = 4'd6,
    SITE_FILTER    = 4'd7,
    SITE_VLAN      = 4'd8,
    SITE_FIFO      = 4'd9,
    SITE_DMA       = 4'd10,
    SITE_DRIVER    = 4'd11
  } site_e;

  typedef logic [11:0] site_mask_t;

  localparam site_mask_t ALL_SITES = 12'hFFF;

  // Section 2's table, one row per counter: the sites at which a fault
  // makes this counter move. These are the ONLY facts the method has.
  localparam site_mask_t M_CRC       = 12'b0000_0011_0111;  // 0,1,2,4,5
  localparam site_mask_t M_ALIGN     = 12'b0000_0000_0111;  // 0,1,2
  localparam site_mask_t M_UNDERSIZE = 12'b0000_0101_0001;  // 0,4,6
  localparam site_mask_t M_OVERSIZE  = 12'b0000_0101_0000;  // 4,6
  localparam site_mask_t M_FRAMES_IN = 12'b0000_0000_1111;  // 0,1,2,3
  localparam site_mask_t M_SYMBOL    = 12'b0000_0000_0011;  // 0,1
  localparam site_mask_t M_FILTERED  = 12'b0000_1000_0000;  // 7
  localparam site_mask_t M_VLAN_DISC = 12'b0001_0000_0000;  // 8
  localparam site_mask_t M_FIFO_DROP = 12'b0010_0000_0000;  // 9
  localparam site_mask_t M_DESC_ERR  = 12'b0100_0000_0000;  // 10

  // Section 4: the RMON-required six leave these five sites with one
  // signature between them. Naming the class is what stops a report
  // from claiming a site.
  localparam site_mask_t CLASS_SILENT = 12'b1111_1000_0000;  // 7..11

  typedef enum logic [1:0] {
    EV_MOVED    = 2'd0,   // the counter is climbing
    EV_ZERO     = 2'd1,   // it is not -- Section 10 on what that means
    EV_ABSENT   = 2'd2    // the counter is not implemented
  } evidence_e;

  function automatic int popcount12(site_mask_t m);
    int n = 0;
    for (int i = 0; i < 12; i++) if (m[i]) n++;
    return n;
  endfunction

endpackage

Classification: a package that is one table, and the table is the chapter's entire factual content.

What it teaches: that a debugging method can be written as a set intersection, and that writing it that way makes the two useful questions mechanical. "Did that observation narrow anything?" is a mask comparison; "have we reached a site or a class?" is a population count. A prose procedure answers neither, which is why a prose procedure can run for a day without anybody noticing it stopped making progress.

And it teaches that EV_ABSENT is a third value and not a missing one. A counter that is not implemented is different from a counter reading zero: zero is evidence, absence is not. Section 2 found five optional counters, so EV_ABSENT is the common case on the observations that matter most, and a method that treats absence as zero concludes the fault is not at a site it cannot see.

Deliberately simplified: the masks are literals rather than derived from a machine-readable model of the design, so a design change silently invalidates them. CLASS_SILENT is named once here and used as a constant, where it should fall out of the RMON subset by construction — Section 16's monitor recomputes it and compares. And the whole table is receive-only, so a transmit fault presents as twelve candidates none of which is right.

Production implication: the ordering of site_e is load-bearing and it is the cheapest thing in the block. Chapter 20.3 §13's conservation identity cuts the path at the MAC's output, and a cut is only a cut if the sites on each side are contiguous in the enumeration. Sites 0 to 6 are before the MAC's frame counters and sites 7 to 11 are after — one comparison separates them, and it is the first observation Section 12's procedure makes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// candidate_mask -- the state of a diagnosis: which sites are still
// possible, and whether the last observation helped.
//
// narrowed_nothing is the output that matters. A procedure whose
// observations stop narrowing has finished, and "finished" is not the
// same as "solved" -- Section 4.
// ---------------------------------------------------------------------
module candidate_mask
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        restart,

  input  logic        obs_valid,
  input  site_mask_t  obs_consistent,   // sites consistent with the reading
  input  evidence_e   obs_kind,

  output site_mask_t  candidates,
  output logic [3:0]  n_candidates,
  output logic        narrowed_nothing,
  output logic        converged,        // exactly one site left
  output logic        class_only,       // several, and none separable
  output logic [3:0]  n_observations
);
  site_mask_t next_mask;

  always_comb next_mask = candidates & obs_consistent;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || restart) begin
      candidates       <= ALL_SITES;
      n_observations   <= 4'd0;
      narrowed_nothing <= 1'b0;
    end else if (obs_valid && obs_kind != EV_ABSENT) begin
      narrowed_nothing <= (next_mask == candidates);
      candidates       <= next_mask;
      n_observations   <= n_observations + 4'd1;
    end
  end

  assign n_candidates = 4'(popcount12(candidates));
  assign converged    = (n_candidates == 4'd1);
  // Section 4: the five silent sites survive every RMON observation
  // together, so a mask equal to CLASS_SILENT is a finished diagnosis
  // that has not identified a site.
  assign class_only   = (candidates == CLASS_SILENT);
endmodule

Classification: a state machine with one state variable, whose interesting outputs are both about having stopped.

What it teaches: that converged and class_only are different terminal conditions and a method must distinguish them. A procedure that runs until n_candidates stops falling reaches CLASS_SILENT on every one of the five silent faults — and reports "five candidates" rather than a site. That is the correct answer and Section 20's rejected property is what happens when somebody asserts it cannot occur.

And it teaches why obs_kind != EV_ABSENT gates the update. An absent counter contributes no mask — not the empty mask, which would clear every candidate, and not the full mask, which would be free. It contributes nothing and must not increment n_observations either, because a procedure that counts unavailable observations as steps reports progress it did not make.

Deliberately simplified: obs_consistent is supplied by the caller rather than derived from a counter reading and its mask, so the block cannot check that the caller used the right table. narrowed_nothing is a single-cycle flag and not sticky. And class_only compares against exactly one class, where Section 4 found two — the silent five and the PHY/xMII pair.

Production implication: n_observations against n_candidates is the number that tells a team whether its instrumentation or its method is the problem. Section 8 derives the optimum: 12 candidates should fall to 1 in seven observations. A diagnosis that has made ten observations and has five candidates left has an instrumentation problem, not a method problem — and the two conclusions lead to entirely different meetings.


4. Seven Classes, and the One That Holds Five

Run Section 2's table through Section 3's intersection and the twelve sites collapse. With the RMON-required six counters, the distinct signatures are these.

ClassSitesSignature
Achannelcrc, align, undersize, frames_in all move
BPHY lanes, xMIIcrc, align, frames_in — no undersize
Cpreamble/SFDframes_in moves, nothing else
Dparser offsetcrc, undersize, oversize — frames_in does NOT
ECRC logiccrc only
Fvalidity classifierundersize and oversize only
Gfilter, VLAN, FIFO, DMA, drivernothing moves

Seven classes from twelve sites, and the useful way to read that is as the method's ceiling: no procedure using only the required counters can do better than name one of these seven.

Class G is the problem and it is worth stating exactly what it contains.

Site in class GWhat happened to the frameIs it an error?
the address filternot addressed to this portno — Chapter 7.4
VLAN membershipthe port is not in that VLANno — Chapter 13.2
the receive FIFOthe consumer stalledno — Chapter 19.5 §14
the DMA pathno descriptor was postedno — Chapter 19.6
the host driverthe buffer was not reapedno

Every member of class G is a legal discard, which is why no error counter moves — and three of the five are Chapter 20.3 §13's legal drop reasons, the other two being past the MAC entirely. The instrumentation is behaving correctly and the fault is invisible for exactly that reason.

Class B is the smaller version of the same thing. A PHY lane-alignment fault and an xMII timing fault produce the same six readings, and the counter that separates them — c_symbol_errorslives inside the PHY and is reached over MDIO, which is Chapter 4.5's interface and is not always wired.

Now add the optional counters one at a time, best first, and watch the classes.

AddedClassesLargest class
RMON six only75
+ c_symbol_errors85
+ c_filtered94
+ c_vlan_discards103
+ c_fifo_drops112
+ c_desc_errors121

Row two is the shape of the whole problem. Adding the symbol-error counter splits class B and does nothing at all to class G — it raises the class count and leaves the largest class exactly where it was. The four counters that matter are the four that observe non-errors, and each of them splits off exactly one site.

Class G does not shrink by cleverness. It shrinks one site per counter, and the counters are the ones no standard requires.

Which gives the method its most useful precondition, and it is a question to ask before any waveform is opened:

QuestionIf no
is c_filtered implemented?the filter stays in the shortlist forever
is c_vlan_discards implemented?VLAN membership stays
is c_fifo_drops implemented?the FIFO stays — and it is Chapter 19.5 §14's truncation path
is c_desc_errors readable?the DMA and the driver are one candidate

Four yes/no questions, asked of a datasheet rather than of a waveform, decide whether the coming investigation has five candidates or one. They take a minute and they are almost never the first thing anybody does.


5. RTL 2 — The Symptom Classifier

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// symptom_classifier -- turn a set of counter readings into the mask
// of sites consistent with them.
//
// The block is trivial and the discipline is not: a reading that is
// ZERO narrows the space by INTERSECTING WITH THE COMPLEMENT, and a
// reading that is ABSENT narrows it by nothing at all. Conflating the
// second with the first is the most common error in this chapter and
// Section 10 measures what it costs.
// ---------------------------------------------------------------------
module symptom_classifier
  import debugm_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,

  input  logic       sample,
  input  logic [31:0] d_crc,        // deltas over the sample window
  input  logic [31:0] d_align,
  input  logic [31:0] d_undersize,
  input  logic [31:0] d_oversize,
  input  logic [31:0] d_frames_in,
  input  logic [31:0] d_frames_out,

  // Which of the optional counters this platform actually has.
  input  logic [4:0]  optional_present,   // symbol, filtered, vlan, fifo, desc
  input  logic [31:0] d_symbol,
  input  logic [31:0] d_filtered,
  input  logic [31:0] d_vlan_disc,
  input  logic [31:0] d_fifo_drop,
  input  logic [31:0] d_desc_err,

  output site_mask_t  consistent,
  output logic        consistent_valid,
  output logic [3:0]  observations_usable,
  output logic        symptom_is_silent    // Section 4's class G
);
  site_mask_t m;
  logic [3:0] usable;

  // One helper, applied ten times. A counter that moved keeps the
  // sites it moves for; a counter at zero keeps the complement.
  function automatic site_mask_t apply(site_mask_t cur, logic present,
                                       logic [31:0] delta, site_mask_t moves);
    if (!present)      return cur;            // EV_ABSENT -- no evidence
    else if (delta != 32'd0) return cur & moves;
    else                     return cur & ~moves;
  endfunction

  always_comb begin
    m      = ALL_SITES;
    usable = 4'd0;

    m = apply(m, 1'b1, d_crc,       M_CRC);       usable++;
    m = apply(m, 1'b1, d_align,     M_ALIGN);     usable++;
    m = apply(m, 1'b1, d_undersize, M_UNDERSIZE); usable++;
    m = apply(m, 1'b1, d_oversize,  M_OVERSIZE);  usable++;
    m = apply(m, 1'b1, d_frames_in, M_FRAMES_IN); usable++;
    // d_frames_out is deliberately NOT applied. Section 6.

    m = apply(m, optional_present[0], d_symbol,    M_SYMBOL);
    m = apply(m, optional_present[1], d_filtered,  M_FILTERED);
    m = apply(m, optional_present[2], d_vlan_disc, M_VLAN_DISC);
    m = apply(m, optional_present[3], d_fifo_drop, M_FIFO_DROP);
    m = apply(m, optional_present[4], d_desc_err,  M_DESC_ERR);
    for (int i = 0; i < 5; i++) if (optional_present[i]) usable++;

    consistent          = m;
    observations_usable = usable;
    symptom_is_silent   = (d_frames_in != 32'd0) &&
                          (d_frames_out < d_frames_in) &&
                          (d_crc == 32'd0) && (d_align == 32'd0) &&
                          (d_undersize == 32'd0) && (d_oversize == 32'd0);
  end

  assign consistent_valid = sample;
endmodule

Classification: a ten-way intersection whose whole content is one three-valued helper function.

What it teaches: that the three cases of apply are the method. A counter that moved intersects with its mask; a counter at zero intersects with its complement — which is a real narrowing and is the half people forget — and a counter that is absent intersects with nothing. Writing them as one function with a present argument makes the third case impossible to skip, where a procedure written as prose skips it every time.

And it teaches that symptom_is_silent is a named symptom rather than a derived one. It is the signature of Section 4's class G — frames in, fewer out, no error counter movingand it is worth its own output because it is the single most common real-world Ethernet complaint and the one where the RMON set has already said everything it can.

Deliberately simplified: the six required counters are assumed present, which is what "required" means and is not always true of a switch's per-port statistics. The deltas are supplied rather than sampled, so the block has no opinion about the window length — Section 18's row three. usable++ in a for loop is not synthesisable as written. And a counter that moved by one in a million frames is treated identically to one that moved on every frame, which Section 17 argues is sometimes the whole answer.

Production implication: observations_usable is the number to print at the top of a diagnostic report. It is 5 on a platform with only the required counters — because frames_out is excluded — and 10 on a fully instrumented one. The rest of the report should be read against it: a five-observation diagnosis that names a site has either been lucky or has been wrong, and Section 8 says which is more likely.


6. Why frames_out Carries Zero Bits

Every diagnosis starts by looking at whether frames are reaching the host. That observation carries no information at all, and the arithmetic is one line.

An observation's value is how much it narrows the candidate set. Formally, for a space of twelve sites and a counter that moves for k of them, the reading's entropy is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
H = −p·log2(p) − (1−p)·log2(1−p),   p = k / 12
CounterMoves forpBits
c_crc_errors50.4170.980
frames_in40.3330.918
c_alignment_errors30.2500.811
c_undersize30.2500.811
c_oversize20.1670.650
c_symbol_errors20.1670.650
c_filtered20.1670.650
c_fifo_drops10.0830.414
c_desc_errors10.0830.414
frames_out121.0000.000

The last row is the finding. frames_out falls short for every one of the twelve sites — that is what "the link is broken" means — so the reading is the same whatever the fault is and it eliminates nothing. It is the observation that starts every investigation and it is worth zero bits.

That is not an argument against looking at it. frames_out is what tells you there is a fault; it is the trigger, not a measurement, and confusing the two is why investigations spend their first hour confirming what the ticket said.

RoleBits
frames_out shortthe trigger0.000
c_crc_errorsthe best first question0.980
frames_inthe second0.918
c_fifo_dropsa last-resort separator0.414

Row two is the procedure's actual first step and it is worth knowing why. c_crc_errors moves for the channel, the PHY lanes, the xMII, the parser and the CRC engine's own logic — five of twelve, the closest any counter comes to an even splitso it is the best available question and it is still only 0.98 of a bit. A perfectly balanced question would be worth 1.000.

And the bottom of the table is the reason Section 8's answer is seven and not four. Four of the counters are worth 0.414 bits each, and they cannot be combined: each splits off exactly one site. There is no clever ordering that makes them worth more, because information that is not there cannot be rearranged into existence.


7. RTL 3 — The Conservation Checker

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// conservation_checker -- Chapter 20.3 Section 13's identity, used as
// a diagnostic cut rather than as a scoreboard check.
//
//   frames_in - frames_out == crc + filtered + dropped + under + over
//
// If the identity HOLDS, every missing frame is accounted for by a
// counter and the fault is at a site the instrumentation names. If it
// does NOT hold, frames are disappearing somewhere no counter watches,
// which is a different and much shorter list.
// ---------------------------------------------------------------------
module conservation_checker
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] d_frames_in,
  input  logic [31:0] d_frames_out,
  input  logic [31:0] d_crc,
  input  logic [31:0] d_undersize,
  input  logic [31:0] d_oversize,
  input  logic [31:0] d_filtered,       // may be absent
  input  logic [31:0] d_fifo_drop,      // may be absent
  input  logic [4:0]  optional_present,

  input  logic [31:0] skew_bound,       // Chapter 19.1 Section 6's 12

  output logic        identity_holds,
  output logic        identity_undecidable,
  output site_mask_t  cut_mask,
  output logic [31:0] unaccounted
);
  logic [31:0] accounted, missing;

  always_comb begin
    missing   = d_frames_in - d_frames_out;
    accounted = d_crc + d_undersize + d_oversize
              + (optional_present[1] ? d_filtered  : 32'd0)
              + (optional_present[3] ? d_fifo_drop : 32'd0);

    unaccounted = (missing > accounted) ? (missing - accounted) : 32'd0;

    // Chapter 20.3 Section 13: the identity is exact only when the
    // pipeline is empty. Under load the counters commit at different
    // depths, so a skew of up to twelve frames is not a discrepancy.
    identity_holds = (unaccounted <= skew_bound);

    // An identity that needs counters this platform does not have
    // cannot be evaluated -- and reporting it as FALSE would blame the
    // design for the instrumentation.
    identity_undecidable = !optional_present[1] || !optional_present[3];

    // The cut. If frames are unaccounted for, no counter watches the
    // site, so the candidates are the ones with no counter of their
    // own: preamble detection, VLAN membership, DMA and the driver.
    cut_mask = identity_undecidable ? ALL_SITES
             : (identity_holds ? ~12'b1101_0000_1000 : 12'b1101_0000_1000);
  end
endmodule

Classification: a conservation law used backwards — as a partition of a fault space rather than as a check on a design.

What it teaches: that Chapter 20.3 §13's identity is worth more as a diagnostic than as an assertion. As an assertion it says "frames are not being lost"; as a cut it says which half of the space to search, because a frame that vanishes without incrementing anything can only have vanished where nothing counts. Four of the twelve sites have no counter of their own — preamble detection, VLAN membership, the DMA path and the driver — and the identity separates those four from the other eight in one arithmetic step.

And it teaches that identity_undecidable must be a distinct output from !identity_holds. The identity needs c_filtered and c_fifo_drops; both are optional. On a platform without them the sum is short by construction, unaccounted is large, and a checker that reports "the identity fails" has reported a missing counter as a design fault.

Deliberately simplified: skew_bound is an input rather than derived, where Chapter 19.1 §6's twelve frames in flight is the correct value at 100 Gb/s and 0.19 at 1 Gb/s — the same constant is wrong by a factor of 63 across the family, which is Chapter 20.6 §6's problem arriving in a debugger. missing can underflow if frames_out exceeds frames_in, which happens across a counter wrap. And the cut mask is a literal, where it should be computed as the sites with no counter mask covering them.

Production implication: the identity is the cheapest step in the whole method and it is usually skipped, because it requires reading six counters and doing arithmetic rather than looking at one. It is worth roughly a bit and a half on its own — four sites against eight — and it needs no waveform, no capture and no traffic generator. A procedure whose first two steps are this identity and c_crc_errors has spent no equipment time and has 3 or 4 candidates left.


8. Seven Observations, Not Four

A search over twelve candidates should take four binary questions, because twelve is three point five eight five bits. The counters make it seven. The first question is the crc error counter, which moves for five of the twelve sites and is therefore worth nought point nine eight bits, the most even split available. The second is either the frames in counter or the undersize counter, each worth about nought point nine. The third is the alignment or symbol counter. After three questions the space is down to seven classes, which is what the required counters can distinguish, and the procedure is close to optimal so far. Then it stops being a binary search. The four counters that separate the remaining five sites each move for exactly one of them, so each question can only ask is it this one, and four such questions are needed regardless of the order they are asked in. Each is worth nought point four one four bits. The remedy is not a cleverer ordering but different sensors: a plain frame counter at each of the twelve boundaries makes every question a bisection and brings the total back to four, and twelve such counters cost about one thousand one hundred and eighty eight flip flops against a datapath of fourteen thousand one hundred and sixty six.12 candidates3.585 bitsc_crc_errors0.980 bits — 5 of 12frames_in0.918 bitsc_alignment_errors0.811 bits7 classes, 3stepsnear-optimal so farFour one-sitecounters0.414 bits eachA linear tail4 more questions7 observationsagainst a floor of 412 boundarycountersa bisection — 8.4%12
Figure 2 — three balanced questions, then four that each eliminate one candidate.

Twelve candidates is 3.585 bits, so four binary observations is the floor. A perfectly played diagnosis needs seven, and the gap is a property of the counters rather than of the player.

Observations
information-theoretic floor — ceil(log2 12)4
optimal decision tree over all eleven counters7
optimal tree over the RMON six3, to seven classes
the ratio1.75×

Row three is not better than row two; it is a different question. With only the required counters the space has seven distinguishable classes, and three observations reach themceil(log2 7) is 3, and the RMON counters happen to partition evenly enough to achieve it. The procedure finishes quickly and finishes on a class of five.

Row two is what it costs to go from seven classes to twelve sites, and the cost is four more observations for five more distinctions — a rate of 1.25 observations per site, which is the arithmetic of a linear search and not of a binary one.

Why the tail is linear:

StageCandidatesBest questionBits
112c_crc_errors0.980
25 or 7frames_in or c_undersize~0.9
32 to 5c_alignment_errors or c_symbol_errors~0.8
45 — class Gc_filtered0.722 of that subset
54c_vlan_discards0.811
63c_fifo_drops0.918
72c_desc_errors1.000

Stages four to seven are the linear tail and they are forced. Each of those four counters moves for exactly one site, so each question can only ever answer "is it this one?" — and four such questions are needed to separate five candidates, regardless of order.

A fault space is searchable in log2(n) steps only when the observations partition it. Ethernet's do not, because four of the five decisive counters are one-site indicators.

Which suggests the one instrumentation change that would matter, and it is not another counter.

ChangeClassesObservations to a site
add all four optional counters127
add one counter that moves for 6 of the 12125
add a per-site "frames entered" counter124 — the floor

Row three is what a designer can do for a debugger and nobody does. A single counter at each of the twelve boundaries — frames entering the parser, entering the filter, entering the FIFO, entering the DMA — turns the search into a bisection, and a bisection over twelve reaches a site in four observations, which is the floor.

Flops
Chapter 19.7 §19's counter block — 27 counters and a shadow bank2 672
per counter~99
twelve boundary counters~1 188
Chapter 19.7 §19's MAC datapath14 166
the addition, as a share8.4%

Eight and a half per cent of a datapath to take every future localisation from seven observations to four — and the reason nobody spends it is that the benefit accrues to a different team from the one that pays, which is not an engineering argument and is the real one.


9. RTL 4 — The Discriminator Ranker

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// discriminator_ranker -- given the surviving candidates, say which
// observation to make next.
//
// The ranking is by how evenly a counter splits the CURRENT candidate
// set, not the whole space. A counter worth 0.98 bits at the start can
// be worth zero three steps in, because every site it distinguishes
// has already been eliminated.
// ---------------------------------------------------------------------
module discriminator_ranker
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  site_mask_t  candidates,
  input  logic [4:0]  optional_present,

  output logic [3:0]  best_counter,      // index into the ten
  output logic [3:0]  best_split,        // candidates it would remove
  output logic        no_useful_question,
  output logic [3:0]  questions_left
);
  site_mask_t masks [10];
  logic       avail [10];

  always_comb begin
    masks[0] = M_CRC;       avail[0] = 1'b1;
    masks[1] = M_ALIGN;     avail[1] = 1'b1;
    masks[2] = M_UNDERSIZE; avail[2] = 1'b1;
    masks[3] = M_OVERSIZE;  avail[3] = 1'b1;
    masks[4] = M_FRAMES_IN; avail[4] = 1'b1;
    masks[5] = M_SYMBOL;    avail[5] = optional_present[0];
    masks[6] = M_FILTERED;  avail[6] = optional_present[1];
    masks[7] = M_VLAN_DISC; avail[7] = optional_present[2];
    masks[8] = M_FIFO_DROP; avail[8] = optional_present[3];
    masks[9] = M_DESC_ERR;  avail[9] = optional_present[4];
  end

  logic [3:0] n_cand, yes_n, best_score;

  always_comb begin
    n_cand       = 4'(popcount12(candidates));
    best_counter = 4'd0;
    best_score   = 4'd0;
    questions_left = 4'd0;

    for (int i = 0; i < 10; i++) begin
      if (avail[i]) begin
        yes_n = 4'(popcount12(candidates & masks[i]));
        // An even split is the best question. The score is the size of
        // the SMALLER half -- maximising it maximises the guaranteed
        // reduction, which is the right objective for a worst case.
        if (yes_n != 4'd0 && yes_n != n_cand) begin
          questions_left = questions_left + 4'd1;
          if (((yes_n < (n_cand - yes_n)) ? yes_n : (n_cand - yes_n)) > best_score) begin
            best_score   = (yes_n < (n_cand - yes_n)) ? yes_n : (n_cand - yes_n);
            best_counter = 4'(i);
          end
        end
      end
    end

    best_split         = best_score;
    // Section 4's class G: every remaining counter either moves for
    // all the candidates or for none of them.
    no_useful_question = (questions_left == 4'd0) && (n_cand > 4'd1);
  end
endmodule

Classification: a greedy chooser whose objective is the worst case rather than the average.

What it teaches: that the right score is the size of the smaller half. Maximising the expected reduction favours a lopsided question that is usually cheap; maximising the smaller half bounds the number of steps whatever the answer is — and a debugging procedure is judged on its worst case, because the failures that reach an engineer are the ones the easy path did not solve.

And it teaches that no_useful_question is the method's honest terminal state. When every available counter moves for all the surviving candidates or for none of them, the procedure is finished and has not converged. That is class G with the optional counters absent, it is the common case, and Section 20's rejected property is the assertion that it cannot happen.

Deliberately simplified: the ranking ignores the cost of an observation, and the costs differ by three orders of magnitude — a register read is microseconds, an MDIO transaction is milliseconds, and a capture at a different point in the topology is a site visit. A real ranker divides the score by the cost. The for loop over ten masks with a nested population count is combinational and enormous. And best_counter breaks ties by index rather than by cost, so it will send an engineer to MDIO before checking a free local register.

Production implication: the block's most useful output is the one that looks least interesting. questions_left counts the observations that would still narrow something, and when it reaches zero the correct action is to change the instrumentation, not to keep measuring. Teams reliably do the opposite: the tenth measurement on a converged mask feels like progress and Section 22's first complaint is exactly that pattern.


10. What a Zero Rules Out

A counter that moves is a strong statement. A counter reading zero is a weaker one and is not a worthless one, and the difference between those two claims is where most Ethernet debugging goes wrong.

Formally, a zero reading intersects the candidate set with the counter's complement — so it eliminates exactly the sites the counter moves for.

CounterA move eliminatesA zero eliminates
c_crc_errors7 of 125
frames_in84
c_alignment_errors93
c_undersize93
c_oversize102
c_fifo_drops111
c_desc_errors111
frames_out012 — and it never reads zero

Both columns of every row sum to twelve, which is the point: a reading eliminates the sites it is inconsistent with, and "moved" and "zero" are inconsistent with complementary sets. The asymmetry is not in the information; it is in which set is bigger.

Three zeros are worth having and the rest are nearly free of content.

Zero readingEliminatesWorth it?
c_crc_errors = 0channel, PHY, xMII, parser, CRC logicyes — five sites, the largest single step
frames_in = 0channel, PHY, xMII, preambleyes, and it means the frames never arrived
c_fifo_drops = 0the FIFOonly if the counter exists

Row one is the single most valuable observation in the method and it is a zero. c_crc_errors at zero over a window in which frames were lost eliminates everything from the connector to the check enginefive of twelve sites, 0.98 bits — and it costs one register read. The instinct to go looking for CRC errors is right; the instinct to stop when there are none is exactly wrong.

And there is one reading that is not evidence at all, which Section 3's EV_ABSENT exists for:

MeansEliminates
c_fifo_drops reads 0the FIFO did not drop1 site
c_fifo_drops is not implementednothing0 sites
the register reads 0 because it is not implementednothing0 — and it looks like the first row

Row three is the trap and it is a hardware fact rather than a methodological one. An unimplemented register in a MAC's statistics block very often reads as zero rather than faultingso the two readings are byte-identical and the only way to tell them apart is the datasheet. A method that does not check implementation first will confidently eliminate a site on the strength of a register that does not exist, which is the most expensive single error available in this chapter.

A zero eliminates the counter's sites. An absent counter eliminates nothing. On most hardware they read the same, so implementation is a precondition and not a detail.

One more asymmetry, because it decides the order of Section 12's steps.

A moving counter and a zero counter carry the same information in the formal sense — they eliminate complementary sets — but they do not carry the same risk.

A moveA zero
can be produced by an absent counter?noyes
can be produced by too short a window?noyes — Section 18's row nine
can be produced by a counter that under-counts?it still movesonly if it under-counts to zero
failure modenoneeliminates a site wrongly

A counter that moves is self-validating: something incremented it. A counter at zero is a claim about the absence of an event, and three separate mechanisms produce that reading without the absence being real — the register does not exist, the window was too short, or Chapter 19.7 §7's addend is undersized on exactly this traffic.

Which is why the procedure reads the most informative counter first and treats its zero as the strongest step it will take. c_crc_errors at zero eliminates five sites — the largest single move in the methodand it is also the single reading most worth double-checking, by lengthening the window before acting on it.


11. RTL 5 — The Zero-Evidence Guard

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// zero_evidence_guard -- distinguish "this counter read zero" from
// "this counter does not exist", when the register returns zero in
// both cases.
//
// The trick is that an unimplemented counter is zero under conditions
// where an implemented one CANNOT be. If frames are being filtered --
// which a deliberately wrong destination address guarantees -- then a
// c_filtered of zero is proof the counter is not there.
// ---------------------------------------------------------------------
module zero_evidence_guard
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  // A probe: traffic the environment KNOWS must move each counter.
  input  logic        probe_active,
  input  logic [2:0]  probe_kind,        // 0 filtered, 1 vlan, 2 fifo, 3 desc
  input  logic [31:0] probe_frames_sent,

  input  logic [31:0] d_filtered,
  input  logic [31:0] d_vlan_disc,
  input  logic [31:0] d_fifo_drop,
  input  logic [31:0] d_desc_err,

  output logic [4:0]  proven_present,
  output logic [4:0]  proven_absent,
  output logic        probe_inconclusive
);
  localparam int PROBE_MIN = 1000;

  logic enough;
  assign enough = probe_active && (probe_frames_sent >= 32'(PROBE_MIN));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      proven_present <= 5'd0;
      proven_absent  <= 5'd0;
    end else if (enough) begin
      case (probe_kind)
        3'd0: if (d_filtered  != 32'd0) proven_present[1] <= 1'b1;
              else                      proven_absent [1] <= 1'b1;
        3'd1: if (d_vlan_disc != 32'd0) proven_present[2] <= 1'b1;
              else                      proven_absent [2] <= 1'b1;
        3'd2: if (d_fifo_drop != 32'd0) proven_present[3] <= 1'b1;
              else                      proven_absent [3] <= 1'b1;
        default: if (d_desc_err != 32'd0) proven_present[4] <= 1'b1;
                 else                     proven_absent [4] <= 1'b1;
      endcase
    end
  end

  // Section 12's precondition, as a bit: until every optional counter
  // has been probed, a zero reading on any of them is not evidence.
  assign probe_inconclusive = ((proven_present | proven_absent) != 5'b11110);
endmodule

Classification: an instrumentation test, which is a different kind of block from everything else in the chapter.

What it teaches: that the way to tell a zero from an absence is to make the counter move on purpose. Send a thousand frames to a destination address the port does not own; Chapter 7.4's filter must discard every one of them. If c_filtered is still zero, the counter is not implemented — and that is now a proven fact rather than an assumption. The probe is Chapter 20.5's error injector used against the instrumentation instead of against the design.

And it teaches that this is a precondition rather than a step. probe_inconclusive should be checked before the first diagnostic observation, because every subsequent conclusion drawn from a zero depends on it — and a procedure that probes after the fact has to discard everything it concluded.

Deliberately simplified: PROBE_MIN is 1 000 frames, which is a guess at how many are needed for a counter to be visibly non-zero and takes no account of a counter that samples. The FIFO probe cannot be performed from the wire at allChapter 20.5 §12 proved no sequence of frames overflows a correctly sized FIFO — so probe_kind 2 requires a stalled consumer, which this block silently assumes somebody arranged. And proven_absent is never cleared, so a platform change mid-session is not noticed.

Production implication: the FIFO row is the one that makes this block honest about its limits. Three of the four optional counters can be probed from the wire with one malformed or misaddressed frame each; the fourth cannot be probed from the wire at all. So on a platform where c_fifo_drops reads zero and cannot be provoked, the receive FIFO remains in the candidate set permanently — and Chapter 19.5 §14's truncation path is exactly the failure that most needs localising. The chapter's method is strongest where the design is easiest and weakest where it is hardest, which is worth knowing before an outage rather than during one.


12. The Procedure, Written Down

The procedure has eight steps and the first six cost almost nothing. Step zero is a datasheet lookup: four yes or no questions about which optional counters this platform implements, which decides whether the investigation can end with a site or a shortlist. Step zero b is a probe, four thousand frames, proving each optional counter actually moves so that a later zero reading is evidence rather than an absence. Step one compares frames in against frames out, which confirms a fault and eliminates nothing at all, because that reading is the same whatever the fault is. Step two evaluates the conservation identity from the scoreboard chapter, six register reads, which separates the four sites with no counter of their own from the eight that have one. Step three reads the crc error counter, the most informative single question available. Step four reads the alignment and undersize counters, which separate the physical sites from the logical ones and the parser from the validity classifier. Step five walks the optional counters one at a time. Only at step six, with the candidates down to one site or one class of five, is a capture worth taking, and by then it is aimed at one interface under one condition with an oracle.Step 0: thedatasheeta minute, sets theceilingStep 0b: theprobe4 000 frames, onceStep 1: frames invs outthe trigger — 0 bitsStep 2:conservation6 reads — 12 to 8 or 4Step 3:c_crc_errors0.980 bitsStep 4: align,undersizetwo readsStep 5: theoptional fourone site eachStep 6: a capturehours, and now aimed12
Figure 3 — eight steps, and only the last one needs equipment.

Eight steps. The first three cost nothing, the next three cost a register read each, and only the last two need equipment.

StepDoCostsNarrows from 12 to
0read the datasheet: which optional counters exist?a minute12 — but it decides the floor
0bprobe each one — Section 114 000 frames12
1frames_in against frames_out: is there a fault?a read12 — zero bits, Section 6
2the conservation identity — Section 7six reads8 or 4
3c_crc_errorsa read5 or 3
4c_alignment_errors and c_undersizetwo reads1 to 3
5the optional counters, in Section 8's ordera read each1, if they exist
6only now: a capture or a waveformhours

Step 6's position is the whole argument for writing the procedure down. Every step above it is a register read, and together they take a candidate space of twelve to a single site on a fully instrumented platform or to a class of five otherwise. A capture answers one question and takes three hours; a register read answers a question worth 0.98 bits and takes a millisecond.

The steps in order, with what each one is actually asking.

Step 0 — implementation. Four questions of a datasheet. The answers decide whether the investigation can end with a site or with a shortlist, and they are free.

Step 0b — the probe. Three of the four optional counters can be made to move deliberately. Do it once per platform, not once per outage — Section 11.

Step 1 — the trigger. frames_out short of frames_in confirms a fault and eliminates nothing. Its value is that it bounds the window: the counters read from here are deltas, and Section 18's row three is about how long that window should be.

Step 2 — conservation. Does the arithmetic balance? Chapter 20.3 §13's identity, with Chapter 19.1 §6's twelve-frame skew allowance. It balances → the missing frames are accounted for by a named counter. It does not → they vanished where nothing counts, which is four sites.

Step 3 — the best single question. c_crc_errors splits five from seven — the most even split any counter offers. Moving points at the channel, the PHY, the xMII, the parser or the CRC logic; zero eliminates all five.

Step 4 — the separators. c_alignment_errors separates the physical sites from the logical ones; c_undersize and c_oversize separate the parser from the validity classifier, which is the one pair in the space that is genuinely easy to confuse and genuinely easy to separate.

Step 5 — the linear tail. Four counters, four sites, one at a time. Section 8's arithmetic says this cannot be improved with these counters.

Step 6 — equipment. By now the candidate set is a site or a class of five, and a capture taken at step 6 is aimed — at one interface, for one condition, with an oracle. A capture taken at step 1 is a fishing expedition and Chapter 21.9 is about what it can and cannot show.

And the procedure's own stopping rule, which is the part people leave out:

Stop when questions_left is zero. If the mask is a single site, that is the answer. If it is a class, the answer is "these five, and here is the counter that would separate them" — and that is a finished diagnosis, not a failed one.

Terminal stateReportNext action
one sitethe sitefix it
class G — five sitesthe class, and which counter is missinginstrumentation, not investigation
class B — two sitesPHY or xMIIread the PHY over Chapter 4.5's MDIO
empty maskthe model is wrongSection 22's third complaint

And there is a variant of the procedure worth naming, because it is what a monitoring system should run rather than what a person runs.

Every step up to 5 is a register read, so the whole thing can be executed continuously on every port in a fleet, at no cost, with nobody watching.

Run by a person, during an outageRun by a monitor, continuously
step 0once, and usually skippedonce per platform, recorded
step 0brarely doneat commissioning
steps 1 to 5eleven reads, by handeleven reads, every minute
what it producesan answer, after an houra candidate mask, already narrowed
best_possible_answerdiscovered latea fleet-wide inventory

Row five is the one that changes how a team spends its time. site_unreachable is a property of the port, it never changes, and knowing it for every port before anybody is paged turns "this link is dropping frames" into either a one-hour investigation or a four-hour one, decided in advance. The reads cost nothing and the inventory is built once.

Row four is the state nobody plans for and it happens. An empty candidate mask means two observations contradicted each other under Section 2's table — which is not a design fault and not an instrumentation fault. It is a fault in the table, and the correct response is to find which pair of observations is inconsistent rather than to keep measuring.


13. RTL 6 — The Evidence Recorder

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// evidence_recorder -- what was observed, in what order, and what each
// observation ruled out.
//
// The output that matters is not the conclusion. It is the AUDIT: a
// diagnosis that cannot say which observation eliminated which site
// cannot be reviewed, and an unreviewable diagnosis is an opinion.
// ---------------------------------------------------------------------
module evidence_recorder
  import debugm_pkg::*;
#(
  parameter int MAX_STEPS = 16
)(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        restart,

  input  logic        obs_valid,
  input  logic [3:0]  obs_counter,       // which of the ten
  input  evidence_e   obs_kind,
  input  site_mask_t  before_mask,
  input  site_mask_t  after_mask,

  output logic [3:0]  n_steps,
  output site_mask_t  eliminated_by [MAX_STEPS],
  output logic [3:0]  counter_of    [MAX_STEPS],
  output logic [3:0]  wasted_steps,
  output logic        contradiction
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || restart) begin
      n_steps       <= 4'd0;
      wasted_steps  <= 4'd0;
      contradiction <= 1'b0;
      for (int i = 0; i < MAX_STEPS; i++) begin
        eliminated_by[i] <= 12'd0;
        counter_of[i]    <= 4'd0;
      end
    end else if (obs_valid && n_steps < 4'(MAX_STEPS)) begin
      eliminated_by[n_steps] <= before_mask & ~after_mask;
      counter_of[n_steps]    <= obs_counter;
      n_steps                <= n_steps + 4'd1;

      // An observation that eliminated nothing. Section 9's ranker
      // should have prevented it; recording it is how a team finds
      // out the ranker was bypassed.
      if ((before_mask & ~after_mask) == 12'd0)
        wasted_steps <= wasted_steps + 4'd1;

      // Section 12's row four: two readings that cannot both be true
      // under Section 2's table.
      if (after_mask == 12'd0) contradiction <= 1'b1;
    end
  end
endmodule

Classification: a log, and the only block in the chapter whose value is entirely retrospective.

What it teaches: that eliminated_by is the field that makes a diagnosis reviewable. "It is the DMA" is an opinion; "c_crc_errors was zero, which eliminated five sites; the conservation identity held, which eliminated four more; c_filtered and c_vlan_discards were zero, which eliminated two" is an argument — and the difference is that the second one can be checked by somebody who was not there.

And it teaches that wasted_steps is a measurement of the procedure rather than of the fault. A step that eliminated nothing was a question whose answer was already known, and Section 9's ranker exists to prevent exactly that. A post-mortem with three wasted steps out of seven has found a process problem, and it is a process problem that costs about three hours per outage.

Deliberately simplified: the recorder stores MAX_STEPS masks in flops — 16 × 12 bits plus 16 × 4 is 256 flops — where a real one writes to a log. There is no timestamp, so the record cannot say how long each step took, which is the other number a post-mortem wants. And contradiction is set and never explained: the block knows the mask went empty and not which earlier observation the last one contradicted.

Production implication: the contradiction case is worth instrumenting properly, because it is the one that indicts Section 2's table. When a mask goes empty, one of the ten mask constants is wrong for this design — a counter that moves for a site the table says it does not, or the reverse. Storing before_mask for every step makes it a five-minute search; storing only the conclusion makes it unfindable, and the table then stays wrong for every future investigation.


14. What a Debugging Method Must Never Do

Six prohibitions. Three are about evidence and three are about stopping, and every one of them produces a confident wrong answer rather than a visible failure.

NeverBecause
1treat an absent counter as a zeroSection 10 — on most hardware they read the same
2conclude a site from a symptomc_crc_errors moving is consistent with five sites
3take a capture before step 5Section 12 — three hours for less than a bit
4report a class as a siteSection 4's class G has five members
5keep measuring when questions_left is zeroSection 9 — the instrumentation is the blocker
6discard the evidence log after the fixSection 13 — the table stays wrong

Row two is the one that is violated hourly and it has a name in this track. "CRC errors are climbing, so the cable is bad" is Chapter 11.4 §15's class 48 — a symptom implying a cause where the mapping is many-to-many — arriving as an operational habit rather than as an assertion. It is right about five twelfths of the time and it is the reason cables get replaced on links whose fault is in the parser's offset arithmetic.

Row four is the one that damages a team's credibility. Reporting "the fault is in the DMA" when the evidence supports "the filter, VLAN, the FIFO, the DMA or the driver" is a one-in-five guess dressed as a conclusion. When it is wrong, the next investigation starts from a discredited method — and the honest report costs nothing: five candidates, and c_fifo_drops would separate them.

And the two that look like impatience and are not:

Why it is a prohibition
row threea capture is worth about one bit and costs three hours
row fivemore measurement of the same counters adds exactly zero

Both produce activity that looks like progress and is not, which is the property all six share and is why a procedure needs a stopping rule written down before the outage rather than during it.


15. RTL 7 — Debug Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// debugm_telemetry -- how well the method is doing, which is a
// different question from what the fault is.
//
// Two groups. "Progress" says whether the procedure is still
// narrowing. "Ceiling" says how good an answer this platform can
// produce at all -- and the second is knowable before the first step.
// ---------------------------------------------------------------------
module debugm_telemetry
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  site_mask_t  candidates,
  input  logic [3:0]  n_observations,
  input  logic [3:0]  wasted_steps,
  input  logic [4:0]  optional_present,
  input  logic        probe_inconclusive,
  input  logic        contradiction,
  input  logic        no_useful_question,

  // Progress.
  output logic [3:0]  n_candidates,
  output logic [15:0] efficiency_pct,
  output logic        stalled,

  // Ceiling -- all knowable at step 0.
  output logic [3:0]  classes_available,
  output logic [3:0]  largest_class,
  output logic [3:0]  best_possible_answer,
  output logic        site_unreachable
);
  localparam int OPTIMAL_STEPS = 7;

  assign n_candidates = 4'(popcount12(candidates));

  // What fraction of the observations made were useful.
  always_comb
    efficiency_pct = (n_observations == 4'd0) ? 16'd100
      : 16'(((32'(n_observations) - 32'(wasted_steps)) * 32'd100)
            / 32'(n_observations));

  assign stalled = no_useful_question || contradiction;

  // Section 4's table as arithmetic: seven classes with the required
  // six, and one more class per optional counter present.
  always_comb begin
    classes_available = 4'd7;
    for (int i = 0; i < 5; i++)
      if (optional_present[i]) classes_available = classes_available + 4'd1;

    // The silent class shrinks by one per NON-symbol optional counter.
    largest_class = 4'd5;
    for (int i = 1; i < 5; i++)
      if (optional_present[i] && largest_class > 4'd1)
        largest_class = largest_class - 4'd1;
  end

  assign best_possible_answer = largest_class;
  // A platform on which the answer can never be a single site.
  assign site_unreachable = (largest_class > 4'd1) || probe_inconclusive;
endmodule

Classification: a reporting block whose second group of outputs does not depend on any observation at all.

What it teaches: that best_possible_answer is computable at step 0 — from a datasheet, before any traffic — and that it is the single most useful thing to tell a team at the start of an investigation. "On this platform the best answer available is a class of three" changes the plan; discovering it after four hours changes only the mood.

And it teaches that efficiency_pct measures the procedure and n_candidates measures the fault. They are independent: a procedure can be 100% efficient and still finish with five candidates, which is the platform's fault, and it can be 40% efficient and finish with one, which is luck. Reporting only the second is how a bad method survives a good outcome.

Deliberately simplified: largest_class decrements once per optional counter without checking which — so a platform with c_symbol_errors and nothing else gets credit it has not earned, since the symbol counter splits class B and not class G. The loop starts at index 1 to exclude it, which is correct and is a hardcoded fact about Section 4's table. OPTIMAL_STEPS is declared and unused, which is where an efficiency figure against the seven-step optimum should go.

Production implication: site_unreachable is the bit that should appear in a monitoring system rather than in a debug session. It is a property of the platform, it never changes, and knowing it for every port in a fleet turns "this link is dropping frames" into either a four-hour investigation or a one-hour one before anybody is paged. Chapter 19.7 §15's telemetry made the same argument about the design; this is the same argument about the instrumentation around it.


16. RTL 8 — The Debug Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// debugm_conformance_monitor -- six verdicts about the METHOD, and
// four of them fire on a method that is being followed correctly.
//
// That ratio is the chapter's point. Most of what limits an Ethernet
// diagnosis is not the investigator; it is a counter that does not
// exist, and no amount of care substitutes for it.
// ---------------------------------------------------------------------
module debugm_conformance_monitor
  import debugm_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  site_mask_t  candidates,
  input  logic [3:0]  n_observations,
  input  logic [3:0]  wasted_steps,
  input  logic [4:0]  optional_present,
  input  logic        probe_inconclusive,
  input  logic        contradiction,
  input  logic        capture_taken,
  input  logic [3:0]  capture_at_step,

  output logic        instrumentation_limited,
  output logic        probe_skipped,
  output logic        capture_premature,
  output logic        steps_wasted,
  output logic        table_wrong,
  output logic        method_exhausted,
  output logic        method_sound
);
  localparam int CAPTURE_MIN_STEP = 5;

  // Section 4: the four non-symbol optional counters are what shrink
  // class G, and their absence is a platform fact.
  assign instrumentation_limited = (optional_present[4:1] != 4'b1111);

  // Section 11: a zero reading is not evidence until the counter is
  // proven to exist.
  assign probe_skipped = probe_inconclusive && (n_observations > 4'd0);

  // Section 12's row three. A capture before the register reads are
  // exhausted costs hours and is worth about a bit.
  assign capture_premature = capture_taken &&
                             (capture_at_step < 4'(CAPTURE_MIN_STEP));

  assign steps_wasted = (wasted_steps > 4'd0);

  // Section 13: an empty mask means two observations contradicted each
  // other under Section 2's table, which indicts the table.
  assign table_wrong = contradiction;

  assign method_exhausted = (popcount12(candidates) > 1) &&
                            (n_observations >= 4'd7);

  assign method_sound = !probe_skipped && !capture_premature &&
                        !steps_wasted && !table_wrong;

  always_ff @(posedge clk) begin
    if (rst_n && instrumentation_limited)
      $display("[debug] best possible answer on this platform is a class, not a site");
  end
endmodule

Classification: an auditor of a procedure, whose most-fired verdict is not about the procedure.

What it teaches: that instrumentation_limited is deliberately not a term of method_sound. A platform missing three optional counters does not make the method unsound — the method is doing everything available correctly and the answer is a class. Folding a platform limitation into a verdict about the investigator's work is how a team learns to distrust its own process for a reason that has nothing to do with it.

And it teaches why method_exhausted is separate from stalled. Section 15's stalled means no question remains; method_exhausted means seven observations have been made and more than one candidate survives — which, by Section 8's arithmetic, should be impossible on a fully instrumented platform. When both are false and the count is above seven, the observations are being wasted, and steps_wasted says so.

Deliberately simplified: CAPTURE_MIN_STEP is 5, which is a judgement and is wrong for a fault whose symptom is a malformed frame — there, a capture at step 1 is the correct first move because the frame's contents are the evidence. The monitor has no notion of symptom class, which Chapter 21.2 supplies. And $display fires every cycle that the condition holds, which is deliberate for the same reason as elsewhere in the track: a silence reads as a pass.

Production implication: probe_skipped is the verdict worth wiring into a team's tooling rather than into a report. It fires the moment somebody draws a conclusion from a counter whose existence has not been established — and Section 10 showed that an unimplemented counter and a genuinely zero one read identically. One bit, checked before the first conclusion, prevents the most expensive single error in the chapter, which is eliminating the site the fault is actually at.


17. Where the Method Stops

The method's output is a mask. This section is about the four ways that mask fails to be a single site, and which of them are the investigator's problem.

Terminal stateCauseWhose problemFrequency
a single sitefull instrumentation, sound methodonly with all eleven counters
class G — five sitesthe four optional counters absentthe platformthe common case
class B — two sitesno PHY symbol counter over MDIOthe platformcommon
an empty maskSection 2's table is wrong for this designthe tablerare and important
more than seven observationsthe ranker was bypassedthe methodavoidable

And one terminal state that is not in the table because it is not a state of the mask.

The procedure can finish correctly, name a single site, and be wrong — when the fault is not in the space at all. A transmit-side fault at the far end, a misconfigured switch three hops away, an MTU mismatch that drops only large frames: none of the twelve sites is faulty and the method will still converge, because it is an intersection over a table that has no row for "none of the above."

Detectable by the mask?Detectable at all?
a fault outside the twelvenoonly by the conservation identity balancing
a transmit faultnoSection 18's row six
a size-dependent dropnothe counters are not conditioned on size

Row three is the one worth knowing about because it has a cheap tell. An MTU mismatch drops only frames above a threshold, so frames_out is short and c_oversize never moves — which looks exactly like class G. The distinguishing evidence is not a counter at all: it is that the loss rate is a function of the frame size, and no register in the RMON set is conditioned on size. Chapter 19.7 §2's histogram is the nearest thing, and comparing its transmit and receive shapes is the one observation that separates a size-dependent drop from an unconditional one.

Rows two and three cover seven of the twelve sites — 58.3% — and neither is anybody's mistake. They are what the RMON standard's scope costs: the standard specifies error counters, and the five sites in class G do not produce errors.

And there is a fifth state the mask cannot represent at all.

The method assumesWhen it is false
one faultone site is faultytwo faults mask each other's evidence
a stationary faultthe counters describe the same faultan intermittent link changes between reads
a receive faultthe space is the receive patha transmit fault presents as twelve wrong candidates

Row one is the assumption whose failure is hardest to see. Two simultaneous faults do not produce the union of their signatures — they produce the signature of whichever one the frame meets first, because a frame discarded at the parser never reaches the FIFO. So the candidate mask converges, confidently, on the earlier of the two sites; the fix is applied; the symptom persists; and the second investigation starts from scratch with the first one's conclusion now looking like an error.

The detection is available and it is not in the mask. Chapter 20.3 §13's conservation identity is the one observation that sees all the loss at once:

One faultTwo faults
candidate maskconvergesconverges — on the earlier site
unaccounted after the fixzerostill non-zero
what to doclose itrestart with the mask reset

Row two is the check to run after every fix and before closing anything, and it costs six register reads. A fix that eliminates the symptom and leaves unaccounted non-zero has not finished — and that is the cheapest available protection against the second-fault case, which is otherwise indistinguishable from a bad diagnosis.

And the masking is not symmetric, which is worth one more table because it says which pairs are dangerous.

A frame meets the twelve sites in order. So a fault at site i masks a fault at site j whenever i is less than j and the earlier fault discards the frame — and every one of the twelve except the CRC-logic case does discard it.

Earlier faultMasksOf the twelve
1 channelsites 2 to 1211
4 preamblesites 5 to 128
7 validitysites 8 to 125
10 FIFOsites 11 and 122
12 drivernothing0

Summed over all twelve sites, 66 of the 132 ordered pairs are masking pairs — exactly half. So if two independent faults are present, there is a 50% chance the method sees only one of them, and it sees the earlier one with certainty rather than at random.

Value
ordered pairs of distinct sites132
pairs where the earlier masks the later66
share50.0%
which one the mask converges onalways the earlier

The "always the earlier" row is what makes the case tractable. A masked second fault is not a random error; it is systematically downstream of the one that was found — so the re-check after a fix has a direction: look at the sites after the one you just fixed, and the conservation identity says whether you need to. That is a much better position than "the fix did not work", and it costs the same six reads.


18. What the Method Assumes

Nine assumptions. Three are about the counters, three about the fault and three about the design — and the one most likely to be false is about the window.

AssumptionFromIf false
1a counter that exists is correctChapter 19.7Section 2's table is a fiction
2an unimplemented counter reads zerohardware conventionit faults, which is better
3the deltas cover one windowthe readerSection 18's row nine
4exactly one site is faultyconvenienceSection 17 — the mask converges on the earlier
5the fault is stationaryconveniencean intermittent link changes between reads
6the fault is on the receive pathscopetwelve wrong candidates
7Section 2's table matches this designModules 5 to 19an empty mask — Section 13
8the counters commit within 12 framesChapter 19.1 §6the conservation identity mis-fires
9the window is long enough to see a rare faultnothing at alla zero that is not a zero

Row nine is the assumption nothing in this chapter supports and it deserves its own arithmetic. A fault that corrupts one frame in ten million is a real and common failure mode — a marginal connector, a lane at the edge of its eye. At 100 Gb/s and minimum size, 148.9 million frames per second means one corruption every 67 milliseconds, which any window catches. At 1 Gb/s the same rate is 1.488 million frames per second and one corruption every 6.7 secondsso a one-second window reads c_crc_errors as zero and eliminates five sites that should have stayed.

Line rateFrames per second, minimum sizeA 1-in-10⁷ fault appears everyA 1 s window sees
1 Gb/s1.488 M6.7 s0 or 1 — usually 0
10 Gb/s14.88 M672 msabout 1
100 Gb/s148.9 M67 msabout 15

The same fault, the same counter, and the same window produce a confident elimination at 1 Gb/s and a clear detection at 100. Which gives the rule the method needs and does not have:

A zero is only evidence if the window was long enough for the rarest fault you care about. At 1 Gb/s that is tens of seconds; at 100 Gb/s it is tens of milliseconds.

Row one is the assumption whose failure is the most embarrassing. Chapter 19.7 §7 showed a counter sized for conformant traffic counts exactly half of a 47-octet runt flood, because its addend is one bit where two are needed. A method reading such a counter gets a number that is wrong by a clean fraction — and a clean fraction reads as a real measurement rather than as a bug.

And three things deliberately not assumed:

Not assumedWhy not
that a symptom implies a siteSection 2's table is many-to-many — Chapter 11.4 §15's class 48
that a converged mask is correctSection 17's two-fault case converges too
that the investigator has a captureSection 12 — five of the eight steps are register reads

Row three is the chapter's practical thesis. Everything up to step 5 is a register read, which means the whole method can be run from a script, from a monitoring system, or by somebody with no equipment and a serial console — and it reaches a site or a class of five before anybody unpacks a capture tool.


19. The Cost, Accounted

A debugging method is not hardware, so the accounting here is of two different things: what the method's own machinery costs if you build it, and what the instrumentation it wants costs in the design.

BlockFlopsNature
debugm_pkg0one table
candidate_mask~2112 bits of mask and two counters
symptom_classifier0combinational
conservation_checker0combinational
discriminator_ranker0combinational
zero_evidence_guard~10two 5-bit results
evidence_recorder — 16 × 16 bits~268the audit log
debugm_telemetry0combinational
debugm_conformance_monitor0combinational
total~299 flops

Two hundred and ninety-nine flops is the cheapest structure in Modules 20 and 21, and the reason is that a decision procedure over twelve candidates has almost no state: twelve bits of mask, a step count, and a log. Chapter 20.6 §19's join table was 74 017 flops because it remembers something per frame; this remembers something per observation, and there are seven of those.

The instrumentation is the real cost and it is in the design.

FlopsBuys
the RMON sixalready there — Chapter 19.77 classes, largest 5
c_filtered~99splits one site off class G
c_vlan_discards~99one more
c_fifo_drops~99one more
c_desc_errorsin the driver — 0 gatesone more
the four optional~29712 classes, largest 1
twelve boundary counters — Section 8~1 1884 observations instead of 7

Two hundred and ninety-seven flops — 2.1% of Chapter 19.7 §19's 14 166-flop datapath — is the difference between a diagnosis that names a site and one that names five. That is the whole economic content of this chapter, and it is a smaller number than any single mechanism Module 19 argued about.

FlopsShare of the datapath
Chapter 19.4's correction barrels5 397 XOR termsthe largest single mechanism
Chapter 19.7's shadow bank9927.0%
the four optional counters~2972.1%
twelve boundary counters~1 1888.4%

Row three is 2.1% and it is not spent, on most parts. Row two is 7.0% and it is spent, because a common reading instant is a correctness requirement and a debuggable fault space is not. Nothing in the standard requires a counter for a non-error, which is the whole reason class G exists — and Section 4's table is what the omission costs, expressed as five candidates instead of one.

And a last comparison, because it is the one that persuades:

Cost
four optional counters~297 flops, once, at design time
one investigation that ends at class Ga capture, three hours, and a one-in-five guess

There is a second cost this chapter's method imposes that is not flops at all, and it is the one a team actually feels.

Cost per investigation
step 0 — four datasheet questionsone minute, once per platform ever
step 0b — the probe4 000 frames, once per platform ever
steps 1 to 5 — eleven register readsmilliseconds, scriptable
step 6 — a capturethree hours, and only if steps 1 to 5 left a class

Rows one and two are amortised over a platform's whole life and rows three is amortised over nothing because it is free. The entire method costs one minute, once, and then milliseconds per outage — against three hours for the single observation it is designed to postpone.

Which produces the comparison worth putting in front of whoever decides the 2.1%:

A port without the four countersWith them
terminal statefive candidatesone site
captures per outageat least oneoften none
expected wrong fixes0.8 — four in five0
cost, once0 flops~297 flops

Row three is the number that does not appear on any design review slide. A class of five resolved by guessing is wrong four times in five, and each wrong fix is a maintenance window, a change record and a second investigation that starts from a discredited method. Two hundred and ninety-seven flops is about three tenths of what Chapter 19.7's shadow bank costs, and the shadow bank was built without argument because a common reading instant is a correctness requirement. Debuggability is not, which is the entire reason this section exists.


20. Properties Worth Asserting, and One Worth Refusing

A fault localiser's natural property is that it terminates with exactly one candidate, because anything else looks like a bug in the search. On a platform implementing all eleven counters the property holds, because all twelve sites have distinct signatures. On a platform implementing only the six counters the remote monitoring standard requires, the property fails for seven of the twelve sites: the five in the silent class, which discard frames legally and move no counter, and the two separated only by a symbol error counter inside the physical layer. That is fifty eight point three per cent of the space, and it fails on a procedure that is behaving perfectly, because no search can separate what the observations do not distinguish. Worse, the natural repair is to make the localiser pick one, by prior or by order, which converts a truthful shortlist of five into a one in five guess reported with the confidence of a proof. The correct properties constrain the search instead: soundness, that it never eliminates a site consistent with every observation, and completeness, that it eliminates every site inconsistent with one. Both are checkable at every step. The uniqueness of the answer is not the search's contract but the instrumentation's, and it belongs in a report as a ceiling computed at step zero from a datasheet.It ends with onesitethe tempting propertyAll 11 countersholds — 12 classesThe RMON sixfails on 7 of 12Class G and classB58.3% of the spaceThe naturalrepairmake it pick oneA guess, dressedupworse than thepropertySoundnessnever over-eliminateCompletenessalways fully eliminateReport theceilingstep 0, from adatasheet12
Figure 4 — the property names the search and constrains the sensors.

Thirty-three properties and eight covers, in four groups: the mask's algebra, the evidence's three-valuedness, the procedure's progress, and the terminal states.

Group one — the mask.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A candidate set only ever shrinks, and an observation that grows it
// is a table error rather than a new hypothesis.
p_mask_monotone:    assert property (@(posedge clk) disable iff (!rst_n)
                      obs_valid |=> ((candidates & $past(candidates)) == candidates));

p_mask_starts_full: assert property (@(posedge clk) disable iff (!rst_n)
                      restart |=> (candidates == ALL_SITES));

p_mask_intersect:   assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && obs_kind != EV_ABSENT) |=>
                        (candidates == ($past(candidates) & $past(obs_consistent))));

p_count_matches:    assert property (@(posedge clk) disable iff (!rst_n)
                      n_candidates == 4'(popcount12(candidates)));

p_converged_is_one: assert property (@(posedge clk) disable iff (!rst_n)
                      converged |-> (n_candidates == 4'd1));

p_classg_is_five:   assert property (@(posedge clk) disable iff (!rst_n)
                      class_only |-> (n_candidates == 4'd5));

p_no_regrowth:      assert property (@(posedge clk) disable iff (!rst_n)
                      (n_candidates <= $past(n_candidates)) || restart);

p_empty_is_flagged: assert property (@(posedge clk) disable iff (!rst_n)
                      (candidates == 12'd0) |-> contradiction);

Group two — evidence, and its three values. Every property here exists because EV_ABSENT is not EV_ZERO.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_absent_no_narrow: assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && obs_kind == EV_ABSENT) |=>
                        (candidates == $past(candidates)));

p_absent_no_step:   assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && obs_kind == EV_ABSENT) |=>
                        (n_observations == $past(n_observations)));

p_zero_narrows:     assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && obs_kind == EV_ZERO &&
                       (($past(candidates) & obs_consistent) != $past(candidates)))
                        |=> (n_candidates < $past(n_candidates)));

p_probe_before_use: assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_kind == EV_ZERO && !optional_proven) |-> probe_skipped);

p_probe_is_sticky:  assert property (@(posedge clk) disable iff (!rst_n)
                      proven_present[1] |=> always proven_present[1]);

p_probe_exclusive:  assert property (@(posedge clk) disable iff (!rst_n)
                      !(proven_present & proven_absent));

p_frames_out_unused:assert property (@(posedge clk) disable iff (!rst_n)
                      consistent_valid |-> (consistent == m_without_frames_out));

p_silent_is_classg: assert property (@(posedge clk) disable iff (!rst_n)
                      symptom_is_silent |-> ((consistent & CLASS_SILENT) != 12'd0));

Group three — progress.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_ranker_narrows:   assert property (@(posedge clk) disable iff (!rst_n)
                      (!no_useful_question && obs_valid &&
                       obs_counter == $past(best_counter)) |=>
                        (n_candidates < $past(n_candidates)));

p_best_split_valid: assert property (@(posedge clk) disable iff (!rst_n)
                      (best_split > 4'd0) |-> !no_useful_question);

p_no_question_stuck:assert property (@(posedge clk) disable iff (!rst_n)
                      no_useful_question |=> $stable(candidates));

p_wasted_counted:   assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && $stable(candidates) && obs_kind != EV_ABSENT)
                        |=> (wasted_steps == $past(wasted_steps) + 4'd1));

p_efficiency_range: assert property (@(posedge clk) disable iff (!rst_n)
                      efficiency_pct <= 16'd100);

p_seven_suffices:   assert property (@(posedge clk) disable iff (!rst_n)
                      ((optional_present == 5'b11111) && (n_observations >= 4'd7) &&
                       (wasted_steps == 4'd0)) |-> converged);

p_conservation_cut: assert property (@(posedge clk) disable iff (!rst_n)
                      (!identity_undecidable && identity_holds) |->
                        ((cut_mask & 12'b1101_0000_1000) == 12'd0));

p_undecidable_full: assert property (@(posedge clk) disable iff (!rst_n)
                      identity_undecidable |-> (cut_mask == ALL_SITES));

p_skew_allowance:   assert property (@(posedge clk) disable iff (!rst_n)
                      (unaccounted <= skew_bound) |-> identity_holds);

Group four — terminal states, and the ceiling.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_ceiling_static:   assert property (@(posedge clk) disable iff (!rst_n)
                      $stable(optional_present) |=> $stable(best_possible_answer));

p_site_needs_all:   assert property (@(posedge clk) disable iff (!rst_n)
                      (optional_present[4:1] != 4'b1111) |-> site_unreachable);

p_limited_not_fault:assert property (@(posedge clk) disable iff (!rst_n)
                      instrumentation_limited |-> !(method_sound == 1'b0 &&
                                                    !probe_skipped && !steps_wasted));

p_capture_late:     assert property (@(posedge clk) disable iff (!rst_n)
                      (capture_taken && capture_at_step >= 4'd5) |-> !capture_premature);

p_exhausted_needs7: assert property (@(posedge clk) disable iff (!rst_n)
                      method_exhausted |-> (n_observations >= 4'd7));

p_sound_excludes:   assert property (@(posedge clk) disable iff (!rst_n)
                      method_sound |-> (!table_wrong && !capture_premature));

p_table_wrong_stick:assert property (@(posedge clk) disable iff (!rst_n)
                      table_wrong |=> always table_wrong);

p_log_records_all:  assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && n_steps < 4'd16) |=>
                        (n_steps == $past(n_steps) + 4'd1));

p_log_elimination:  assert property (@(posedge clk) disable iff (!rst_n)
                      (obs_valid && n_steps < 4'd16) |=>
                        (eliminated_by[$past(n_steps)] ==
                         ($past(before_mask) & ~$past(after_mask))));

And eight covers, because six of these are states a reviewer wants to have seen rather than to be promised.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_reached_site:    cover property (@(posedge clk) converged);
c_reached_classg:  cover property (@(posedge clk) class_only);
c_contradiction:   cover property (@(posedge clk) contradiction);
c_absent_counter:  cover property (@(posedge clk) obs_kind == EV_ABSENT);
c_probe_absent:    cover property (@(posedge clk) proven_absent != 5'd0);
c_identity_fails:  cover property (@(posedge clk) !identity_holds && !identity_undecidable);
c_seven_steps:     cover property (@(posedge clk) n_observations == 4'd7);
c_wasted_step:     cover property (@(posedge clk) wasted_steps > 4'd0);

21. Verification Scenarios

Fifty-eight scenarios for a procedure, plus a five-run directed test whose content is an instrumentation change rather than a stimulus one.

The mask algebra — 10 scenarios.

#ScenarioExpected
1resetall 12 bits set
2c_crc_errors moved5 candidates: 0, 1, 2, 4, 5
3c_crc_errors zero7 candidates: the complement
4crc zero then align zerostill 7 — align's sites already gone
5narrowed_nothing on that second stepasserts
6crc moved then align moved3 candidates: 0, 1, 2
7then c_undersize moved1 candidate: the channel
8converged at that pointasserts
9crc moved then crc zeroempty mask; contradiction
10restart after that12 bits again; contradiction sticky

Evidence and its three values — 11 scenarios.

#ScenarioExpected
11c_filtered = 0, counter presentsite 7 eliminated
12c_filtered = 0, counter absentnothing eliminated
13the two above, same register readidentical bus traffic
141 000 misaddressed frames, c_filtered movesproven_present[1]
15the same, c_filtered stays zeroproven_absent[1]
16999 framesinconclusive — below PROBE_MIN
17a zero used before the probeprobe_skipped
18a FIFO probe from the wireimpossible — Chapter 20.5 §12
19a FIFO probe with a stalled consumerconclusive
20EV_ABSENT on every optional countern_observations stays at 5
21probe_inconclusive at the startasserts until all four are probed

The conservation cut — 9 scenarios.

#ScenarioExpected
22in − out equals the error sumidentity_holds; 8 candidates
23in − out exceeds it by 40identity fails; 4 candidates
24in − out exceeds it by 8within the 12-frame skew; holds
25c_filtered absentidentity_undecidable; 12 candidates
26the same, reported as a failurethe bug Section 7 exists to prevent
27frames_out exceeds frames_inunderflow — a counter wrapped
28a 1 Gb/s port with skew_bound = 12too generous by 63× — §8's constant
29all four optional counters presentthe identity is decidable every window
30two faults, identity checked after the fixunaccounted still non-zero

The ranker and progress — 10 scenarios.

#ScenarioExpected
3112 candidates, all countersbest is c_crc_errors, split 5
325 candidates in class G, RMON onlyno_useful_question
33the same with c_filteredbest is c_filtered, split 1
342 candidates, sites 1 and 2best is c_symbol_errors
35the same without itno_useful_question
36a question asked twicewasted_steps increments
37seven observations, full instrumentationconverged
38eight observations, still 3 candidatesmethod_exhausted
39efficiency_pct with 2 wasted of 771
40ties between two equal-split countersbroken by index, not by cost

Terminal states and the ceiling — 10 scenarios.

#ScenarioExpected
41RMON six onlybest_possible_answer = 5 at step 0
42RMON plus c_symbol_errorsstill 5 — class G untouched
43RMON plus the four non-symbol1
44site_unreachable on the RMON sixasserts before any observation
45a capture at step 1capture_premature
46a capture at step 6not premature
47an empty masktable_wrong, sticky
48instrumentation_limited and no wasted stepsmethod_sound stays high
49a converged mask after 4 observationslucky, not optimal — Chapter 20.4 §18's reading
50the evidence log after a 7-step run7 non-zero eliminated_by entries

The window — 8 scenarios.

#ScenarioExpected
511-in-10⁷ fault, 100 Gb/s, 1 s windowabout 15 CRC errors — detected
52the same at 10 Gb/sabout 1.5 — marginal
53the same at 1 Gb/s0 or 1 — usually eliminated wrongly
541 Gb/s with a 60 s windowabout 9 — detected
55a counter that samples rather than countsthe window means nothing
56a read-to-clear counter read twicethe second read is a delta of a delta
57Chapter 19.7 §7's one-bit addend, runt floodthe count is exactly half
58a count that is a clean fractionthe most identifiable counter bug there is

And the directed test, because random stimulus will not produce it.

The case: one fault, one design, five platforms — and the answer gets better without anybody touching the procedure.

No amount of stimulus reaches this. The variable is which counters the platform implements, which is a synthesis-time or a vendor decision, not a field of anything a generator can randomise. The only way to reach it is to run the same fault against five instrumentation configurations.

RunCounters presentWhat it establishes
Athe RMON sixclass G — five candidates
B+ c_symbol_errorsstill five — the wrong counter
C+ c_filteredfour
D+ c_vlan_discards, c_fifo_dropstwo
E+ c_desc_errorsone — the site

The oracle is four-part and every part is checkable without knowing the answer.

PartABCDE
candidates at termination55421
best_possible_answer at step 055421
method_soundtruetruetruetruetrue
observations made34577

Row three is the control and it is why the test is conclusive. The procedure was sound in all five runs — no wasted steps, no premature capture, no conclusion from an unproven zero. Row one moved from five to one and row three never changed, so the improvement is entirely instrumentation and none of it is method.

Row two is the test's real product. best_possible_answer predicted row one in every run, at step 0, from a list of which registers exist — before a single frame was sent. A procedure that can tell you its own ceiling before it starts is a procedure that can be planned around, and Section 15's whole second output group exists for that one row.

Run B is the instructive failure. Adding a counter improved the class count from seven to eight and improved the answer by nothing, because c_symbol_errors separates class B and the fault was in class G. More instrumentation is not better instrumentation, and the four counters that matter are named in Section 4.


22. Debugging the Debugger

Six complaints about the method itself.

Complaint 1 — "we have been measuring for an hour and the list is not getting shorter."

CheckIf yesMeaning
no_useful_question?yesthe procedure finished — Section 9
n_candidates = 5?yesclass G
are the four optional counters present?nothe answer, and it was knowable at step 0
wasted_steps climbing?yesthe same questions are being re-asked

Row three is the whole diagnosis and it needed a datasheet rather than an hour. best_possible_answer reports it before the first observation, and a team that reads it first spends the hour on the four candidates it cannot separate rather than on measurements that cannot separate them.

Complaint 2 — "we eliminated the FIFO and the fault was in the FIFO."

CheckIf yesMeaning
was c_fifo_drops read as zero?yesthe elimination came from that
was it proven present?noSection 10 — the trap
does the register exist in the datasheet?noit read zero because it is not there
probe_skipped set?yes, throughoutthe monitor said so and nobody looked

This is the most expensive error in the chapter and it is one register. An unimplemented counter reads zero exactly like an implemented one at rest, and the site it would have exonerated is the site the fault was at. Section 11's probe takes four thousand frames and settles it permanently for the platform.

Complaint 3 — "the candidate list went empty."

CheckIf yesMeaning
contradiction set?yestwo observations are inconsistent
which two?the log — Section 13before_mask per step
is one of them from an absent counter?oftenan absence treated as a zero
if notSection 2's table is wrong for this design

Row four is rare and is the one worth chasing. A genuinely inconsistent pair of readings means a counter moves for a site the table says it does not — a real fact about this MAC that nobody had written down. Fixing the table is worth more than fixing the outage, because the table is used by every future investigation.

Complaint 4 — "the fix did not work."

CheckIf yesMeaning
did the symptom change at all?nowrong site, or a class reported as a site
was the terminal state a class?yesa one-in-five guess — Section 14's row four
is unaccounted still non-zero?yesSection 17 — a second fault
did the mask converge in under 7 steps?yeslucky; check the log

Row three is the check nobody runs and it costs six register reads. Two simultaneous faults converge the mask on the earlier one, the fix is correct, the symptom persists, and the method looks discredited — when the conservation identity would have said, in one step, that loss remained.

Complaint 5 — "the counters say zero but the link is clearly broken."

CheckIf yesMeaning
what is the line rate?1 Gb/sSection 18's row nine
how long was the window?one second1.488 M frames
what fault rate would that miss?1 in 10⁷one event every 6.7 s
retry with a 60-second windowabout 9 eventsdetected

The same fault at 100 Gb/s appears fifteen times in one second and at 1 Gb/s appears once in seven. A zero is only evidence if the window was long enough, and the required window scales inversely with the line rate — which is the opposite of the instinct that slow links are easier to debug.

Complaint 6 — "the counter reads exactly half of what the capture shows."

CheckIf yesMeaning
is the traffic runts?yesChapter 19.7 §7
are they 16 to 47 octets?yestwo frames can end in one beat
is the counter's addend one bit?yesit counts one of every two
is the ratio clean?exactly 0.5the most identifiable counter bug there is

A counter wrong by a clean fraction is a design bug and not a measurement error, and it is the one case in this chapter where the instrumentation itself is the fault. Chapter 19.7 §7 derived the threshold at 48 octets — below it a 64-octet beat holds two frame endings — and a design that sized its addend for conformant traffic counts exactly half of a 47-octet flood and exactly a third of a 15-octet one.

Complaint 7 — "the same procedure gave two different answers on two ports."

CheckIf yesMeaning
same optional_present on both?nodifferent ceilings, so different answers
same line rate?noSection 18's row nine — different window sensitivity
same skew_bound?nothe conservation identity has a different tolerance
same MAC design?noSection 2's table may not apply to both

All four rows can be no on two ports of the same switch, which is the awkward part. A 1 Gb/s access port and a 100 Gb/s uplink differ in every one of them: the uplink has richer statistics, a hundred times the window sensitivity, and a twelve-frame skew where the access port has 0.19. The procedure is identical and the evidence it collects is not comparable, which is Chapter 20.6 §8's problem — a constant that is secretly the line rate — arriving in an operations team rather than in a testbench.

And the three symptoms this chapter is systematically blamed for:

SymptomBlamed onUsually is
"the method did not find it"the methodclass G — Section 4
"the counters lied"the designan absent counter read as a zero
"we fixed it and it came back"the diagnosisa second fault — Section 17

23. Misconceptions

Misconception 1 — "CRC errors mean a bad cable."

The wrong model: the check sequence protects the frame across the channel, so a failing check sequence means the channel damaged the frame.

What it costs: c_crc_errors moves for five of the twelve sites — the channel, the PHY's lane alignment, the xMII interface, the parser's offset arithmetic and the check engine's own logic. Four of those five are inside the chip. Replacing a cable is right about one time in five, and the four wrong times each consume a maintenance window and leave the fault in place.

The corrected model: a moving CRC counter is the best single question available and it is worth 0.98 bits — it narrows twelve to five. The next two register reads narrow five to one, and they cost a millisecond each. Section 6.

Misconception 2 — "a counter reading zero rules that site out."

The wrong model: the counter for site X reads zero, therefore the fault is not at site X.

What it costs: it depends on something the reading cannot tell you — whether the counter exists. An unimplemented statistics register very often reads zero rather than faulting, so "zero" and "not there" are byte-identical on the bus — and the four counters most often absent are the four that observe class G, which is the five sites the method most needs to separate.

The corrected model: a zero eliminates the counter's sites; an absence eliminates nothing. Prove the difference once per platform by making each counter move on purpose — a misaddressed frame for c_filtered, a non-member VLAN tag for c_vlan_discardsand record the result. Sections 10 and 11.

Misconception 3 — "take a capture first, then you can see what is happening."

The wrong model: the frames are the ground truth, so start by looking at them.

What it costs: three hours of setup, a span port or a tap, a storage decision, and an observation worth about one bit — because a capture at one point in a topology tells you frames were or were not there, which is frames_in and frames_out with more steps. Meanwhile five register reads would have taken twelve candidates to one or five.

The corrected model: the capture is step 6, and by then it is aimed: one interface, one condition, one oracle. Section 12's first five steps are register reads and cost milliseconds, and the whole procedure up to the capture can be run from a script.

Misconception 4 — "five candidates means the investigation failed."

The wrong model: a diagnosis that does not name a component has not finished.

What it costs: it pushes an investigator into choosing one of the five — by prior, by habit, by which team is easiest to talk to — and reporting a one-in-five guess as a conclusion. When it is wrong, and it is wrong four times in five, the method is discredited rather than the guess.

The corrected model: "the filter, VLAN, the FIFO, the DMA or the driver, and c_fifo_drops would separate them" is a finished diagnosis. It names the next action correctly — instrumentation, not investigation — and Section 15 could have predicted it at step 0.

Misconception 5 — "more counters would help."

The wrong model: the fault space is under-observed, so add observability.

What it costs: it is true and unhelpfully vague, and the obvious addition is the wrong one. Adding c_symbol_errors raises the class count from seven to eight and leaves the largest class at five, because it splits class B and class B was never the problem. Four specific counters matter, they are the four that observe non-errors, and each buys exactly one site.

The corrected model: the four are c_filtered, c_vlan_discards, c_fifo_drops and c_desc_errors, about 297 flops — 2.1% of the datapath. And the change that would matter more is different in kind: twelve boundary counters make the search a bisection and take it from seven observations to four, for 8.4%. Section 8.

Misconception 6 — "we found the fault, so the method worked."

The wrong model: a correct outcome validates the procedure.

What it costs: the two are independent. A procedure can be 40% efficient, take ten observations, and converge by luck; it can be perfectly efficient and finish with five candidates because of the platform. Judging the method by the outcome means learning nothing from either case — and the efficient-but-limited case is the one that gets a team to change the wrong thing.

The corrected model: report two numbers. efficiency_pct is about the procedure; n_candidates against best_possible_answer is about the platform. A run at 100% efficiency that ends with five candidates is a perfect investigation of an under-instrumented port, and the action it implies is a design change.


24. Interview Questions

Question 1 — "A link is dropping frames. What is the first thing you look at?"

What the answer should establish: that frames_out is the trigger and not a measurement. It falls short for every possible fault, so it eliminates nothing — zero bits. A strong answer names the real first step: the conservation identity, which balances or does not and cuts twelve candidates to eight or four for the cost of six register reads. The strongest answer asks a question first: which optional counters does this platform implement, because the answer sets the ceiling on everything that follows.

Question 2 — "CRC errors are climbing. Is the cable bad?"

What the answer should establish: that five sites move that counter — the channel, the PHY's lane alignment, the xMII, the parser's offset arithmetic and the check engine itself — and four of them are inside the chip. A strong answer gives the next step: c_alignment_errors separates the physical three from the logical two, and frames_in separates the sites before the port counter from those after. Two more register reads, and the five become one.

Question 3 — "Frames go in, fewer come out, and no error counter moves. What is wrong?"

What the answer should establish: that this is a class and not a fault — the address filter, VLAN membership, the receive FIFO, the DMA path or the host driver, five sites, all of which discard frames legally and none of which is an error. A strong answer explains why the standard is silent: RMON counts errors, and a legal discard is not one. The strongest answer names which counter separates which site and notes that all four are optional.

Question 4 — "Twelve candidates is under four bits. Why does a diagnosis take seven steps?"

What the answer should establish: that the counters do not partition the space evenly. The best available question, c_crc_errors, splits five from seven — 0.98 bits — and the four decisive counters at the end split one from eleven, which is 0.414 bits each and cannot be improved by ordering. A strong answer draws the conclusion: the tail of the search is linear, so the cost is 4 + (number of one-site indicators needed).

Question 5 — "Your counter reads zero. How do you know it works?"

What the answer should establish: make it move on purpose. A thousand frames to an address the port does not own must increment c_filtered; a tagged frame for a VLAN the port is not in must increment c_vlan_discards. If the counter stays at zero, it is not implemented — and that is now a fact rather than an assumption. A strong answer notes the exception: c_fifo_drops cannot be provoked from the wire, because no sequence of frames overflows a FIFO whose drain rate exceeds the line rate.

Question 6 — "You would add one thing to a MAC to make it debuggable. What?"

What the answer should establish: not another error counter. A plain frame counter at each of the twelve boundaries — entering the parser, the filter, the FIFO, the DMA — turns the search from a lopsided decision tree into a bisection, four observations instead of seven. A strong answer costs it: about 1 188 flops against a 14 166-flop datapath, 8.4%. The strongest answer says why it does not get built: the cost falls on the design team and the benefit falls on whoever is paged at three in the morning.


25. Questions and Answers


26. What's Next

This chapter built the method. The rest of Module 21 is what the method is applied to.

ChapterSupplies
Chapter 21.2the symptom catalogue — what each error class physically means
Chapter 21.3the first class, and the channel-versus-logic separation
Chapter 21.4a link that never reaches a usable state — a different fault space
Chapter 21.9step 6, and what a capture cannot show

Chapter 21.2 fills in Section 2's table from the other direction. This chapter took the counters as given and asked what they separate; the taxonomy asks what each error class physically isChapter 7.3's five widened to symbol errors, drops, overflows and descriptor faults. The two together are the method: a table of sites, a table of symptoms, and an intersection.

Chapter 21.3 is the first entry and it is the one this chapter kept returning to. c_crc_errors is the best single question in the method — 0.98 bits, five sites of twelve — and four of those five are inside the chip. Separating the channel from the logic is Chapter 19.4 §14's equivalence checker on one side and a marginal connector on the other, and the distinction is worth a chapter because the maintenance action is completely different.

And the series is now ninety-four classes long. Chapter 20.2 §8's six-group taxonomy has already needed extending twice — Chapter 20.4's class 91 opened a seventh group and Chapter 20.6's class 93 fits none of them. Class 94 is a third kind again: a property about a search whose subject is really the sensors. What the last three have in common is that the property is correct about the thing it names and wrong about the thing it constrains — which may be the eighth group, and Chapter 21.2 is where a taxonomy of errors gets argued about properly.


Continue learning

Standards & specifications

Governing standard
IEEE Std 802.3 (Ethernet)(opens IEEE in a new tab)

Defines the Ethernet MAC, the media-independent interfaces and the physical-layer sublayers, including framing, access control, auto-negotiation and per-rate PHY specifications. VLAN tagging, priority and time-sensitive shaping are defined by IEEE 802.1, not by 802.3.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the Ethernet curriculum.