Skip to content
VLSI Mentor

Ethernet · Module 21

The Ethernet Error Taxonomy

Seven of the twelve reachable frame shapes belong to more than one error class, so exclusivity comes from a priority rule — and the two obvious rules disagree on exactly half the space.

An error class is a name for a set of frames. The useful question about a taxonomy is whether those sets overlap — and in Ethernet they do, on seven of the twelve reachable frame shapes.

Count
reachable frame shapes12
shapes satisfying two or more error predicates7 — 58.3%
shapes with no name in the standard1
shapes where the two obvious classification orders disagree6 — exactly half
error classes that are frame properties6
error classes that are not6

Row four is the chapter's finding. The standard classifies size first and error second; the implementation everybody writes classifies error first and size second. Both are reasonable, both are self-consistent, and they disagree on half the reachable shapes — every one of which is a runt or a giant that also carries an error.

Which is precisely the traffic Chapter 20.5 §6 and Chapter 19.7 §7 spend their whole arithmetic on.

Row three is the smaller and stranger result. A frame that is not a whole number of octets and whose check sequence nonetheless passes is reachable, is not a good frame, and has no class in RMON at all — it is not an alignment error, because that requires the check to fail.


1. Scope, and Twelve Classes in Two Halves

Scope: every error class an Ethernet receive path can report, what each one physically is, and what decides which class a frame that qualifies for several ends up in.

Not in scope: localising a fault. Chapter 21.1 is the method and this chapter is the table it reads. Nor the individual diagnosesChapter 21.3 takes the first class and separates its causes.

The twelve classes, in two halves, and the halves are not symmetric.

ClassIs it a property of the frame?Required by RMON?
1FCS erroryesyes
2alignment erroryesyes
3undersizeyesyes
4fragmentyesyes
5oversizeyesyes
6jabberyesyes
7symbol errorno — a PHY eventno
8filteredno — a table lookupno
9VLAN discardno — port configurationno
10buffer dropno — a system stateno
11mid-frame truncationno — a system stateno
12descriptor errorno — a host stateno

Six and six, and the line between them is exactly the line the RMON standard draws. Classes 1 to 6 are predicates over the octets of a frame — a receiver holding a frame can evaluate every one of them with no other information. Classes 7 to 12 need something else: a PHY's internal state, a filter table, a port's VLAN configuration, a buffer's occupancy, a descriptor ring.

And that is why Chapter 21.1 §4's class G holds five members. The five sites it could not separate — the filter, VLAN membership, the FIFO, the DMA and the driver — are exactly the sites whose error classes are numbers 8 to 12 of this table, and none of those has a required counter, because none of them is a property of a frame.

Chapter 21.1's viewThis chapter's view
class G exists becausefive sites share a counter signaturefive classes are not frame properties
the fix isfour optional countersthe same four, named as classes

Two chapters, one finding, arrived at from opposite directions — and the agreement is the evidence that the taxonomy is drawn in the right place.


2. The Predicates, and Why They Are Not a Partition

Three predicates decide the six frame error classes. The first is whether the frame is a whole number of octets. The second is whether the frame check sequence matches. The third is the wire length, which takes one of three values: in range, below sixty four octets, or above the maximum transmission unit. Two times two times three is twelve reachable combinations, and every one of them can be produced on a wire by the error injector's five actions. Of those twelve, exactly one satisfies no predicate and is a good frame, four satisfy exactly one predicate, and seven satisfy two or more. Seven of twelve is fifty eight point three per cent, so more than half of all reachable frames qualify for several error classes at once. The frame validity chapter resolves this in four words, saying that a frame is counted once, and it never states which class wins. The exclusivity of the error counters is therefore manufactured by a priority rule rather than found in the frame, and the rule has to be reconstructed from the way the remote monitoring standard words its definitions of fragments and jabbers.A: not wholeoctets2 valuesB: check fails2 valuesS: the lengthin range, short, long12 reachableshapesall producible5 in one classor none at all7 in several58.3%A priority rulenot in the standardA partition,afternot before12
Figure 1 — three predicates, twelve reachable shapes, and seven of them in more than one class.

The first six classes are defined by three predicates over a received frame.

PredicateDefinitionFrom
Athe frame is not a whole number of octetsChapter 7.3 §3
Bthe frame check sequence does not matchChapter 7.3 §3
Sthe wire length: in range, below 64, or above the MTUChapter 5.1

S has three values and the other two have two, so there are twelve reachable combinations — and "reachable" is the right word: every one of the twelve can be produced on a wire. Chapter 20.5 §2's five injection actions reach all of them.

Now the observation the whole chapter rests on.

ShapesShare
total reachable12100%
satisfying exactly one predicate433.3%
satisfying two or more758.3%
satisfying none — a good frame18.3%

Seven of twelve reachable frames qualify for more than one error class at once. A 40-octet frame with a bad check sequence is short and failing; a 40-octet frame that is also not whole octets is short, failing and misaligned. The classes overlap on more than half the space.

Chapter 7.3 §3 resolves this in one sentence — a frame is counted once — and the sentence does not say which one.

The standard providesWhat it does not provide
the predicatesprecise, normative
the exclusivity"counted once"
the tie-breakimplied by the class definitionsnever stated as a rule

So the taxonomy is a partition only after a priority rule is applied, and the priority rule has to be reconstructed from how RMON words the definitions of fragments and jabbers. Section 4 reconstructs it, Section 6 argues it is the right one, and Section 7 implements the other one to show what it costs.

The error classes are not a partition of the frame space. They are a partition of the frame space quotiented by a priority rule — and the rule is the part nobody writes down.


3. RTL 1 — The Taxonomy Package and the Predicate Extractor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// taxon_pkg -- the twelve classes, the three predicates, and the two
// priority orders.
//
// The package's whole design argument is that the PREDICATES and the
// CLASSES are different types. A frame has predicates; a counter has a
// class; and the function between them is a priority rule that the
// standard implies and never states.
// ---------------------------------------------------------------------
package taxon_pkg;

  // Section 1's twelve. The first six are frame properties; the last
  // six need state the frame does not carry.
  typedef enum logic [3:0] {
    CLS_GOOD      = 4'd0,
    CLS_FCS       = 4'd1,
    CLS_ALIGN     = 4'd2,
    CLS_UNDERSIZE = 4'd3,
    CLS_FRAGMENT  = 4'd4,
    CLS_OVERSIZE  = 4'd5,
    CLS_JABBER    = 4'd6,
    CLS_SYMBOL    = 4'd7,
    CLS_FILTERED  = 4'd8,
    CLS_VLAN      = 4'd9,
    CLS_DROP      = 4'd10,
    CLS_TRUNCATED = 4'd11,
    CLS_DESCRIPTOR= 4'd12,
    CLS_UNNAMED   = 4'd15   // Section 8 -- reachable and nameless
  } err_class_e;

  typedef enum logic [1:0] {
    SZ_IN_RANGE = 2'd0,
    SZ_SHORT    = 2'd1,     // below 64 octets
    SZ_LONG     = 2'd2      // above the MTU
  } size_class_e;

  // A frame's three predicates. Nothing here is a class.
  typedef struct packed {
    logic        not_whole_octets;   // A
    logic        fcs_mismatch;       // B
    size_class_e size;               // S
  } predicates_t;

  // Section 4: the two orders. The standard's is size-major.
  typedef enum logic {
    ORDER_SIZE_MAJOR  = 1'b0,
    ORDER_ERROR_MAJOR = 1'b1
  } priority_order_e;

  // Section 10: which classes a frame's own octets can decide.
  localparam logic [12:0] FRAME_DECIDABLE = 13'b0000000_1111111;

  function automatic logic is_frame_property(err_class_e c);
    return (c <= CLS_JABBER);
  endfunction

  // Section 2: how many of A, B and S-not-in-range a frame satisfies.
  function automatic int n_predicates(predicates_t p);
    return int'(p.not_whole_octets) + int'(p.fcs_mismatch)
         + int'(p.size != SZ_IN_RANGE);
  endfunction

endpackage

Classification: a package whose central claim is a type distinction — predicates_t is not err_class_e.

What it teaches: that a frame does not have an error class; it has predicates, and a class is assigned. Writing those as one type — an enum a comparator drives directly — makes the priority rule invisible and unarguable, which is how two implementations end up disagreeing about a runt with a bad check sequence without either one containing a line anybody would call wrong.

And it teaches that CLS_UNNAMED has to exist. Section 8's shape — not whole octets, check sequence passes, length in range — is reachable and has no class in RMON. A classifier whose enum has no slot for it will put it somewhere, and wherever it puts it will be wrong.

Deliberately simplified: size_class_e has three values where Chapter 19.7 §2's histogram has seven, so this package cannot express "oversize but within the jumbo limit." The MTU is not a parameter here. FRAME_DECIDABLE is a literal where it should follow from is_frame_property. And there is no representation of a frame that is simultaneously filtered and malformed, which is real and which Section 11 argues is a different kind of overlap.

Production implication: the enum's numbering is load-bearing. is_frame_property is a single comparison because classes 0 to 6 are contiguous, and that one comparison is what Chapter 21.1 §4's class G reduces to. An enum reordered for readability turns a comparator into a lookup table — and more importantly turns a fact about the taxonomy into a coincidence about the encoding.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// predicate_extractor -- evaluate A, B and S over a received frame.
// It assigns NO class. That separation is the point.
// ---------------------------------------------------------------------
module predicate_extractor
  import taxon_pkg::*;
#(
  parameter int MTU_OCTETS = 1518
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_end,
  input  logic [13:0] wire_len,        // octets, including the FCS
  input  logic [2:0]  trailing_bits,   // 0 to 7 -- Chapter 7.3 Section 7
  input  logic        fcs_ok,

  output predicates_t pred,
  output logic        pred_valid,
  output logic [3:0]  n_pred,

  output logic [31:0] c_frames,
  output logic [31:0] c_multi_predicate
);
  always_comb begin
    pred.not_whole_octets = (trailing_bits != 3'd0);
    pred.fcs_mismatch     = !fcs_ok;
    pred.size             = (wire_len < 14'd64)            ? SZ_SHORT
                          : (wire_len > 14'(MTU_OCTETS))   ? SZ_LONG
                                                           : SZ_IN_RANGE;
    n_pred     = 4'(n_predicates(pred));
    pred_valid = frame_end;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_frames <= '0; c_multi_predicate <= '0;
    end else if (frame_end) begin
      c_frames <= c_frames + 32'd1;
      // Section 2: 58.3% of reachable shapes land here, and every one
      // of them needs a priority rule to become a class.
      if (n_pred > 4'd1) c_multi_predicate <= c_multi_predicate + 32'd1;
    end
  end
endmodule

Classification: a pure predicate evaluator, and the only block in the chapter that touches a frame.

What it teaches: that trailing_bits is where the alignment predicate actually comes from, and that it is an interface signal rather than a computation. Chapter 7.3 §7's detector carries a bit count through the receive pipeline — Chapter 20.5 §19 costed it at 48 shipped flops — and a MAC without it cannot evaluate predicate A at all, so two of the six frame classes are unreachable for it.

And it teaches that c_multi_predicate is the number that tells a team whether the priority rule matters on their traffic. On conformant traffic it is zero and the two orders of Section 4 agree everywhere. On the error-injected traffic of Chapter 20.5 it is the majority of frames, and there the two orders disagree on half.

Deliberately simplified: MTU_OCTETS is a parameter with no VLAN awareness, so a double-tagged frame at 1 526 octets is classified SZ_LONG on a port that should permit itChapter 13.2's problem appearing as a misclassification. wire_len includes the check sequence, which is the right convention for RMON and the opposite of Chapter 19.4's pre-FCS length. And fcs_ok is taken as given where Section 17 argues that its own correctness is one of the twelve classes.

Production implication: the block deliberately has no err_class_e port, and a reviewer should check that before anything else. A predicate extractor that emits a class has made the priority decision inside a block whose name says it does not — and that is exactly where the disagreement in Section 4 hides in real designs: not in a classifier anybody reviews, but in a comparator chain somebody wrote in the order the conditions occurred to them.


4. Twelve Reachable Frame Shapes, and Six Disagreements

Enumerate all twelve and classify each one twice: the standard's way and the obvious way.

ABSizeRMON — size-majorError-majorAgree?
00in rangegoodgoodyes
00shortundersizeundersizeyes
00longoversizeoversizeyes
01in rangeFCS errorFCS erroryes
01shortfragmentFCS errorNO
01longjabberFCS errorNO
10in rangeno nameno nameyes
10shortfragmentundersizeNO
10longjabberoversizeNO
11in rangealignmentalignmentyes
11shortfragmentalignmentNO
11longjabberalignmentNO

Six of twelve — exactly half — and the six have a shape.

Rows that disagree
all six areshort or long
all six carryat least one error predicate
none of the six isa frame that is only one thing

The two orders agree on every frame that qualifies for exactly one class and disagree on every frame that is out of range and broken. Which is a clean statement and is worth reading twice: the disagreement is not at the margins, it is the whole overlap.

RMON's rule, reconstructed from the wording of its own definitions:

fragments are packets shorter than 64 octets with a bad check sequence or a bad alignment. jabbers are packets longer than the maximum with a bad check sequence or a bad alignment.

That "or" is the priority rule. It folds rows 5, 8 and 11 into fragments and rows 6, 9 and 12 into jabbers, because the size test is applied first and the error type only decides whether the frame is undersize or fragment. An implementation that tests A && B before testing the size reaches alignment first and never gets to the size test, which is rows 11 and 12.

And the consequence, in the counters a debugger reads:

TrafficSize-major reportsError-major reports
1 000 runts with bad FCSfragments = 1 000fcs_errors = 1 000
1 000 dribbled runtsfragments = 1 000alignment = 1 000
1 000 dribbled giantsjabbers = 1 000alignment = 1 000

Row two is the one that matters for Chapter 21.1's method. The same physical fault moves a different counter on two conformant-looking implementations — and Chapter 21.1 §2's site table is keyed on which counter moves. A method built on one order gives a wrong answer on hardware built to the other, silently, and the only evidence is that the fragment counter is zero when it should not be.

And it is worth saying exactly how the method goes wrong, because the failure is specific rather than general.

Chapter 21.1 §2's table says c_crc_errors moves for five sites: the channel, the PHY lanes, the xMII, the parser and the check engine. That row is written for a size-major classifier. On an error-major one, every short or long broken frame also moves c_crc_errors or c_alignment_errors — so the counter's site set silently widens.

CounterSites, size-majorSites, error-major
c_crc_errors55, and it also absorbs the fragments
c_alignment_errors33, and it also absorbs dribbled runts
c_fragments1, the medium0 — the counter is dead
c_jabbers1, the far end0 — the counter is dead

Two of the twelve counters become permanently zero, and a permanently zero counter is read as evidence. Chapter 21.1 §10 showed that a zero eliminates the sites its counter moves for — so on an error-major chip, c_fragments at zero "eliminates" the cut-cable site on every single investigation, forever, and the site is never a candidate.

The cost, stated exactly
what the method believesthe medium is not cutting frames
whyc_fragments is zero
why it is zerothe classifier can never assign it
how often this is checkednever, on any platform

That is the chapter's most consequential finding and it is a two-register test to rule out — Section 16's fragments_never_move, or one injected frame.


5. RTL 2 — The Size-Major Classifier

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// size_major_classifier -- the standard's order, written so the
// priority is visible as a structure rather than as the order of some
// if-else arms.
//
// Size decides the FAMILY; the error predicates decide WHICH member of
// that family. Writing it as two steps is the whole difference from
// Section 7's version.
// ---------------------------------------------------------------------
module size_major_classifier
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        pred_valid,
  input  predicates_t pred,

  output err_class_e  cls,
  output logic        cls_valid,
  output logic        priority_applied,   // more than one class qualified

  output logic [31:0] c_class [13]
);
  logic broken;

  // STEP ONE: is the frame broken at all? Both error predicates fold
  // into one bit here, which is exactly what makes this size-major.
  assign broken = pred.not_whole_octets || pred.fcs_mismatch;

  // STEP TWO: the size chooses the family.
  always_comb begin
    case (pred.size)
      SZ_SHORT: cls = broken ? CLS_FRAGMENT  : CLS_UNDERSIZE;
      SZ_LONG:  cls = broken ? CLS_JABBER    : CLS_OVERSIZE;
      default:  cls = pred.fcs_mismatch
                    ? (pred.not_whole_octets ? CLS_ALIGN : CLS_FCS)
                    : (pred.not_whole_octets ? CLS_UNNAMED : CLS_GOOD);
    endcase
    // Section 2: the frame qualified for more than one class and a
    // rule chose. Reporting THAT is what makes the rule auditable.
    priority_applied = (n_predicates(pred) > 1);
  end

  assign cls_valid = pred_valid;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) for (int i = 0; i < 13; i++) c_class[i] <= '0;
    else if (pred_valid && cls != CLS_UNNAMED)
      c_class[cls] <= c_class[cls] + 32'd1;
  end
endmodule

Classification: a two-step classifier whose first step is a disjunction, and the disjunction is the standard's rule.

What it teaches: that broken is where the priority lives. Folding A and B into one bit before looking at the size is size-major classification — it discards the distinction between an alignment error and a check-sequence error precisely for the frames whose size already put them out of range. RMON's "bad FCS or bad alignment" in the definitions of fragments and jabbers is that ||, and it is the only place the standard's priority is expressible as one operator.

And it teaches that priority_applied belongs in the design rather than in a comment. A frame that qualified for one class needed no rule; a frame that qualified for three had a rule applied to it, and a counter of how often that happened is the difference between a classifier whose behaviour can be audited and one whose behaviour must be inferred from its counters.

Deliberately simplified: c_class skips CLS_UNNAMED entirely, so Section 8's shape is silently uncounted — which is what real hardware does and is the reason nobody knows how often it occurs. The default arm handles SZ_IN_RANGE and any illegal encoding identically. And the counters are a flat array of 13 × 32 bits with no shadow bank, so Chapter 19.7 §15's common reading instant is unavailable.

Production implication: priority_applied against c_frames is the number that decides whether an implementation's classification order is observable on a given link. Zero means the traffic never produced a multi-predicate frame and the two orders of Section 4 are indistinguishable here. Anything else means the link is exercising the rule, and the counters a debugger reads depend on which rule this chip implements — which is not in any datasheet.


6. Why the Standard Classifies Size First

Take a forty octet frame whose check sequence has been stomped. Under the standard's size major rule the classifier first asks whether the frame is broken at all, folding the alignment and check sequence predicates into one bit, and then lets the size choose the family. Forty octets is below the minimum, the frame is broken, so the class is fragment, and a fragment tells the operator that a frame was cut: a collision remnant in a shared medium deployment, a parting cable, or a truncation. Under the error major rule the classifier tests the most specific error first. The check sequence failed, so the class is a check sequence error, and that tells the operator to look for a marginal channel. Both classifiers implement every normative definition faithfully and they send the operator to two different places. The standard's order is the right one because bit damage changes octets and does not change a length: a marginal channel corrupts frames but does not shorten them to forty octets, since the length is decided by the transmitter and the framing rather than by the damage.40 octets, FCSstompedone frameSize-majorbroken? then whichsizeError-majorwhich error? then sizefragmentthe frame was cutfcs_errorthe channel damaged itLook for a cutcable, partner,truncationLook for achannelwhich is not thereDamage changesoctetsnot lengths12
Figure 2 — the same frame, two orders, two different places to send the operator.

The size-major rule looks arbitrary until you ask what each class is for. Then it is the only sensible order.

ClassThe operator is being toldThe action
FCS errorsomething is corrupting frameslook at the channel and the logic
alignment errorsomething is corrupting frames at bit granularitylook at the PHY and the interface
undersizea partner is emitting illegal frameslook at the far end's transmitter
fragmenta collision or a truncation happenedlook for a collision domain or a cut
oversizean MTU mismatchlook at configuration
jabbera partner is stuck transmittingisolate the port

The six actions divide into two groups and the division is by size, not by error. fragment and jabber are operational categories — they mean a device is misbehaving or the medium was cutand they are actionable without knowing whether the check sequence or the alignment failed. FCS error and alignment error are diagnostic categories about a link that is otherwise working.

So the rule is: if the frame's size already says the link is broken, the error type adds nothing.

Size-majorError-major
a 40-octet frame with a bad FCSfragment — a cut or a collisionfcs_error — a marginal channel
the operator's actionlook for a physical breaklook for a marginal channel
which is right?a 40-octet frame is not a marginal channel symptom

Row three is the argument. A marginal channel corrupts frames; it does not shorten them to 40 octets, because the length is determined by the transmitter and by the framing, not by the bits the channel damaged. A 40-octet frame with a bad check sequence is a frame that was cut — a collision in a shared-medium deployment, a truncation, a partner that stopped transmitting. Reporting it as an FCS error sends the operator to the wrong place, and that is the whole reason the standard folds it into fragments.

And the same argument, in the other direction, explains the one shape nobody named.

Reasoning
a short frame with any error is a fragmentthe size is the diagnosis
an in-range frame with both errors is an alignment errorthe finer error is the diagnosis
an in-range frame with A and no Bneither rule applies — Section 8

The taxonomy is coherent on eleven of twelve shapes and silent on the twelfth, which is a better record than most standards manage and is still one shape more than a classifier can leave undefined.

And the argument generalises into a rule worth carrying to any taxonomy, not just this one.

When two classes overlap, the one whose action is coarser should win — because a coarse action is correct whenever a fine one would have been, and the reverse is not true.

CoarseFine
fragmentlook for a physical break
fcs_errorlook for a marginal channel
if the frame really was cutcorrectwrong
if the channel really was marginalfinds it eventuallycorrect

Row three and row four are asymmetric and that asymmetry is the whole argument. An operator sent to look for a physical break on a marginally-noisy link will find the noise while looking; an operator sent to look for a marginal channel on a cut cable will measure a channel that is not the problem. Classifying to the coarser action is the error-tolerant choice, and the standard made it.


7. RTL 3 — The Error-Major Classifier, For Contrast

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// error_major_classifier -- the version everybody writes, shown here
// to make the disagreement concrete rather than hypothetical.
//
// Nothing in this block is careless. Every arm is correct in
// isolation. The ORDER is the whole defect, and the order is the one
// the conditions occur to you in.
// ---------------------------------------------------------------------
module error_major_classifier
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        pred_valid,
  input  predicates_t pred,

  output err_class_e  cls,
  output logic        cls_valid
);
  // The natural order: check the most specific error first, then the
  // less specific one, then the size. Every one of these tests is
  // exactly Chapter 7.3's definition. The result is wrong on six of
  // the twelve shapes.
  always_comb begin
    if      (pred.not_whole_octets && pred.fcs_mismatch) cls = CLS_ALIGN;
    else if (pred.fcs_mismatch)                          cls = CLS_FCS;
    else if (pred.size == SZ_SHORT)                      cls = CLS_UNDERSIZE;
    else if (pred.size == SZ_LONG)                       cls = CLS_OVERSIZE;
    else if (pred.not_whole_octets)                      cls = CLS_UNNAMED;
    else                                                 cls = CLS_GOOD;
  end

  assign cls_valid = pred_valid;
endmodule

Classification: a correct-looking priority chain, included as a control.

What it teaches: that the defect is not in any line. Every arm implements a normative definition faithfully: an alignment error is a frame that is not whole octets and whose check fails; an FCS error is a frame whose check fails. The block is six correct statements in the wrong order, and no line-level review finds it — which is why Section 13's comparator exists and Section 20's property does not.

And it teaches how the order arises. The conditions are written in decreasing specificity, which is the standard advice for a priority chain and produces error-major classification every time. Size is the least specific test, so it goes last; and going last is exactly what makes fragments and jabbers unreachable.

Deliberately simplified: the block is the contrast, so it has no counters and no priority_applied output. CLS_FRAGMENT and CLS_JABBER are never assigned by it at all, which is the finding stated as an unreachable enum value — a linting tool would flag it, and in a real design the two values are reached by a different code path and the unreachability is hidden.

Production implication: the tell is a counter that is permanently zero. A MAC whose fragments counter has never moved in a year of operation is either on a perfect link or is classifying error-major — and the second is far more likely, because Chapter 20.5 §6 showed runts are 0.08% of the coverage cross and are also the commonest symptom of a genuinely broken partner. The check is one injected frame: send a 40-octet frame with a stomped check sequence and see which counter moves.


8. The One Shape With No Name

A receiver's trailing bit count tells the classifier whether a frame ended on an octet boundary. Suppose that register is stuck at a non zero value. Every frame then satisfies the not whole octets predicate. But the frames are fine, so their check sequences pass, and their lengths are in range. That is the one reachable shape the standard does not name: an alignment error is defined as a frame that is both not an integral number of octets and failing its check sequence, so a frame satisfying only the first is not one, and no other class fits. The classifier has nowhere to put it and real hardware counts it nowhere. The operator therefore sees frames arriving normally at the port counter, every single error counter reading zero, and frames not reaching the host. The debugging method reads that evidence correctly and converges on the class of five sites past the media access controller: the address filter, virtual network membership, the receive buffer, the direct memory access path and the host driver. The actual fault is in the parser, which is not one of those five. One counter for the unnamed shape, about ninety nine flip flops, turns the whole thing into a single register read.Bit count stucksite 5, the parserA true, B falselength in rangeNo class fitsalignment needs BNothing countedevery counter zeroframes_in healthyframes_out short21.1 says class Gthe five past the MACc_unclassified99 flops, one read12
Figure 3 — a stuck bit count, every counter at zero, and a method that converges on the wrong five.

Row seven of Section 4's table: not a whole number of octets, check sequence passes, length in range. It is reachable and RMON has no class for it.

Value
predicate Atrue — trailing bits are non-zero
predicate Bfalse — the check sequence matched
sizein range
RMON classnone
802.3 classnone — alignment error requires B

The definition is explicit and the exclusion is deliberate. Chapter 7.3 §3 defines an alignment error as a frame that is both not an integral number of octets and failing its check sequence. A frame satisfying only the first is not an alignment error by the letter of the definition — and there is no other class it could be.

How does it arise? Three ways, and two of them are real.

CauseReal?Rate
chance — the check passes over the octet prefixyes, and rareabout 1 in 4.29 billion
a transmitter that appends a valid FCS to a misaligned frameyeswhatever the fault's rate is
a receiver whose bit count is wrongyesa design fault

Row one is the birthday-free version and the number is exact. Chapter 19.4's check value is 32 bits, so a randomly damaged frame passes its check with probability 2^-32 — one in 4 294 967 296. At 100 Gb/s and minimum size, 148.9 million frames per second, a link running entirely corrupted traffic produces one every 28.8 seconds. On realistic error rates it is a once-a-year event and it is not zero.

Row three is the one that matters, because it is a design fault that presents as nothing. A receiver whose trailing-bit count is stuck non-zero reports predicate A on every frame. With the check sequence passing — because the frames are fine — every frame lands in the unnamed shape. Size-major classification sends them to CLS_UNNAMED and the counter array does not count it; error-major sends them to CLS_UNNAMED too. So:

What the operator sees
frames_inmoving normally
every error counterzero
frames_outshort by every frame
Chapter 21.1's maskclass G — five candidates

A stuck bit-count register in the parser presents as Chapter 21.1 §4's class G, which is the five sites past the MAC — and the fault is at site 5, which class G does not contain. The method converges on a set that does not include the answer, and the reason is one unnamed shape in a taxonomy.

An unclassifiable frame is not counted, and an uncounted frame is invisible to every method built on counters. The one shape the taxonomy does not name is the one shape a debugger cannot see.

The fix is a counter, and it costs about 99 flopsChapter 19.7 §19's per-counter figure. c_unclassified is not in any standard, it would have caught this fault in one register read, and it is the thirteenth entry Section 12's table argues for.


9. RTL 4 — The Symbol-Error Bridge

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// symbol_error_bridge -- class 7, and the only class in the taxonomy
// that is reported by a different chip.
//
// A symbol error is a PCS event: a code group the line coding cannot
// decode. It has no frame, no length and no check sequence, and it
// reaches the MAC as a control character in the middle of a frame or
// not at all. The bridge's job is to relate it to a frame WITHOUT
// pretending it is a property of one.
// ---------------------------------------------------------------------
module symbol_error_bridge
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  // From the PHY, over the xMII control encoding.
  input  logic        rx_error,          // Chapter 4.2's RX_ER
  input  logic        in_frame,

  // From the PHY's own registers, over MDIO -- Chapter 4.5.
  input  logic        mdio_valid,
  input  logic [15:0] phy_symbol_errors,

  output logic [31:0] c_symbol_in_frame,
  output logic [31:0] c_symbol_in_gap,
  output logic [15:0] phy_count_shadow,
  output logic        counts_disagree,
  output logic        phy_unreadable
);
  logic [15:0] last_phy;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_symbol_in_frame <= '0; c_symbol_in_gap <= '0;
      last_phy <= '0; phy_count_shadow <= '0;
      counts_disagree <= 1'b0; phy_unreadable <= 1'b1;
    end else begin
      // A symbol error INSIDE a frame corrupts it and will also show
      // up as an FCS or alignment error. One physical event, two
      // classes -- Section 10's overlap, across a chip boundary.
      if (rx_error &&  in_frame) c_symbol_in_frame <= c_symbol_in_frame + 32'd1;
      // A symbol error in the gap corrupts nothing and is invisible
      // to every frame-based class. It is still a failing channel.
      if (rx_error && !in_frame) c_symbol_in_gap   <= c_symbol_in_gap + 32'd1;

      if (mdio_valid) begin
        phy_unreadable   <= 1'b0;
        phy_count_shadow <= phy_symbol_errors;
        last_phy         <= phy_symbol_errors;
        // The MAC sees RX_ER; the PHY counts internally. They should
        // agree, and a disagreement is an xMII problem rather than a
        // channel one -- Chapter 21.1's sites 2 and 3, separated.
        if ((phy_symbol_errors - last_phy) !=
            16'(c_symbol_in_frame + c_symbol_in_gap))
          counts_disagree <= 1'b1;
      end
    end
  end
endmodule

Classification: a cross-chip reconciliation, and the only block here whose two inputs come from different silicon.

What it teaches: that a symbol error is not a frame class and relating it to one is the bridge's entire difficulty. A corrupted code group inside a frame will also fail the check sequence, so one physical event produces a symbol error and an FCS error — the same frame counted in two places, which is not double counting because the two counters are in two chips.

And it teaches why c_symbol_in_gap is the more interesting of the two counters. A symbol error between frames corrupts nothing — there is no frame to damage — so it is invisible to every one of the six frame classes. A channel degrading steadily produces gap symbol errors long before it produces frame errors, which makes this the earliest available warning in the whole taxonomy and the one no RMON counter carries.

Deliberately simplified: counts_disagree subtracts 16-bit values with no wrap handling, so a PHY counter that wraps between MDIO reads produces a false disagreement. The comparison assumes the two counts cover the same window, which MDIO's millisecond-scale access makes untrue. And phy_unreadable starts asserted and clears on the first valid read, so a PHY that is never polled reports as unreadable forever — which is correct and is the common configuration.

Production implication: counts_disagree is the one observation that separates Chapter 21.1 §4's class B. The PHY says it saw N bad code groups; the MAC says it saw M assertions of RX_ER. Agreement means the channel is damaging symbols and the interface is faithfully reporting them — site 1 or 2. Disagreement means the interface between them is losing or inventing eventssite 3. Two sites, one comparison, and it needs an MDIO transaction that most monitoring systems do not make.


10. The Six Classes That Are Not Frame Properties

Classes 7 to 12 need information the frame does not carry, and the six are not alike. Three are about the frame's destination, two about the system's state, and one about a different chip.

ClassNeedsDecided byIs the frame damaged?
symbol errorthe PHY's decoder stateanother chipmaybe — Section 9
filteredthe address tableconfigurationno
VLAN discardport membershipconfigurationno
buffer dropFIFO occupancythe system's timingno
mid-frame truncationFIFO occupancy mid-framethe system's timingyes — by the receiver
descriptor errorthe host's ring statesoftwareno

Row five is the odd one and it is worth naming why. A mid-frame truncation is the only class in the whole taxonomy where the receiver damages the frame. Chapter 19.5 §14's overflow guard cuts a frame that was perfect on the wire — so downstream of the FIFO it looks exactly like a fragment, and the distinction between "the partner sent a broken frame" and "we broke a good frame" is a counter in the MAC and nothing else.

A fragmentA truncation
damaged bythe far end or the mediumthis receiver
looks like, downstreamshort, bad checkshort, bad check
the actioninvestigate the partner or the cableinvestigate the memory system
the distinguishing evidencec_fifo_drops — optional

Four of the six do not damage anything, which is Chapter 21.1 §4's finding in this chapter's vocabulary: they are legal discards, no standard requires a counter for them, and the five sites that produce them are indistinguishable without one.

And the six split differently again on a question a debugger actually asks — can I reproduce it?

ClassReproducible from the wire?
symbol erroronly with a channel impairment
filteredyes — one wrong destination address
VLAN discardyes — one non-member tag
buffer dropNO — Chapter 20.5 §12
mid-frame truncationNO — the same proof
descriptor errorNO — a host action

And one more split, because it decides whether a class can appear in a switch's statistics at all.

Four of the six are per-port facts and two are per-system.

ClassScopeConsequence
symbol errorper port, per lanea lane-level counter is finer than the class
filteredper portcountable per port
VLAN discardper port, per VLANa per-port counter aggregates the VLANs away
buffer dropper port on an input-buffered switch; per system on a shared-memory onethe counter may not be attributable
truncationthe samethe same
descriptor errorper queue, per hostnot a port property at all

Rows four and five are why buffer-drop counters are so often absent or aggregated. On a shared-memory switch the buffer that overflowed does not belong to a port; it belongs to the fabric, and attributing the drop to the ingress port is a design choice rather than a fact. A counter that cannot be attributed is a counter nobody builds — which is the mechanism behind Chapter 21.1 §4's class G being unresolvable on exactly the hardware where frames most often go missing.

Three of the six cannot be produced from the wire at any rate, in any pattern, with any frame sizes. Chapter 20.5 §12 proved it for the buffer: the arrival rate is the line rate and Chapter 19.5 §4's drain rate exceeds it, so the FIFO empties whatever arrives. The consumer has to stop — which is a memory-system action — and that is why Chapter 21.1 §11's probe cannot prove those counters exist.


11. RTL 5 — The Non-Error Discriminator

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// nonerror_discriminator -- classes 8 to 12, which a frame cannot
// decide about itself.
//
// The block exists to make one distinction loud: a frame that was
// DISCARDED and a frame that was DAMAGED are different events, and
// only one of them is an error. Conflating them is how a filtered
// frame ends up in a drop counter and a real fault stays hidden.
// ---------------------------------------------------------------------
module nonerror_discriminator
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_end,
  input  predicates_t pred,

  // The five pieces of state a frame does not carry.
  input  logic        addr_miss,        // Chapter 7.4's filter
  input  logic        vlan_nonmember,   // Chapter 13.2's port mode
  input  logic        fifo_full,        // Chapter 19.5 Section 14
  input  logic        truncated,        // the same, mid-frame
  input  logic        no_descriptor,    // Chapter 19.6

  output err_class_e  cls,
  output logic        cls_valid,
  output logic        is_legal_discard,
  output logic        damaged_by_us,

  output logic [31:0] c_legal_discards,
  output logic [31:0] c_self_inflicted
);
  // Order matters here too, and it is the ORDER OF THE PIPELINE --
  // a frame meets the filter before the FIFO and the FIFO before the
  // descriptor ring. Chapter 21.1 Section 17: the earliest fault
  // masks every later one.
  always_comb begin
    if      (addr_miss)      cls = CLS_FILTERED;
    else if (vlan_nonmember) cls = CLS_VLAN;
    else if (truncated)      cls = CLS_TRUNCATED;
    else if (fifo_full)      cls = CLS_DROP;
    else if (no_descriptor)  cls = CLS_DESCRIPTOR;
    else                     cls = CLS_GOOD;

    // Four of the five discard an intact frame on a rule. One of them
    // cuts a frame that was perfect. That is the distinction.
    is_legal_discard = (cls == CLS_FILTERED) || (cls == CLS_VLAN) ||
                       (cls == CLS_DROP)     || (cls == CLS_DESCRIPTOR);
    damaged_by_us    = (cls == CLS_TRUNCATED);
  end

  assign cls_valid = frame_end;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_legal_discards <= '0; c_self_inflicted <= '0;
    end else if (frame_end) begin
      if (is_legal_discard) c_legal_discards <= c_legal_discards + 32'd1;
      if (damaged_by_us)    c_self_inflicted <= c_self_inflicted + 32'd1;
    end
  end
endmodule

Classification: a second priority chain, whose order is a physical fact rather than a standard's wording.

What it teaches: that this chain's order is not a choice. Section 5's order was a rule somebody wrote; this one is the order the frame meets the blocks in. A frame rejected by Chapter 7.4's filter never reaches the FIFO, so addr_miss genuinely does take precedence — and Chapter 21.1 §17's masking result is this if-else chain seen from the outside.

And it teaches that damaged_by_us deserves a counter of its own. Four of the five classes discard an intact frame; CLS_TRUNCATED cuts a frame that arrived perfectly. Downstream of the FIFO the two are indistinguishable — both are short frames with bad check sequences — so c_self_inflicted is the only evidence that separates "our partner is broken" from "our memory system is slow."

Deliberately simplified: the five state inputs are assumed to be aligned to frame_end, which truncated cannot be: a truncation is decided mid-frame and the frame's end never arrives. fifo_full and truncated are separate inputs where Chapter 19.5 §14's guard derives both from one condition. And a frame can be filtered and malformed — the chain reports CLS_FILTERED and loses the malformation, which is correct for a discard counter and wrong for a channel diagnosis.

Production implication: the last simplification is a real design decision and it should be made deliberately. A malformed frame addressed to another port is discarded twice over, and which counter moves decides what a monitoring system concludes about the link. Counting it as filtered hides a channel fault; counting it as an FCS error inflates the error rate with traffic that was never ours. Chapter 7.3 §3's rule settles it — validity is checked before the address, so the error class wins — and an implementation that filters first will under-report channel errors in exact proportion to how much foreign traffic the port sees.


12. The Taxonomy, Complete

Thirteen classes — the twelve of Section 1 plus the one Section 8 found — with what each physically is, what causes it, and which Chapter 21.1 sites it points at.

#ClassPhysicallyPoints at sites
1FCS errorthe octets changed between transmitter and check1, 2, 3, 5, 6
2alignment errorthe frame ended part-way through an octet1, 2, 3
3undersizea conformant-looking frame below 64 octetsthe far end's transmitter
4fragmenta frame that was cut, or a collision remnant1, the medium, a partner
5oversizea frame above this port's MTUconfiguration, 5, 7
6jabbera partner transmitting continuously and badlythe far end
7symbol errora code group the line coding cannot decode1, 2, 3
8filterednot addressed here8 — and it is not a fault
9VLAN discardthis port is not in that VLAN9 — not a fault
10buffer dropthe consumer stalled past the FIFO's depth10, 11, 12
11mid-frame truncationthe same, mid-frame10, 11, 12
12descriptor errorno buffer was posted11, 12
13unclassifiedSection 8's shape5 — and nothing counts it

Three rows deserve a sentence because they are routinely misread.

Row 3 — undersize is not an error in the frame. A 40-octet frame with a valid check sequence is a well-formed frame of an illegal length. Nothing corrupted it; a transmitter emitted it. Chapter 5.6's padding rule exists precisely to make this impossible, so an undersize frame means a transmitter that is not padding — a software MAC, a malformed injection, or a partner that is broken in a very specific way.

Row 4 — a fragment is a historical name for two different things. In a shared-medium deployment it is a collision remnant, which is what the name comes from. On a switched full-duplex link there are no collisions, so a fragment there is a frame that was cut — by a cable that parted mid-frame, by a partner that stopped transmitting, or by Chapter 19.5 §14's truncation at the far end's receiver being looped back. Same counter, two eras, and one of the two causes no longer exists.

Row 6 — jabber is the one class whose action is to disconnect. A partner transmitting continuously denies the medium to everything else in a shared domain and floods a buffer in a switched one. The standard's response is isolation rather than diagnosis, which makes it the only class in the table where the correct first action is not to investigate.

And the taxonomy, arranged by what a counter reading tells you about where to look:

Counter movingLook atCertainty
alignment errors onlythe PHY or the xMII3 sites
FCS errors onlythe check engine — nothing else moved1 site
FCS and alignmentthe channel or the PHY3 sites
fragmentsa cut, a collision, or a truncationseveral, and one is local
jabbersthe far end1 site, effectively
undersize with no errorsthe far end's transmitter1 site
oversize with no errorsan MTU mismatchconfiguration
nothingclasses 8 to 12 — Chapter 21.1's class G5 sites

And the same table, read backwards — from a suspected site to the counter that would confirm it — because that is how an investigation that has a hypothesis actually proceeds.

Suspected siteConfirming evidenceDisconfirming evidence
the channelFCS and alignment both climbingalignment at zero
the PHY's lanesthe PHY's own symbol counter agreescounts_disagree
the xMIIcounts_disagree — Section 9the two counts agree
the parser's offsetsundersize and oversize together, frames_in healthyframes_in short
the check engineFCS alone, everything else zeroany alignment error
the validity boundsundersize and oversize on conformant sizesthe sizes are genuinely illegal
a partner not paddingundersize with valid check sequencesthe check sequences fail
an MTU mismatchoversize clustered at 1 522 or 1 526the sizes are spread
a cut cablefragments — if the classifier can assign themSection 4's error-major case
our own FIFOc_self_inflicted — if implementedit is not implemented

Rows four and five are the pair worth memorising because they are adjacent and opposite. Both present as errors on frames the wire delivered intact; the parser moves the size counters and the check engine moves only the check-sequence oneand frames_in separates them from the three physical sites in one read.

Rows nine and ten are the two whose confirming evidence may not exist. A cut cable's fragments are unassignable on an error-major chip, and our own truncation needs a counter no standard requires — so two of the ten hypotheses in this table cannot be confirmed on a default platform, which is Section 19's argument in the vocabulary of an investigation rather than of a budget.

Row two is the reading everybody gets wrong and it is the most precise row in the table. FCS errors climbing with alignment errors at zero, on a link whose frames_in is healthy, is not a channel symptom: a damaged channel corrupts bits and bit corruption does not preserve octet boundaries reliably, so a real channel fault moves both counters. FCS errors alone, on frames the port counted correctly, points at the check engine itselfChapter 19.4 §14's equivalence checker — and that is Chapter 21.3's whole subject.


13. RTL 6 — The Classification Comparator

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// classification_comparator -- run both orders on every frame and
// count where they differ.
//
// This is the only way to find out which order a piece of silicon
// implements, because no datasheet states it. In a testbench it is a
// checker; against real hardware it is a one-frame experiment.
// ---------------------------------------------------------------------
module classification_comparator
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        cls_valid,
  input  predicates_t pred,
  input  err_class_e  cls_size_major,
  input  err_class_e  cls_error_major,
  input  err_class_e  cls_observed,      // what the DUT reported

  output logic        orders_differ,
  output logic [31:0] c_differ,
  output logic [31:0] c_agree,
  output priority_order_e inferred_order,
  output logic        order_known,
  output logic        dut_matches_neither
);
  logic [31:0] votes_size, votes_error;

  assign orders_differ = (cls_size_major != cls_error_major);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_differ <= '0; c_agree <= '0;
      votes_size <= '0; votes_error <= '0;
      dut_matches_neither <= 1'b0;
    end else if (cls_valid) begin
      if (orders_differ) begin
        c_differ <= c_differ + 32'd1;
        // Only frames where the two orders DISAGREE carry information
        // about which order the DUT implements. The other 6 of 12
        // shapes are votes for both.
        if      (cls_observed == cls_size_major)  votes_size  <= votes_size + 32'd1;
        else if (cls_observed == cls_error_major) votes_error <= votes_error + 32'd1;
        else    dut_matches_neither <= 1'b1;
      end else begin
        c_agree <= c_agree + 32'd1;
      end
    end
  end

  assign inferred_order = (votes_size >= votes_error)
                        ? ORDER_SIZE_MAJOR : ORDER_ERROR_MAJOR;
  // One discriminating frame is enough. Section 21's directed test
  // sends exactly one.
  assign order_known = ((votes_size + votes_error) > 32'd0);
endmodule

Classification: an inference block — it determines a fact about a design by watching it.

What it teaches: that only six of the twelve shapes carry any information about the classification order, and a test that never produces one cannot determine which order the hardware implements, however many frames it sends. Conformant traffic is entirely in the agreeing half; so is every frame with exactly one error. The discriminating frames are short-or-long and broken — which is Chapter 20.5's injector and nothing else.

And it teaches that dut_matches_neither is a real outcome rather than a defensive default. A third order exists — error-major but with alignment last, which some implementations use — and it agrees with neither of the two modelled here on rows 8 and 9. A comparator that assumed two possibilities would silently record a vote for whichever it matched by accident.

Deliberately simplified: the vote is a simple majority with no tie-break beyond the >=, so a design with zero discriminating frames reports ORDER_SIZE_MAJOR with no evidenceorder_known exists to say so and nothing enforces reading it. The block compares three classifications where a real one compares a class against a set of counters, because hardware does not report a class; it increments a counter. And c_agree counts frames that carry no information, which is 6 of 12 shapes and almost 100% of real traffic.

Production implication: this block is the reason Chapter 21.1's method needs a per-platform fact recorded beside the counter inventory. The site table in Chapter 21.1 §2 is keyed on which counter moves, and rows 5, 8 and 11 of Section 4 move a different counter on the two orders. One injected frame — 40 octets, check sequence stomped — settles it permanently, and the answer belongs in the same one-minute datasheet check that Chapter 21.1 §12's step 0 already performs.


14. What a Taxonomy Must Never Do

Six prohibitions. Three are about the classes and three are about the counters, and all six produce a number that looks like a measurement.

NeverBecause
1treat the classes as disjoint predicates7 of 12 shapes satisfy two or more
2leave a reachable shape unclassifiedSection 8 — an uncounted frame is invisible
3assume the priority order without testing itSection 13 — no datasheet states it
4count a discard and a damage in one counterSection 11 — different actions
5count a truncation as a fragmentone is the partner's fault and one is ours
6report a symbol error as a frame classSection 9 — the gap errors have no frame

Row two is the one with the highest cost-to-effort ratio in the chapter. One counter — c_unclassified, about 99 flopsturns a silent stuck-bit-count fault from a class-G mystery into a one-read diagnosis. No standard requires it; nothing prevents it; and it is the thirteenth row of Section 12's table because this chapter added it.

Row five is the one that misdirects an entire team. A mid-frame truncation and a fragment are byte-identical downstream of the FIFO — short, bad check sequence — and the actions are opposite: investigate the partner, or investigate our own memory system. Chapter 19.6 §22's likely_buffer_not_cable exists for exactly this and is optional.

And the two that look like pedantry and are not:

Why it is a prohibition
row onea $onehot over predicates fails on 58.3% of reachable shapes
row sixa gap symbol error is the earliest channel warning available

Both mistake a property of the counting for a property of the frame, which is what all six have in common and is exactly the shape of Section 20's rejected property.

And three things a taxonomy must do, stated positively, because six prohibitions are hard to act on.

AlwaysCosts
1make the priority rule a visible structure, not an if-else ordernothing — Section 5 against Section 7
2count every reachable shape, including the unnamed one~99 flops
3report priority_applied beside the class countsone bit

Row one is free and is the one that would have prevented every disagreement in this chapter. Section 5's classifier folds A and B into broken and then switches on the size; Section 7's chain tests conditions in decreasing specificity. They differ by one intermediate signal, and the one with the intermediate signal cannot be written in the wrong order without somebody noticing, because broken has a name and a meaning and a reviewer can ask what it is for.

Row three is the one that makes the whole taxonomy auditable from outside. A frame that qualified for one class needed no rule; a frame that qualified for three had a rule applied to it. A counter of how often that happened turns "which order does this chip use" from a question requiring injected frames into a question answerable from a status page — and it is one bit and one counter.

A taxonomy is not a list of names. It is a list of names plus a rule for the overlap, and only the first half ever gets written down.


15. RTL 7 — Taxonomy Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// taxon_telemetry -- what the class counts mean, which is a different
// question from what they are.
//
// Three groups: SHAPE says what kind of traffic this is; ORDER says
// whether the classification rule is observable here; and GAPS says
// what the taxonomy could not name.
// ---------------------------------------------------------------------
module taxon_telemetry
  import taxon_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] c_frames,
  input  logic [31:0] c_class [13],
  input  logic [31:0] c_multi_predicate,
  input  logic [31:0] c_unclassified,
  input  logic [31:0] c_differ,
  input  logic [31:0] c_legal_discards,
  input  logic [31:0] c_self_inflicted,
  input  logic        order_known,
  input  priority_order_e inferred_order,

  // Shape.
  output logic [15:0] error_rate_ppm,
  output logic [15:0] multi_predicate_pct,
  output logic [2:0]  dominant_class,

  // Order.
  output logic        order_observable,
  output logic        order_is_size_major,

  // Gaps.
  output logic [15:0] unclassified_ppm,
  output logic        taxonomy_incomplete,
  output logic        discards_exceed_errors
);
  logic [31:0] total_errors;

  always_comb begin
    total_errors = c_class[CLS_FCS]      + c_class[CLS_ALIGN]
                 + c_class[CLS_UNDERSIZE]+ c_class[CLS_FRAGMENT]
                 + c_class[CLS_OVERSIZE] + c_class[CLS_JABBER];

    error_rate_ppm = (c_frames == 32'd0) ? 16'd0
                   : 16'((total_errors * 32'd1000000) / c_frames);

    multi_predicate_pct = (total_errors == 32'd0) ? 16'd0
                        : 16'((c_multi_predicate * 32'd100) / total_errors);

    // Section 4: the order is only observable on frames where the two
    // rules disagree, and those are short-or-long AND broken.
    order_observable    = (c_differ > 32'd0) && order_known;
    order_is_size_major = (inferred_order == ORDER_SIZE_MAJOR);

    unclassified_ppm = (c_frames == 32'd0) ? 16'd0
                     : 16'((c_unclassified * 32'd1000000) / c_frames);

    // Section 8: a shape the standard does not name, occurring at a
    // rate far above the 1-in-4.29-billion chance figure, is a design
    // fault -- almost always a stuck trailing-bit count.
    taxonomy_incomplete = (c_unclassified > 32'd1000);

    // Section 10: four of the six non-frame classes are not errors,
    // so a link discarding far more than it errors is CONFIGURED
    // that way rather than broken.
    discards_exceed_errors = (c_legal_discards > (total_errors << 4));
  end

  // Which frame class dominates. It is the fastest summary there is.
  always_comb begin
    dominant_class = 3'd0;
    for (int i = 1; i <= 6; i++)
      if (c_class[i] > c_class[dominant_class]) dominant_class = 3'(i);
  end
endmodule

Classification: a reporting block whose three groups answer three different people's questions.

What it teaches: that order_observable has to be reported beside every class count. If it is false, the link produced no frame on which the two classification rules differ — so the counts are the same under either rule and a method built on one of them is safe here. If it is true, the counts depend on which rule this chip implements, and Chapter 21.1 §2's site table has to be selected accordingly.

And it teaches that discards_exceed_errors is a good reading. Four of the six non-frame classes are legal discards — a port on a busy segment filters most of what it hears — so a link discarding sixteen times more than it errors is working exactly as configured. Reporting discards and errors in one "drops" number, which many systems do, makes a healthy port look like a failing one and hides a failing one inside a healthy count.

Deliberately simplified: total_errors sums six counters that the standard already guarantees are disjoint, so the sum is safe only if this design's classifier is one of Section 4's two — a third order could double-count. dominant_class is a linear scan with no tie-break. discards_exceed_errors uses a shift by four as "sixteen times", which is a threshold nobody derived. And unclassified_ppm is against all frames where Section 8's chance rate is against corrupted ones.

Production implication: unclassified_ppm is the output that turns Section 8's invisible fault into a one-read diagnosis. Chance produces one unnamed frame in 4.29 billion corrupted ones, so any sustained rate above a handful per billion is a design fault and is almost always a trailing-bit count stuck non-zero. The counter does not exist in any standard, costs about 99 flops, and is the difference between a fault that presents as Chapter 21.1's class G and one that names its own site.


16. RTL 8 — The Taxonomy Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// taxon_conformance_monitor -- six verdicts, and the first one fires
// on a design that is perfectly conformant to a different reading of
// the same standard.
// ---------------------------------------------------------------------
module taxon_conformance_monitor
  import taxon_pkg::*;
#(
  parameter priority_order_e EXPECTED_ORDER = ORDER_SIZE_MAJOR
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        cls_valid,
  input  predicates_t pred,
  input  err_class_e  cls,
  input  logic        order_known,
  input  priority_order_e inferred_order,
  input  logic [31:0] c_unclassified,
  input  logic [31:0] c_class [13],
  input  logic [31:0] c_frames,
  input  logic        dut_matches_neither,

  output logic        order_unexpected,
  output logic        order_untested,
  output logic        unnamed_shape_seen,
  output logic        fragments_never_move,
  output logic        classes_overlap,
  output logic        third_order_detected,
  output logic        taxonomy_sound
);
  // Section 4: not a fault, and it changes which counter every fault
  // in Chapter 21.1's table moves.
  assign order_unexpected = order_known && (inferred_order != EXPECTED_ORDER);

  // A run that never produced a discriminating frame has not tested
  // the thing this monitor exists to test.
  assign order_untested = !order_known && (c_frames > 32'd100000);

  assign unnamed_shape_seen = (c_unclassified > 32'd0);

  // Section 7: an error-major classifier can never reach these two,
  // so a permanently-zero pair is the tell.
  assign fragments_never_move = (c_frames > 32'd1000000) &&
                                (c_class[CLS_FRAGMENT] == 32'd0) &&
                                (c_class[CLS_JABBER]   == 32'd0);

  // The counters must be disjoint even though the predicates are not.
  assign classes_overlap = cls_valid && (n_predicates(pred) > 1) &&
                           (cls == CLS_GOOD);

  assign third_order_detected = dut_matches_neither;

  assign taxonomy_sound = !unnamed_shape_seen && !classes_overlap &&
                          !third_order_detected && !order_untested;

  always_ff @(posedge clk) begin
    if (rst_n && order_unexpected)
      $display("[taxon] classifier is %s; Chapter 21.1's site table must match",
               inferred_order == ORDER_SIZE_MAJOR ? "size-major" : "error-major");
  end
endmodule

Classification: an auditor whose loudest verdict reports a legitimate design choice.

What it teaches: that order_unexpected is deliberately not a term of taxonomy_sound. A size-major and an error-major classifier are both defensible readings of Chapter 7.3 §3, and only one of them is RMON's. Calling the other unsound would be wrong; saying nothing would let a debugging method silently use the wrong table. So it is a loud report and not a failure — the same split Chapter 21.1 §16 made for instrumentation_limited.

And it teaches that fragments_never_move is the cheapest possible detection of an error-major classifier. Section 7 showed that chain never assigns CLS_FRAGMENT or CLS_JABBER at all — so on any link that has seen a million frames and some errors, both counters being exactly zero is close to proof. No injection, no comparator, no MDIO: two register reads.

Deliberately simplified: classes_overlap checks only the one impossible case — multiple predicates classified as CLS_GOODwhere a full check would need the counters, not the class. order_untested's threshold of 100 000 frames is a guess at how long before a discriminating frame should have appeared, and on a healthy link the honest answer is never. And EXPECTED_ORDER is a parameter whose default encodes this chapter's conclusion, which is a position rather than a fact.

Production implication: fragments_never_move belongs in a fleet inventory rather than in a debug session. Two register reads per port, once, and the result is a per-platform fact that never changes: this chip classifies size-major, that one error-major, and Chapter 21.1 §2's table must be selected to match. A monitoring system that records it alongside the optional-counter inventory has captured everything a method needs to know about a port before anybody is paged.


17. What Each Class Physically Means

A class name is a compressed causal claim, and six of the thirteen names are older than the technology they now describe.

ClassThe name claimsStill true?
FCS errorthe check sequence failedyes — it is a definition
alignment errorthe frame is misaligned to octetsyes
undersizetoo shortyes
fragmenta collision remnantNO — no collisions on a switched link
oversizetoo longyes
jabbera station jabbering on the mediumpartly — no shared medium
symbol erroran undecodable code groupyes
filterednot for usyes
buffer dropwe ran out of roomyes
truncationwe cut a frameyes, and the name hides who

And two names that are precise and are still routinely misread, which is a different failure from an obsolete name.

NamePrecise meaningCommon misreading
alignment errornot whole octets and the check failed"the frame was misaligned somehow"
oversizelonger than this port's MTU"longer than Ethernet allows"

The second is the one that costs time. There is no universal Ethernet maximum: 1 518 untagged, 1 522 with one tag, 1 526 with two, and 9 018 on a jumbo-configured port. oversize is therefore a statement about configuration, not about conformance — and the frames it counts are very often perfectly legal frames on a port that was told a smaller number.

Rows four and six are the two whose names are archaeology. Both come from the shared-medium era: a fragment was what a collision left on the wire, and a jabber was a station that would not stop transmitting. On a modern point-to-point full-duplex link neither cause exists, and the counters are still there counting something else entirely.

fragments counted, 1985fragments counted, today
causea collisiona cut cable, a stopped partner, a far-end truncation
normal ratenon-zero — collisions were expectedzero
what non-zero meansthe segment is busysomething is physically wrong

The change in meaning is total and the counter's name did not move with it. A monitoring system with a fragment threshold inherited from the 1990s will tolerate a rate that today indicates a parting cable — and an engineer who reads "fragment" as "collision" will look for a collision domain that has not existed on that link since it was installed.

And one name hides an important distinction rather than an obsolete one.

"Truncation" does not say who truncated. Chapter 19.5 §14's overflow guard cuts frames this receiver could not store; a cut cable produces frames the medium cut. Downstream they are identical — short, bad check sequence, reported as fragments — and the actions are opposite. Chapter 19.6 §22's likely_buffer_not_cable bit exists to separate them, it is optional, and Section 14's row five is the prohibition it enforces.

Six of the thirteen classes are precise. Two describe a mechanism that no longer exists. One hides the agent. And one has no name at all.

Now the physical layer, class by class, because "what it physically means" is a different answer at every rate and the classes do not say so.

ClassAt 1 Gb/s over copperAt 100 Gb/s over fibre
symbol errora 4B/5B or 8B/10B code group that does not decodea 64B/66B block with a bad sync header, or an FEC block beyond correction
alignment errora nibble lost on the interface, or a PHY that paddeda lane deskew failure — Chapter 3.4
FCS errornoise on a pair, or a crosstalk eventalmost always a lane or an interface fault, not the fibre
fragmenta cut, or a legacy collisiona cut, or a far-end truncation
jabbera stuck transmittera stuck transmitter, and rarer

Row three is the one whose meaning inverts across the table and the reason is Chapter 3.7's forward error correction. A 100 Gb/s optical link corrects the channel's errors before the MAC ever sees them — so by the time a frame reaches the check sequence with damage, the damage happened after correction, which means inside the receiver. The same counter name points outward at 1 Gb/s and inward at 100.

What a rising FCS count suggests
1 Gb/s copper, no FECthe channel — look outward
10 Gb/s, no FECthe channel or the interface
25 Gb/s and above, with FECpast the FEC — look inward

And that is not a subtlety; it is the difference between replacing a cable and reading Chapter 19.4's residue logic. A rising uncorrected-error rate on a FEC-protected link should have shown up in the FEC's own counters first — and those are in the PHY, over MDIO, which is the same place Section 9's symbol counters live and the same reason nobody reads them.

One more physical note, about the class nobody expects to be physical.

filtered is not physical and VLAN discard is not physical, but buffer drop and truncation are timing — and timing is as physical as a cable. Chapter 19.5 §14's overflow happens when a memory system stalls longer than the buffer's depth in time: 2.621 µs at 100 Gb/s for a 512-beat FIFO. A DRAM refresh, a PCIe completion stall, a noisy neighbour on the interconnect — all of them produce a class that looks like a frame error and is a memory-system event.


18. What the Taxonomy Assumes

Nine assumptions. Four are about the standard, three about the design and two about the traffic — and the one most often false is about the MTU.

AssumptionFromIf false
1the classes are counted disjointlyChapter 7.3 §3a frame is counted twice; every rate inflates
2the priority order is size-majorRMON's wordingSection 4 — six shapes move a different counter
3the MTU is what this port was toldconfigurationthe oversize boundary moves; VLAN tags move it too
4trailing bits are availableChapter 7.3 §7predicate A is unevaluable; two classes vanish
5the check sequence result is correctChapter 19.4class 1 is about the checker, not the frame
6validity is checked before the addressChapter 7.3 §3errors on foreign traffic are under-reported
7a truncation is distinguishable from a fragmentan optional counterSection 14's row five
8a symbol error inside a frame also fails the checkline codingthe two counters stop agreeing
9the traffic exercises the overlapnothing at allthe order is untestable — Section 16

Row three is the assumption that is false most often and it is false by configuration rather than by fault. The oversize boundary is 1 518 octets untagged, 1 522 with one VLAN tag and 1 526 with twoChapter 13.2's stacking — so a port configured for untagged frames classifies every single-tagged frame at 1 519 to 1 522 as oversize.

Port configurationThe MTU boundaryA 1 522-octet tagged frame is
untagged1 518oversize
one tag permitted1 522good
two tags permitted1 526good

Four octets of configuration, and a conformant frame becomes an error class. The symptom is characteristic and is worth knowing: oversize climbing with no check-sequence errors, on frames of exactly one or two sizeswhich is not damage at all and is Chapter 21.1's "configuration" row of Section 12's table.

Row five is the one that inverts the whole taxonomy and it is Chapter 21.3's subject. Every class in the table is defined against the check sequence's verdict. If the checker itself is wrong, class 1 is not a statement about the frame — it is a statement about Chapter 19.4's residue comparison, and the frames were perfect. Section 12's row two is the tell: FCS errors alone, alignment at zero, frames_in healthy.

Row nine is the assumption nothing supports and it is why Section 16 has an order_untested verdict. The six discriminating shapes are short-or-long and broken, which conformant traffic never produces — so a link can run for a year without ever revealing which classification order its MAC implements. The test is Chapter 20.5's injector and one frame.

And three things deliberately not assumed:

Not assumedWhy not
that a class implies a siteChapter 21.1 §2 — FCS errors are five sites
that a discard is an errorfour of the six non-frame classes are not
that the taxonomy is completeSection 8 — one reachable shape has no name

Row three is this chapter's only disagreement with the standard, and it is a small one: the standard is complete on the eleven shapes it names and silent on the twelfth, which is not an error in the standard and is an error in any classifier built literally from it.

And there is a tenth assumption that is not in the table because it is about a person rather than about a design.

Every class name in Section 12 is read by somebody as a causal claim, and four of the thirteen names make a claim that is wrong or incomplete on a modern link.

NameWhat it is read asWhat it now means
fragmenta collisiona cut frame, from three causes
jabbera station hogging a shared mediuma stuck transmitter at the far end
truncationsomething cut the framewe cut the frame
undersizethe frame was damageda transmitter did not pad

The assumption is that the reader knows all four, and nothing in a counter's name or a MIB's description conveys any of it. A monitoring system inherits the names, a threshold inherits the 1990s, and an engineer inherits the readingwhich is why Section 17 exists as a section rather than as a footnote.

If the assumption is false
fragment misreada search for a collision domain that does not exist
truncation misreadthe partner is blamed for our memory system
undersize misreadthe channel is blamed for a transmitter

All three send an investigation outward when the fault is inward or the reverse, which is the most expensive class of error in Module 21 and the only one no counter can fix.


19. The Cost, Accounted

A taxonomy is mostly comparators, so the cost is in the counters rather than in the logic.

BlockFlopsNature
taxon_pkg0types and two tables
predicate_extractor~64two counters
size_major_classifier — 13 × 32~416the class counters
error_major_classifier0combinational, and a control
symbol_error_bridge~112two counters and a shadow
nonerror_discriminator~64two counters
classification_comparator~129four counters and a flag
taxon_telemetry0combinational
taxon_conformance_monitor0combinational
total~785 flops

Five of the nine blocks are pure combinational logic, which is what a taxonomy is: a set of comparisons. That is the structural point of the accounting and it is worth pausing on: the classification itself is free. Section 4's six disagreements, Section 8's unnamed shape and Section 13's order inference all cost zero flip-flops — they are questions about how comparators are arranged, not about how much state a design carries.

FlopsShare of this chapter
comparators and priority chains00%
counters785100%

Every decision in this chapter is free and every observation costs about 99 flops, which is a useful way to read the whole of Module 21: getting the taxonomy right is an argument, and being able to see it is a budget. All 785 flops are counters, and 416 of them are the class counts that Chapter 19.7 already builds — so the chapter's genuine addition to a MAC is about 369 flops.

And the addition breaks down into three decisions, each of which can be taken separately.

AdditionFlopsBuys
c_unclassified~99Section 8's invisible fault becomes one read
c_self_inflicted~99Section 11 — our truncation vs their fragment
c_symbol_in_gap~99Section 9 — the earliest channel warning there is
the comparator's four~129which order this chip implements
all four~4263.0% of Chapter 19.7 §19's datapath

Three per cent, and every one of the four answers a question that is currently unanswerable from outside the chip. Compare the module's other numbers:

FlopsShare of the 14 166-flop datapath
Chapter 21.1 §19's four optional counters~2972.1%
this chapter's four~4263.0%
both~7235.1%
Chapter 21.1 §8's twelve boundary counters~1 1888.4%

Five point one per cent buys the difference between Chapter 21.1's class of five and a named site, plus the three distinctions this chapter found were missing. Thirteen point five per cent buys all of that plus a four-observation bisection, which is the information-theoretic floor.

And the comparison that frames all of it:

FlopsWas it built?
Chapter 19.4's correction barrels5 397 XOR termsyes — correctness
Chapter 19.7's shadow bank992yes — a common reading instant
every counter in Module 21~1 911 at mostno

Module 21's entire wish list is smaller than Chapter 19.7's shadow bank plus a fifth of Chapter 19.4's barrels, and none of it is built — because correctness is a requirement and diagnosability is a preference. That is not an argument; it is the observation the two chapters of this module keep arriving at from different directions.


20. Properties Worth Asserting, and One Worth Refusing

The frame validity chapter says a frame is counted once, so the natural property asserts that at most one error class applies. Bind that property to the predicates, which are facts about the frame's octets, and it fires on seven of the twelve reachable shapes, fifty eight point three per cent, on a design that is entirely correct, because a forty octet frame with a stomped check sequence really is both short and failing. Move the property to the class instead and it becomes vacuous: the class is a single enumerated value, so a one hot check over a decode of it is a tautology that holds on a size major classifier, on an error major one, and on any third order as well. So the property is false where it is meaningful and meaningless where it is true. The useful properties are different in kind. Assert the tie break rule itself, that a frame whose size is out of range lands in one of the four size driven classes. Assert exclusivity where it genuinely holds, on the counter increments rather than on the frame. Assert that the classification is a total function, so that no reachable shape is left undefined. And cover the overlap, because the first two properties both pass on traffic that never overlaps, which is all conformant traffic.At most one classthe tempting propertyBound to thepredicatesfires on 58.3%Bound to theclassa tautologyFalse, thenvacuousclass 95Assert thetie-breakout of range picks 4Assert on thecountersone incrementAssert totalityevery shape mapsCover the overlapor all four pass empty12
Figure 4 — false on the input, vacuous on the output, and the useful property is neither.

Thirty-three properties and eight covers, in four groups: the predicates, the classification, the counters, and the classes that are not frame properties.

Group one — the predicates.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A, B and S are facts about octets. Nothing here mentions a class.
p_pred_from_bits:   assert property (@(posedge clk) disable iff (!rst_n)
                      pred_valid |-> (pred.not_whole_octets == (trailing_bits != 3'd0)));

p_pred_fcs:         assert property (@(posedge clk) disable iff (!rst_n)
                      pred_valid |-> (pred.fcs_mismatch == !fcs_ok));

p_size_exclusive:   assert property (@(posedge clk) disable iff (!rst_n)
                      pred_valid |-> !((pred.size == SZ_SHORT) && (pred.size == SZ_LONG)));

p_size_boundaries:  assert property (@(posedge clk) disable iff (!rst_n)
                      (pred_valid && wire_len == 14'd64) |-> (pred.size == SZ_IN_RANGE));

p_mtu_boundary:     assert property (@(posedge clk) disable iff (!rst_n)
                      (pred_valid && wire_len == 14'(MTU_OCTETS)) |->
                        (pred.size == SZ_IN_RANGE));

p_npred_range:      assert property (@(posedge clk) disable iff (!rst_n)
                      pred_valid |-> (n_pred <= 4'd3));

p_multi_counted:    assert property (@(posedge clk) disable iff (!rst_n)
                      (pred_valid && n_pred > 4'd1) |=>
                        (c_multi_predicate == $past(c_multi_predicate) + 32'd1));

p_extractor_pure:   assert property (@(posedge clk) disable iff (!rst_n)
                      ($stable(wire_len) && $stable(trailing_bits) && $stable(fcs_ok))
                        |-> $stable(pred));

Group two — the classification. Every property here names an order; none of them asserts that the classes are disjoint as predicates, which is Section 20's refused property.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_short_is_family:  assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && pred.size == SZ_SHORT) |->
                        ((cls == CLS_UNDERSIZE) || (cls == CLS_FRAGMENT)));

p_long_is_family:   assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && pred.size == SZ_LONG) |->
                        ((cls == CLS_OVERSIZE) || (cls == CLS_JABBER)));

p_fragment_broken:  assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_FRAGMENT) |->
                        (pred.not_whole_octets || pred.fcs_mismatch));

p_undersize_clean:  assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_UNDERSIZE) |->
                        (!pred.not_whole_octets && !pred.fcs_mismatch));

p_align_needs_both: assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_ALIGN) |->
                        (pred.not_whole_octets && pred.fcs_mismatch));

p_unnamed_shape:    assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_UNNAMED) |->
                        (pred.not_whole_octets && !pred.fcs_mismatch &&
                         (pred.size == SZ_IN_RANGE)));

p_good_is_clean:    assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_GOOD) |-> (n_predicates(pred) == 0));

p_priority_flagged: assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && n_predicates(pred) > 1) |-> priority_applied);

p_orders_agree_1:   assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && n_predicates(pred) <= 1) |->
                        (cls_size_major == cls_error_major));

Group three — the counters.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_one_counter:      assert property (@(posedge clk) disable iff (!rst_n)
                      cls_valid |=> ($countones(counters_moved) <= 1));

p_counter_matches:  assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && cls != CLS_UNNAMED) |=>
                        (c_class[$past(cls)] == $past(c_class[$past(cls)]) + 32'd1));

p_unnamed_uncounted:assert property (@(posedge clk) disable iff (!rst_n)
                      (cls_valid && cls == CLS_UNNAMED) |=> $stable(c_class));

p_sum_le_frames:    assert property (@(posedge clk) disable iff (!rst_n)
                      (total_errors + c_class[CLS_GOOD]) <= c_frames);

p_rate_bounded:     assert property (@(posedge clk) disable iff (!rst_n)
                      error_rate_ppm <= 16'd1000000);

p_order_needs_diff: assert property (@(posedge clk) disable iff (!rst_n)
                      order_known |-> (c_differ > 32'd0));

p_errmajor_no_frag: assert property (@(posedge clk) disable iff (!rst_n)
                      (inferred_order == ORDER_ERROR_MAJOR && order_known) |->
                        (c_class[CLS_FRAGMENT] == 32'd0));

p_agree_no_info:    assert property (@(posedge clk) disable iff (!rst_n)
                      !orders_differ |=> ($stable(votes_size) && $stable(votes_error)));

Group four — the classes that are not frame properties.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_discard_not_error:assert property (@(posedge clk) disable iff (!rst_n)
                      is_legal_discard |-> !damaged_by_us);

p_trunc_is_damage:  assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_TRUNCATED) |-> damaged_by_us);

p_filter_first:     assert property (@(posedge clk) disable iff (!rst_n)
                      (frame_end && addr_miss) |-> (cls == CLS_FILTERED));

p_symbol_no_frame:  assert property (@(posedge clk) disable iff (!rst_n)
                      (rx_error && !in_frame) |=>
                        (c_symbol_in_gap == $past(c_symbol_in_gap) + 32'd1));

p_symbol_in_frame:  assert property (@(posedge clk) disable iff (!rst_n)
                      (rx_error && in_frame) |-> ##[1:$] (pred_valid && pred.fcs_mismatch));

p_phy_read_needed:  assert property (@(posedge clk) disable iff (!rst_n)
                      !mdio_valid |-> phy_unreadable);

p_wire_cannot_drop: assert property (@(posedge clk) disable iff (!rst_n)
                      (cls == CLS_DROP) |-> consumer_stalled);

p_class_partition:  assert property (@(posedge clk) disable iff (!rst_n)
                      cls_valid |-> (is_frame_property(cls) != (cls >= CLS_SYMBOL)));

p_sound_excludes:   assert property (@(posedge clk) disable iff (!rst_n)
                      taxonomy_sound |-> (!classes_overlap && !third_order_detected));

And eight covers, because six of these are shapes a reviewer needs to have seen produced.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_all_twelve:      cover property (@(posedge clk) shapes_seen == 12'hFFF);
c_unnamed_shape:   cover property (@(posedge clk) cls == CLS_UNNAMED);
c_fragment:        cover property (@(posedge clk) cls == CLS_FRAGMENT);
c_jabber:          cover property (@(posedge clk) cls == CLS_JABBER);
c_orders_differ:   cover property (@(posedge clk) orders_differ);
c_gap_symbol:      cover property (@(posedge clk) rx_error && !in_frame);
c_truncation:      cover property (@(posedge clk) damaged_by_us);
c_three_preds:     cover property (@(posedge clk) n_pred == 4'd3);

21. Verification Scenarios

Fifty-eight scenarios for a classifier, plus a five-run directed test that determines a fact about silicon nobody documents.

The predicates — 10 scenarios.

#ScenarioExpected
1a 64-octet frame, good FCS, whole octetsno predicate; CLS_GOOD
2exactly 64 octetsSZ_IN_RANGE — the boundary is inclusive
363 octetsSZ_SHORT
4exactly 1 518 octetsSZ_IN_RANGE
51 519 octetsSZ_LONG
61 522 octets, one VLAN tag, untagged portSZ_LONG — Section 18's row three
7the same on a tag-permitting portSZ_IN_RANGE
83 trailing bitsnot_whole_octets
9a 40-octet frame, 3 trailing bits, bad FCSn_pred = 3
10the same, re-presentedpred is stable — the extractor is pure

The twelve shapes — 12 scenarios.

#ScenarioSize-major expects
11A=0 B=0 in rangegood
12A=0 B=0 shortundersize
13A=0 B=0 longoversize
14A=0 B=1 in rangeFCS error
15A=0 B=1 shortfragment — error-major says FCS
16A=0 B=1 longjabber — error-major says FCS
17A=1 B=0 in rangeCLS_UNNAMED
18A=1 B=0 shortfragment — error-major says undersize
19A=1 B=0 longjabber — error-major says oversize
20A=1 B=1 in rangealignment
21A=1 B=1 shortfragment — error-major says alignment
22A=1 B=1 longjabber — error-major says alignment

The order inference — 9 scenarios.

#ScenarioExpected
231 000 conformant framesorder_known stays low
241 000 in-range FCS errorsstill low — shape 14 agrees
25one frame of shape 15order_known asserts
26a size-major DUT on shape 15votes_size = 1
27an error-major DUT on shape 15votes_error = 1
28a DUT reporting CLS_ALIGN on shape 18dut_matches_neither
291 000 000 frames, fragments = 0fragments_never_move
30the same with one shape-15 frame injectedthe flag clears or the order is proven
31order_untested after 100 000 clean framesasserts

The unnamed shape — 8 scenarios.

#ScenarioExpected
32shape 17 oncec_unclassified = 1; no class counter moves
331 001 of themtaxonomy_incomplete
34a stuck trailing-bit registerevery frame lands in shape 17
35the RMON counters during thatall zero
36frames_in during thathealthy
37frames_out during thatshort by every frame
38Chapter 21.1's mask on that evidenceclass G — and the fault is at site 5
39c_unclassified read oncethe diagnosis, in one register

Symbol errors and the non-frame classes — 10 scenarios.

#ScenarioExpected
40RX_ER during a framec_symbol_in_frame; the FCS also fails
41RX_ER in the gapc_symbol_in_gap; no frame class moves
42a degrading channel, earlygap errors only — the earliest warning
43MDIO never polledphy_unreadable stays asserted
44PHY count exceeds the MAC'scounts_disagree — site 3
45PHY count matchessites 1 or 2
46a filtered frame that is also malformedChapter 7.3 §3 — the error class wins
47the same on a filter-first designchannel errors under-reported
48a mid-frame truncationdamaged_by_us; looks like a fragment
49a fragment from the wireis_legal_discard low, damaged_by_us low

Counters and telemetry — 9 scenarios.

#ScenarioExpected
50one frame, one classexactly one counter increments
51a shape-21 framefragments only — not alignment too
52priority_applied on shape 21asserts
53priority_applied on shape 14asserts — two predicates
54priority_applied on shape 12low — one predicate
5516× more discards than errorsdiscards_exceed_errors — and it is healthy
56c_unclassified at 1 per 4.29 billionchance — Section 8's row one
57c_unclassified at 1 per 1 000a design fault
58error_rate_ppm with every frame bad1 000 000

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

The case: determine which classification order a piece of silicon implements, using one frame.

Constrained-random traffic will not do this in any run of any length, because the six discriminating shapes are short-or-long and carrying an error, and a generator constrained to legal sizes produces none of them — Chapter 20.1 §5's allow_illegal is clear by default. The case needs Chapter 20.5's injector and a deliberate choice of length.

RunThe frameWhat it establishes
A64 octets, goodthe baseline — no counter moves
B64 octets, FCS stompedfcs_errors — both orders agree
C40 octets, good FCSundersize — both orders agree
D40 octets, FCS stompedTHE TEST — fragment or FCS error
E40 octets, 3 trailing bits, FCS stompedfragment or alignment

The oracle is four-part and runs D and E are the only two that carry information.

PartABCDE
size-major classgoodFCSundersizefragmentfragment
error-major classgoodFCSundersizeFCSalignment
do they differ?nononoYESYES
frames needed11111

Row four is the test's whole economy: five frames, and two of them settle a fact no datasheet states. Runs A, B and C exist as controls — they prove the classifier is working at all, which matters because a silent classifier and an error-major one look the same on run D if you only look at fragments.

Run E is the one that separates the three possible orders rather than two. A size-major design reports fragment; an error-major design reports alignment; a design that checks size before alignment but after the check sequence reports fragment on D and alignment on E, which is Section 13's dut_matches_neither and is a real implementation pattern.

And the result belongs in the same one-minute record as Chapter 21.1 §12's step 0. Two facts per platform — which optional counters exist, and which classification order the MAC usesand every subsequent investigation on every port of that platform is faster for it.

One more property of this test is worth naming: it is the only experiment in Module 21 whose oracle is entirely a priori.

Needs
the framesfive, generated by Chapter 20.5's injector
the expected resultsSection 4's table — computed, not measured
a reference designnone
a fault to be presentnone

Row four is what makes it runnable at commissioning rather than during an outage. Nothing has to be broken; the five frames are sent into a perfectly healthy port and the counters answer a question about the silicon. Chapter 21.1 §11's counter probe has exactly the same character — four thousand frames into a healthy port — and the two together are about five seconds of traffic that permanently settle both of a platform's unknowns.

Which suggests the commissioning procedure this module has been building towards without saying so:

StepTrafficSettles
11 000 misaddressed framesis c_filtered implemented?
21 000 non-member VLAN framesis c_vlan_discards implemented?
3a stalled consumeris c_fifo_drops implemented?
4a driver with no descriptorsis c_desc_errors implemented?
540 octets, FCS stompedsize-major or error-major?
640 octets, dribbled, stompedor a third order?
7a dribbled in-range frameis c_unclassified implemented?

Seven steps, a few thousand frames, and the output is two lines in a platform inventorywhich is what every investigation on every port of that platform will otherwise spend an hour rediscovering, badly.


22. Debugging a Classifier

Six complaints.

Complaint 1 — "the fragments counter has never moved."

CheckIf yesMeaning
has the link seen errors at all?yesso the counter should have moved
fragments_never_move set?yesSection 16's two-register test
inject a 40-octet stomped framefcs_errors moves insteaderror-major — Section 7
is that a bug?noit is a different reading of the standard

And one more reading of the same evidence that is not a classification-order finding at all.

CheckIf yesMeaning
has the link seen any error?nothe counter is correct to be zero
is this a full-duplex point-to-point link?yescollisions are impossible, so one cause is gone
is c_self_inflicted implemented?noour own truncations would show as fragments
have frames ever gone missing?nothen nothing has been cut

On a healthy modern link the fragment counter should be zero and stay zero for the life of the port, which is exactly why a permanently-zero reading is not by itself evidence of an error-major classifier. Section 16's fragments_never_move requires a million frames and some errors before it fires, and that qualification is the difference between a verdict and a guess.

Row four is the part that surprises people. An error-major classifier is not non-conformant; it implements every normative definition correctly and applies them in a different order. The consequence is not a defect report — it is a note in the platform record, because Chapter 21.1 §2's site table has to match.

Complaint 2 — "oversize errors on a link with no damage."

CheckIf yesMeaning
are the FCS counters zero?yesnothing is corrupting anything
are the frames one or two sizes?yesnot random damage
are those sizes 1 519 to 1 526?yesVLAN tags — Section 18's row three
is the port configured for tags?nothe answer, and it is configuration

Four octets per tag and a conformant frame becomes an error class. The characteristic signature is oversize climbing with every other counter at zero and the offending frames clustered at exactly 1 522 or 1 526 — which no amount of cable replacement will improve.

Complaint 3 — "all the counters are zero and frames are disappearing."

CheckIf yesMeaning
frames_in healthy?yesthe port is receiving
c_unclassified implemented?nothen you cannot see Section 8's shape
if implemented, is it climbing?yesa stuck trailing-bit count — site 5
Chapter 21.1's mask says?class Gfive sites, and none of them is site 5

Row four is the failure that motivates the whole of Section 8. The method converges on a candidate set that does not contain the answer, and it does so correctly — because an unclassified frame increments nothing and a method built on counters cannot see it. One counter, 99 flops, and the mystery is a register read.

Complaint 4 — "our error rate doubled after a firmware update."

CheckIf yesMeaning
did the traffic change?noso the classification did
is any single counter unchanged?frames_inthe same frames arrived
did fragments fall as FCS rose?yesthe order changed — not the link
or did two counters both rise?yesa frame is being counted twice

Rows three and four are two completely different findings from the same symptom. A reclassification moves frames between counters and leaves the total fixed; a double count raises the total, which violates Chapter 7.3 §3 and is a real defect. The discriminator is whether total_errors against c_frames moved, and it is one subtraction.

Complaint 5 — "the PHY says symbol errors and the MAC says nothing."

CheckIf yesMeaning
are the symbol errors in the gap?yesno frame to damage — Section 9
c_symbol_in_gap implemented?usually notso the MAC genuinely sees nothing
is the channel degrading?probablythis is the earliest warning available
wait for frame errors?they will comeand the link will already be failing

Gap symbol errors are the only leading indicator in the entire taxonomy and almost nothing counts them. A channel degrades for hours or days before its errors start landing inside frames, because a frame occupies a minority of the wire time at low utilisation — and every frame-based class is blind for that whole period.

Complaint 6 — "we see fragments on a full-duplex point-to-point link."

CheckIf yesMeaning
are there collisions?nothe name's original cause is impossible
c_self_inflicted implemented?nothen our own truncation is invisible
is the far end's buffer overflowing?possiblyand it looks identical from here
is a cable parting mid-frame?possiblyalso identical

Fragments on a modern link are never collisions and the counter's name says they are. Three causes remain — our truncation, the far end's truncation looped back, and a physical cutand only the first is distinguishable, by a counter that is optional. Section 17's table is the archaeology; this is what it costs in practice.

Complaint 7 — "two switches on the same link report different error counts."

CheckIf yesMeaning
are the totals equal?yesthe same frames; only the classification differs
do the distributions differ?yesdifferent classification orders — Section 4
are the totals different?yesdifferent MTUs, or one is filtering first
does one report fragments and the other not?yesone is size-major and one is error-major

Rows one and two together are the diagnosis and they are checkable in one subtraction. Two conformant MACs on the ends of one cable see identical frames — so their total_errors must agree to within the skew of the window. A matching total with a different distribution is two classification orders; a different total is a different MTU, a different filter order, or a genuinely asymmetric link, which is a real and much rarer thing.

Complaint 8 — "the error rate improved when the neighbouring port got busier."

CheckIf yesMeaning
is the port promiscuous or on a shared segment?yesit hears foreign traffic
does the design filter before validating?yesSection 18's row six
did c_filtered rise with the "improvement"?yesthe answer
did the physical link change?nonothing improved

A filter-first classifier under-reports channel errors in exact proportion to how much foreign traffic the port sees, because a malformed frame addressed elsewhere is counted as filtered rather than as damaged. So the measured error rate falls when the neighbours get busier, which reads as an improvement and is an artefact of a priority order. Chapter 7.3 §3 says validity is checked first, and an implementation that does otherwise is not obviously wrong and is measurably misleading.

And the three symptoms this chapter is systematically blamed for:

SymptomBlamed onUsually is
a counter that never movesthe designa different classification order
oversize with no damagethe linka VLAN tag and a port's MTU
frames vanishing with no classthe methodSection 8's unnamed shape

23. Misconceptions

Misconception 1 — "the error classes are mutually exclusive."

The wrong model: Chapter 7.3 §3 says a frame is counted once, so a frame belongs to exactly one class.

What it costs: it makes the priority rule invisible. Seven of the twelve reachable frame shapes satisfy two or more error predicates — a 40-octet frame with a stomped check sequence is short and failing — so exclusivity is manufactured by a tie-break, not found in the frame. A team that believes the classes are intrinsically disjoint never asks which rule its hardware uses, and Section 4 says the two candidate rules disagree on half the space.

The corrected model: the classes are a partition of the frame space quotiented by a priority rule. The counters are disjoint; the predicates are not. Section 20's rejected property is this misconception written in SVA.

Misconception 2 — "a fragment means a collision."

The wrong model: the name is literal, so a non-zero fragment count means a collision domain.

What it costs: there are no collisions on a full-duplex point-to-point link, so an engineer looks for a cause that has not existed on that link since it was installed. Meanwhile the three causes that do exist — a cable parting mid-frame, the far end's buffer truncating, our own Chapter 19.5 §14 overflow looped back — go unexamined, and one of the three is local.

The corrected model: fragments today counts cut frames, from three causes, only one of which is distinguishable and only with an optional counter. Section 17's table names the other obsolete class too: jabber, which no longer means what its name says either.

Misconception 3 — "an undersize frame was damaged."

The wrong model: it is in the error counters, so something broke it.

What it costs: it sends an investigation to the channel. An undersize frame has a valid check sequence — nothing corrupted it — it is a well-formed frame of an illegal length, which means a transmitter emitted it that way. Chapter 5.6's padding rule exists to make it impossible, so an undersize frame is a partner that is not padding.

The corrected model: three of the six frame classes are about damage — FCS, alignment, fragment — and three are about a transmitter or a configuration — undersize, oversize, jabber. They take entirely different first actions, and Section 12's table says which is which.

Misconception 4 — "the standard defines everything."

The wrong model: the class definitions are normative and exhaustive, so a literal implementation is correct.

What it costs: one reachable shape has no class — not whole octets, check sequence passing, in range — and a literal classifier must put it somewhere or drop it. Dropping it is what real hardware does, and that makes a stuck trailing-bit count present as Chapter 21.1's class G: five candidates, none of which is the actual site.

The corrected model: the taxonomy is complete on eleven shapes and silent on the twelfth. A classifier needs a thirteenth class and a counter for itc_unclassified, 99 flops — and the standard will not ask for it.

Misconception 5 — "symbol errors and FCS errors are the same fault reported twice."

The wrong model: a symbol error corrupts a frame, which then fails its check, so the two counters are redundant.

What it costs: it discards the most useful counter in the taxonomy. A symbol error in the interframe gap corrupts nothing — there is no frame — so it moves no frame-based class and is invisible to every RMON counter. A degrading channel produces those first, for hours, before any frame error appears, because frames occupy a minority of the wire time at ordinary utilisation.

The corrected model: in-frame symbol errors are largely redundant with FCS errors; gap symbol errors are a leading indicator with no substitute. Section 9's two counters are deliberately separate, and c_symbol_in_gap is the one worth wiring.

Misconception 6 — "our error rate went up, so the link got worse."

The wrong model: the error counters are a measurement of the link.

What it costs: they are a measurement of the link as classified by this chip, with this MTU, on this port's VLAN configuration. A firmware change that alters the classification order moves frames between counters with the total fixed; a VLAN change moves conformant frames into oversize; and neither is a change in the link. Section 22's fourth complaint is a doubled rate whose cause was a reclassification.

The corrected model: compare total_errors against c_frames, which is invariant under reclassification, before comparing any individual class. If the total is stable and the distribution moved, the classifier changed; if the total moved, the link did.


24. Interview Questions

Question 1 — "A 40-octet frame arrives with a bad check sequence. Which counter increments?"

What the answer should establish: that it depends on the implementation and the standard's answer is fragments. RMON defines fragments as short packets with a bad check sequence or a bad alignment, which is a size-major rule. A strong answer names the alternative: an error-major chain tests A && B and then B before it tests the size, reaches fcs_errors, and never assigns fragments at all. The strongest answer gives the test: send that frame and see, because no datasheet states the order.

Question 2 — "Are the Ethernet error classes mutually exclusive?"

What the answer should establish: the counters are; the predicates are not. Seven of the twelve reachable frame shapes satisfy two or more error predicates, so exclusivity is produced by a tie-break rather than found in the frame. A strong answer states what that means for verification: a $onehot0 over the predicates fails on 58.3% of the space on a correct design, and moving it to the class makes it vacuous.

Question 3 — "Is there a frame the taxonomy cannot name?"

What the answer should establish: yes — not a whole number of octets, check sequence passing, length in range. An alignment error requires the check to fail, so this is not one; nothing else fits. A strong answer says why it matters: a stuck trailing-bit count puts every frame there, every error counter reads zero, and the fault presents as the five sites past the MAC — none of which is the parser. The strongest answer prices the fix at one counter.

Question 4 — "What is the difference between a fragment and a truncation?"

What the answer should establish: who cut the frame. A fragment was cut by the far end or the medium; a truncation was cut by this receiver, when Chapter 19.5 §14's overflow guard ran out of buffer mid-frame. Downstream they are identical — short, bad check sequence — and the actions are opposite: investigate the partner, or investigate our own memory system. A strong answer names the separating evidence and notes that it is optional.

Question 5 — "Your oversize counter is climbing and nothing else is. What is it?"

What the answer should establish: almost certainly a VLAN tag against an untagged port's MTU. 1 518 becomes 1 522 with one tag and 1 526 with two; a port configured for untagged frames classifies tagged conformant frames as oversize. A strong answer gives the signature: no check-sequence errors, and the offending frames clustered at exactly one or two sizes — random damage does not produce a size histogram with two spikes in it.

Question 6 — "Which error counter would you add to a MAC if you could add one?"

What the answer should establish: a counter for the unnamed shape, or one for gap symbol errors, and the candidate should be argued rather than named. The unnamed-shape counter turns an invisible parser fault into one register read. The gap symbol counter is the only leading indicator in the taxonomy — it moves for hours before any frame error does. A strong answer costs both at about 99 flops each, notes that all of Module 21's wish list is about 1 900 flops against a 14 166-flop datapath, and observes that none of it is built because correctness is a requirement and diagnosability is a preference.


25. Questions and Answers


26. What's Next

The taxonomy is the table; the rest of Module 21 works through it one class at a time.

ChapterTakesBecause
Chapter 21.3class 1it is 0.98 bits and five sites
Chapter 21.4the link that never comes upa fault space with no frames in it
Chapter 21.5negotiationChapter 11.4's asymmetric symptoms
Chapter 21.6classes 8 to 12the five this chapter could not separate

Chapter 21.3 is next and it inherits this chapter's most precise row. Section 12's table says FCS errors climbing with alignment errors at zero, on frames the port counted correctly, points at the check engine rather than at the channel — because a damaged channel does not reliably preserve octet boundaries, so a real channel fault moves both counters. Separating Chapter 19.4's residue comparison from a marginal connector is the difference between a logic fix and a cable, and both are cheap once you know which.

Chapter 21.6 is where the five unresolvable sites finally get separated, and this chapter has already named the price: four optional counters that no standard requires, because four of the six non-frame classes are legal discards and a standard counts errors.

And the series is now ninety-five classes long. Chapter 20.2 §8's six groups have taken three extensions in five chapters — Chapter 20.4's 91, Chapter 20.6's 93 and Chapter 21.1's 94and class 95 belongs with none of them. What 93, 94 and 95 share is that the property is true of one object and is bound to a different one: a width at a seam, a search against its sensors, a classification's output against its input. That is a coherent eighth group and it now has three members, which is enough to name it: a property bound to the wrong side of a function.


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.