Skip to content
VLSI Mentor

Ethernet · Module 21

CRC Errors

A channel fault's error rate is proportional to frame length and every logic fault's is flat, so the ratio is 23.72 or 1.00 — measurable on counters a MAC already computes.

A rising check-sequence error count is the best single observation in Chapter 21.1's method — 0.98 bits — and it leaves five candidate sites. This chapter separates them, and every separator is arithmetic over counters a MAC already has.

FaultFCS error rateDepends on frame size?Signature
the channel, random bitslowyes — exactly proportional23.72× between 64 and 1 518 octets
the channel, failed FEClow, clusteredyesbursts in time, not in size
the PHY's lane alignmenthighnoalignment errors move too
the xMII interfacehighnothe PHY's own count disagrees
the parser's offsets100%noevery frame, and frames_in is healthy
the check engine's own logic1.56%noa comb: every 64th length

Row one is the chapter's central measurement and it is exact rather than statistical. A random bit error damages a frame with probability proportional to the frame's length in bitsso the per-frame error rate at 1 518 octets is 1 518/64 = 23.72 times the rate at 64 octets, and Chapter 19.7 §2's RMON size histogram already contains the data to measure it.

Row six is the most specific signature in Module 21. Chapter 19.4 §4's engine has 64 partial-word residue classes; a fault in one of them fails 1.56% of frames, and the failing frames are the ones whose length is congruent to a fixed value modulo 64 — 23 of the 1 455 legal sizes, evenly spaced.

A channel fault is a slope. A logic fault is a comb. Nothing else in the taxonomy is that distinguishable.


1. Scope, and Five Sites One Counter Cannot Separate

Scope: the frames that fail their check sequence — what that verdict does and does not mean, and how to tell which of five sites produced it.

Not in scope: the engine itself. Chapter 19.4 builds it and this chapter reads its output as evidence. Nor the other error classesChapter 21.2 is the catalogue, and Chapter 21.1 is the method this chapter is one step of.

Start from where Chapter 21.1 leaves off.

From Chapter 21.1
c_crc_errors is the best single question0.980 bits — the most even split available
it moves for5 of the 12 sites
four of those five areinside the chip
what the method does nextc_alignment_errors, then frames_in

Those two next observations take five candidates to one or two, and this chapter is about what to do when they do not — and about the three measurements that separate the remaining sites without any further counters at all.

The five, with what each one physically does to a frame:

#SiteWhat it does
1the channelflips bits in transit — Chapter 3.3
2the PHY's lane alignmentpresents octets in the wrong order — Chapter 3.4
3the xMII interfaceloses or duplicates octets at the boundary — Chapter 4.2
5the parser's offsetscomputes the check over the wrong octets — Chapter 19.2 §3
6the check engine's logiccomputes the right octets wrongly — Chapter 19.4

Sites 1, 2, 3 and 5 damage or misread the frame. Site 6 does neitherthe frame is intact, the octets are the right octets, and the verdict is wrong. That distinction is Section 2's subject and it is what makes this chapter's rejected property the one it is.


2. What a Check Sequence Actually Guarantees

A frame's path has twelve stages. It begins in host memory, is read by the transmit direct memory access engine, passes through the transmit assembler, and reaches the point where the frame check sequence is appended. That append point is where the protected span begins. The frame then crosses the wire, the receiving physical layer, the media independent interface and the receive parser, and arrives at the check point, where the span ends. After that it passes through the receive buffer, the receive direct memory access engine and into host memory. Four stages are inside the span and eight are outside it. So a matching check sequence says only that these octets are the octets that were present when the value was appended: it says nothing about whether they were correct at that moment, and nothing about what happens to them afterwards. A store and forward switch validates the check sequence, stores the frame and appends a fresh one, which means each hop has its own span and none of them is end to end. A bit flipped in that switch's buffer therefore leaves the switch with a perfectly valid check sequence computed over corrupt data, and no link level mechanism anywhere in the path can detect it.Host memoryunprotectedTransmit DMAunprotectedThe append pointthe span beginsWire, PHY, xMIIprotectedThe parserprotectedThe check pointthe span endsFIFO, RX DMA,memoryunprotectedA switchregeneratesa new span, per hop12
Figure 1 — twelve stages, four inside the span, and a new span at every hop.

A frame check sequence is a statement about a span, not about a frame — and every misreading in this chapter comes from forgetting which span.

A matching check sequence says: these octets are the same octets that were present when the value was appended. It says nothing about whether they were correct then, and nothing about what happens to them afterwards.

Draw the span explicitly.

StageInside the protected span?
the host's memoryno
the transmit DMA read — Chapter 18.4no
the transmit assembler before the appendno
the append point — Chapter 19.3 §4the span begins
the wireyes
the receiving PHYyes
the xMIIyes
the receive parseryes
the check point — Chapter 19.4the span ends
the receive FIFOno
the receive DMA writeno
host memoryno

Four stages are protected and eight are not. Everything before the append and everything after the check is covered by nothing — which is why Chapter 18.6 and Chapter 19.6 carry their own integrity mechanisms and why a frame can arrive in host memory corrupt with a perfectly valid check sequence.

And that gives three distinct ways the verdict and the truth diverge.

The frame wasThe check saysWhere
1corrupt before the appendvalidbefore the span
2intact, the checker is brokeninvalidat the span's end
3corrupt in an undetectable patternvalidinside the span — Section 12
4corrupted after the checkvalidafter the span

Rows one and four are the reason a check sequence is not an end-to-end guarantee, and they are why Ethernet's FCS coexists with IP and TCP checksums that cover a different, overlapping span. Row two is site 6 and is this chapter's hardest separation. Row three is 2^-32 and Section 12 says how often that is.

One more property of the span, because it decides Section 10's burst analysis.

CRC-32 detects
any single bit erroralways
any odd number of bit errorsalways — the polynomial has x + 1 as a factor
any burst of 32 bits or feweralways
any burst longer than 32 bitswith probability 1 − 2^-32

Row four is the one that matters on a modern link. Chapter 3.7's forward error correction operates on blocks of thousands of bits, and a block the FEC cannot correct is damaged across its whole length — far beyond 32 bits. So exactly the failure mode a high-rate link produces is the one the check sequence's burst guarantee does not cover, and it falls back to the 2^-32 random bound.


3. RTL 1 — The Diagnostic Package and the Size-Rate Cross

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// crcdiag_pkg -- the five candidate sites of Section 1, and the three
// measurements that separate them.
//
// The measurements are a SLOPE, a COMB and a BURST. None of them needs
// a counter a MAC does not already have; all three need a counter
// CROSSED with something, and that is the whole cost.
// ---------------------------------------------------------------------
package crcdiag_pkg;

  typedef enum logic [2:0] {
    SRC_CHANNEL_RANDOM = 3'd0,
    SRC_CHANNEL_BURST  = 3'd1,
    SRC_PHY_LANES      = 3'd2,
    SRC_XMII           = 3'd3,
    SRC_PARSER         = 3'd4,
    SRC_CRC_LOGIC      = 3'd5,
    SRC_UNDECIDED      = 3'd7
  } crc_source_e;

  // Chapter 19.7 Section 2's RMON histogram, which already exists and
  // whose buckets are the x-axis of Section 4's measurement.
  localparam int N_BUCKETS = 7;

  // The representative length of each bucket, for the slope. Bucket 0
  // is a single size; the rest are geometric-ish spans and the
  // midpoint is close enough for a ratio test.
  localparam int BUCKET_MID [N_BUCKETS] = '{64, 96, 191, 383, 767, 1271, 4000};

  // Section 6: if the damage is random bits, the per-frame error rate
  // is proportional to the frame's length in bits. Bucket 5 against
  // bucket 0 is therefore 1271/64 = 19.86 for a channel fault and
  // 1.00 for every logic fault in Section 1.
  localparam int SLOPE_CHANNEL_X100 = 1986;
  localparam int SLOPE_LOGIC_X100   = 100;

  // Chapter 19.4 Section 4's residue classes. A fault in one of them
  // is Section 8's comb.
  localparam int N_RESIDUES = 64;

  // Section 12: CRC-32's random escape probability.
  localparam int ESCAPE_SHIFT = 32;

  function automatic int ratio_x100(int num, int den);
    return (den == 0) ? 0 : ((num * 100) / den);
  endfunction

endpackage

Classification: a package whose constants are two predicted slopes and one modulus.

What it teaches: that the whole diagnosis is a ratio test with a predicted value. A channel fault predicts 19.86 between bucket 5 and bucket 0; every logic fault predicts 1.00. Those are not thresholds somebody chose — they are the ratio of two frame lengths, and the prediction is exact in the way a physical law is exact, because the mechanism really is "each bit has an independent chance of flipping."

And it teaches why the RMON histogram is the right x-axis. Chapter 19.7 §2's seven buckets already exist in every conformant MACthe measurement needs the error counter crossed with them and nothing else. A cross of 7 by 1 is six extra counters, about 594 flops, against a diagnosis that currently requires a capture.

Deliberately simplified: BUCKET_MID uses midpoints where the correct value is the mean length of the frames actually in each bucket, which the traffic decides — so the predicted 19.86 is accurate to the traffic's shape rather than exactly. The jumbo bucket's midpoint of 4 000 is a guess. SLOPE_CHANNEL_X100 assumes independent bit errors, which Section 10 shows is false for a FEC-protected link. And ratio_x100 truncates.

Production implication: the two predicted slopes are far enough apart that the measurement tolerates a great deal of imprecision. 19.86 against 1.00 is a factor of twenty, so a bucket-midpoint estimate that is wrong by 30% still separates the hypotheses unambiguously. That is the property that makes this diagnosis practical rather than academic: it does not need a good model of the traffic, only a model good enough to distinguish twenty from one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// size_rate_cross -- check-sequence errors, crossed with Chapter 19.7
// Section 2's size buckets.
//
// Six extra counters, and they turn the single most ambiguous counter
// in Ethernet into a measurement with a predicted value.
// ---------------------------------------------------------------------
module size_rate_cross
  import crcdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_end,
  input  logic [2:0]  size_bucket,      // from Chapter 19.7's histogram
  input  logic        fcs_mismatch,

  output logic [31:0] c_frames_in_bucket [N_BUCKETS],
  output logic [31:0] c_errors_in_bucket [N_BUCKETS],
  output logic [31:0] rate_ppm_in_bucket [N_BUCKETS],
  output logic        cross_populated
);
  logic [N_BUCKETS-1:0] seen;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      for (int i = 0; i < N_BUCKETS; i++) begin
        c_frames_in_bucket[i] <= '0;
        c_errors_in_bucket[i] <= '0;
      end
      seen <= '0;
    end else if (frame_end) begin
      c_frames_in_bucket[size_bucket] <= c_frames_in_bucket[size_bucket] + 32'd1;
      seen[size_bucket] <= 1'b1;
      if (fcs_mismatch)
        c_errors_in_bucket[size_bucket] <= c_errors_in_bucket[size_bucket] + 32'd1;
    end
  end

  // A rate per bucket, which is the quantity Section 4 compares. A
  // COUNT per bucket says only what the traffic mix was.
  always_comb
    for (int i = 0; i < N_BUCKETS; i++)
      rate_ppm_in_bucket[i] = (c_frames_in_bucket[i] == 32'd0) ? 32'd0
        : ((c_errors_in_bucket[i] * 32'd1000000) / c_frames_in_bucket[i]);

  // Section 6: at least two buckets must have traffic or there is no
  // slope to measure, whatever the counts say.
  assign cross_populated = ($countones(seen) >= 2);
endmodule

Classification: a two-dimensional counter, and the second dimension is one a MAC already computes.

What it teaches: that a rate per bucket is the quantity and a count per bucket is not. Minimum-size frames dominate most traffic mixes, so a raw error count is highest in bucket 0 on a channel fault and on a logic fault alike — the count follows the traffic, not the fault. The rate divides that out, and the rate is what has a predicted value.

And it teaches that cross_populated has to gate every conclusion. A link carrying one frame size has one populated bucket and no slope at all — and Chapter 20.1 §17 showed that fixed-size streams are exactly what stress tests produce. The measurement needs a mixed traffic mix, which real links have and synthetic ones often do not.

Deliberately simplified: the rates divide combinationally, seven times, which is seven 32-bit dividers. A real implementation exports the counts and divides in software. size_bucket is an input rather than derived, so the block trusts Chapter 19.7's boundaries — and Chapter 21.2 §18 showed those boundaries move with the VLAN configuration. And there is no windowing: the counts are since reset, so a fault that started an hour ago is diluted by an hour of health.

Production implication: the missing windowing is the one to fix first. A slope computed over all time answers "has this link ever had a channel fault"; a slope over the last minute answers "does it have one now." Chapter 19.7 §15's telemetry has the same shape and the same gap, and the fix is the same: a shadow copy and a subtraction, which is 224 more flops for seven buckets and turns a historical record into a measurement.


4. The Error Rate Is Proportional to Length — If It Is the Channel

Cross the check sequence error counter with the seven size buckets of the remote monitoring histogram, which every conformant media access controller already computes, and compute an error rate per bucket rather than a count per bucket. If the damage is random bit errors on the channel, each bit is an independent opportunity to fail, so the per frame failure rate is proportional to the frame's length in bits. The ratio between the rate at one thousand two hundred and seventy one octets, the midpoint of bucket five, and the rate at sixty four octets, which is bucket zero, is therefore nineteen point eight six, and that ratio is the same at a bit error rate of ten to the minus twelve as at ten to the minus six. Every other candidate fault is a per frame mechanism: a lane deskew error misorders a frame of any length equally, a parser offset error selects the wrong octets for every frame equally, and a check engine fault computes wrongly for every frame it affects. All of them predict a ratio of one point zero zero. The two predictions are a factor of twenty apart, which means the test survives a poor estimate of the bucket midpoints, a skewed traffic mix and a short measurement window.Errors x sizebuckets6 countersA rate per bucketnot a countRandom bits8 L p per frameAny logic faultper frame, flatSlope 19.86at every BERSlope 1.00at every BERA factor of 20robust to estimateerror12
Figure 2 — one measurement, two predicted values, a factor of twenty apart.

A random bit error damages a frame if it lands anywhere in it. So the probability a frame of L octets fails its check is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
P(fail) = 1 − (1 − p)^(8L)   ≈ 8 · L · p   for small p

The approximation is excellent at every bit error rate a working link has, and it says the per-frame rate is linear in the frame's length.

Bit error rate64 octets1 518 octets9 000 octets1 518 / 64
10⁻¹²5.12 × 10⁻¹⁰1.21 × 10⁻⁸7.20 × 10⁻⁸23.72
10⁻¹⁰5.12 × 10⁻⁸1.21 × 10⁻⁶7.20 × 10⁻⁶23.72
10⁻⁸5.12 × 10⁻⁶1.21 × 10⁻⁴7.20 × 10⁻⁴23.72
10⁻⁶5.12 × 10⁻⁴1.21 × 10⁻²6.95 × 10⁻²23.58

The last column is constant across four orders of magnitude of channel quality, and it is exactly 1 518 / 64. The ratio does not depend on how bad the channel is; it depends only on the two frame lengths, which is what makes it a usable test: you do not need to know the bit error rate to run it.

Now every one of the other four sites.

SiteWhat it damagesRate depends on length?
the PHY's lane alignmentthe octet ordering of every frameno — a whole frame or none
the xMIIan octet at a boundary, per frameno
the parser's offsetsthe octets selected, per frameno
the check engine's logicthe computation, per frameno — but see Section 8

All four are per-frame mechanisms. A lane-deskew fault misorders a frame whether it is 64 octets or 9 000; a parser offset error selects the wrong octets for every frame equally. So all four predict a slope of 1.00, and the channel predicts 23.72.

Predicted ratio, bucket 5 against bucket 0
the channel, random bits19.86 — the midpoints' ratio
any of the four logic faults1.00
the gapa factor of 20

A factor of twenty is not a threshold anybody has to argue about, and that is the practical value of the measurement: it survives a bad estimate of the bucket midpoints, a skewed traffic mix and a short window. Any measured slope above about 5 is the channel; anything below about 2 is logic; and the region between is where the windowing of Section 3 matters.

One caveat, and it is the one that will bite.

Problem
the traffic mix decides which buckets are populatedcross_populated
a fixed-size stress testone bucket, no slope
an all-minimum-size floodbucket 0 only
the fixmeasure on production traffic, not on a test pattern

A synthetic test is the worst possible input to this measurement and production traffic is the best, which inverts the usual relationship between a diagnostic and a lab — and is the reason this diagnosis belongs in a monitoring system rather than in a bring-up script.

And the slope has a second use that costs nothing extra: it estimates the bit error rate.

Once the fault is known to be the channel, the per-bucket rate inverts directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p ≈ rate_in_bucket / (8 × bucket_midpoint)
Measured rate in bucket 5Implied bit error rateWhat it means
1 in 10⁶ frames9.8 × 10⁻¹¹a good link
1 in 10⁴9.8 × 10⁻⁹marginal, and degrading
1 in 10²9.8 × 10⁻⁷unusable for bulk transfer
1 in 109.8 × 10⁻⁶far outside any specification

Every Ethernet specification states a target bit error rate — 10⁻¹² for most copper and optical variants — and the frame counters are the only instrument most deployments have that can estimate it. The estimate is crude: it assumes independent bits, which Section 10 says is false on a FEC-protected link, and it ignores the errors the check sequence misses, which Section 12 says are 2^-32 of them. But it is right to within a factor of two on a link whose damage is random, and a factor of two is enough to say whether a link meets its specification.

What the two numbers give
the slopeis it the channel?
the rate, invertedand if so, how far outside spec?

Those two questions are the entire content of a channel-fault escalation, and neither of them needs anything the MAC does not already count.


5. RTL 2 — The Length-Slope Estimator

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// length_slope_estimator -- reduce Section 3's seven rates to one
// number and compare it against two predictions.
//
// The output is not a probability or a confidence. It is a ratio with
// two predicted values twenty apart, and the verdict is which one it
// is nearer to.
// ---------------------------------------------------------------------
module length_slope_estimator
  import crcdiag_pkg::*;
#(
  parameter int MIN_ERRORS = 32       // below this, the ratio is noise
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] rate_ppm_in_bucket [N_BUCKETS],
  input  logic [31:0] c_errors_in_bucket [N_BUCKETS],
  input  logic        cross_populated,

  output logic [31:0] slope_x100,
  output logic        slope_valid,
  output logic        looks_like_channel,
  output logic        looks_like_logic,
  output logic [2:0]  lo_bucket,
  output logic [2:0]  hi_bucket
);
  logic [31:0] total_errors;
  logic        enough;

  // Use the LOWEST and HIGHEST populated buckets that have enough
  // errors to be meaningful, rather than fixed buckets 0 and 5 --
  // the traffic decides which buckets exist.
  always_comb begin
    lo_bucket = 3'd0;
    hi_bucket = 3'd0;
    total_errors = 32'd0;
    for (int i = 0; i < N_BUCKETS; i++) begin
      total_errors += c_errors_in_bucket[i];
      if (c_errors_in_bucket[i] >= 32'(MIN_ERRORS)) begin
        if (lo_bucket == 3'd0) lo_bucket = 3'(i);
        hi_bucket = 3'(i);
      end
    end
    enough = (total_errors >= 32'(MIN_ERRORS)) && cross_populated &&
             (hi_bucket > lo_bucket);

    slope_x100  = enough
                ? 32'(ratio_x100(int'(rate_ppm_in_bucket[hi_bucket]),
                                 int'(rate_ppm_in_bucket[lo_bucket])))
                : 32'd0;
    slope_valid = enough;

    // Section 4: the two predictions are 19.86 and 1.00. Anything
    // above 5 is the channel and anything below 2 is logic; between
    // them the measurement has not decided and must say so.
    looks_like_channel = enough && (slope_x100 >= 32'd500);
    looks_like_logic   = enough && (slope_x100 <= 32'd200);
  end
endmodule

Classification: a ratio and two comparisons, and the honesty is in the gap between them.

What it teaches: that looks_like_channel and looks_like_logic are not complements, and that the region between them is a real state. A slope of 3.5 has not decided; reporting it as one or the other would be a coin flip with a twenty-fold separation available, which means the right response is more traffic or a longer window, not a verdict.

And it teaches why the buckets are chosen dynamically. Fixed buckets 0 and 5 fail on any link whose traffic does not populate both — and Chapter 20.1 §17's fixed-size streams populate exactly one. Choosing the lowest and highest populated buckets makes the measurement work on whatever mix the link actually carries, at the cost of a slope whose predicted value changes with the buckets selected.

Deliberately simplified: the predicted channel slope is computed for buckets 5 and 0 and is not adjusted when other buckets are chosen — so a measurement over buckets 1 and 4 has a predicted channel value of 767/96 = 7.99, not 19.86, and the fixed threshold of 5.00 is then much closer to the prediction than it should be. lo_bucket's "first populated" test misbehaves when bucket 0 has no errors, because its initial value is already 0. And MIN_ERRORS of 32 is a judgement, where a statistician would want a confidence interval.

Production implication: the threshold-versus-prediction mismatch is the block's real defect and it is worth fixing rather than documenting. The predicted slope should be BUCKET_MID[hi] / BUCKET_MID[lo], computed from the buckets actually used, and the verdict should be whether the measurement is nearer that or nearer 1.00. That is one division more and it makes the block correct for every traffic mix instead of for the one its constants were written against — which is Chapter 20.6 §6's lesson about constants that are secretly parameters, arriving in a diagnostic.


6. Twenty-Three Point Seven, and What It Means to Measure It

The slope is the chapter's primary separator, so it is worth being precise about what a measured value means and what it does not.

What it is: the ratio of the per-frame check-failure rate at one length to the rate at another. Under random bit damage it equals the ratio of the two lengths, because each bit is an independent opportunity to fail.

Value
1 518 / 6423.72
9 000 / 64140.63
bucket 5 midpoint / bucket 019.86
any per-frame logic fault1.00

What it is not: a measure of how bad the channel is. The slope is 23.72 at a bit error rate of 10⁻¹² and 23.72 at 10⁻⁸ — a factor of ten thousand in link quality with no change in the slope at all. The slope says what kind of fault; the absolute rate says how bad. Two numbers, two questions, and conflating them is Section 23's fourth misconception.

Says
the slopechannel or logic
the absolute rate in any buckethow severe
the two togetherthe diagnosis and its urgency

And there is a third thing the slope is not, which is a statement about a single frame. A 1 518-octet frame that failed was not damaged more than a 64-octet frame that failed; it was 23.72 times more likely to be damaged at all. The per-frame verdict carries no length information; only the population does — which is why this measurement cannot be made on a capture of a handful of frames and can be made on counters that have been running for an hour.

One genuinely useful corollary, because it inverts a common expectation.

On a channel fault, the frames that fail are disproportionately the large ones — so the bytes lost grow as the square of the length, not linearly.

LengthFailure rate at BER 10⁻⁸Octets lost per frame sent
645.12 × 10⁻⁶3.28 × 10⁻⁴
1 5181.21 × 10⁻⁴0.184
9 0007.20 × 10⁻⁴6.48

Between minimum and maximum size the failure rate rises 23.7× and the octet loss rises 561×, because a longer frame is both more likely to be hit and larger when it is. A link whose measured frame-loss rate looks tolerable on minimum-size traffic can be unusable for bulk transfer, and the single number in a status page does not distinguish the two.


7. RTL 3 — The Residue Histogram

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// fcs_residue_histogram -- failing frames, binned by Chapter 19.4
// Section 4's partial-word residue.
//
// If the check engine has a fault in one of its 64 residue paths, the
// failures land in ONE bin. Nothing else in Section 1's list produces
// a non-uniform residue distribution, so this is the sharpest
// separator in the chapter.
// ---------------------------------------------------------------------
module fcs_residue_histogram
  import crcdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_end,
  input  logic [13:0] wire_len,
  input  logic        fcs_mismatch,

  output logic [31:0] c_by_residue [N_RESIDUES],
  output logic [31:0] c_frames_by_residue [N_RESIDUES],
  output logic [5:0]  peak_residue,
  output logic [31:0] peak_count,
  output logic [31:0] total_errors,
  output logic        comb_detected
);
  logic [5:0] residue;
  // Chapter 19.4 Section 4: the residue is the pre-FCS length modulo
  // the beat width. Chapter 20.6 Section 6 -- this 64 is the beat
  // width and moves with the interface.
  assign residue = 6'((wire_len - 14'd4) % 14'd64);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      for (int i = 0; i < N_RESIDUES; i++) begin
        c_by_residue[i] <= '0;
        c_frames_by_residue[i] <= '0;
      end
      total_errors <= '0;
    end else if (frame_end) begin
      c_frames_by_residue[residue] <= c_frames_by_residue[residue] + 32'd1;
      if (fcs_mismatch) begin
        c_by_residue[residue] <= c_by_residue[residue] + 32'd1;
        total_errors <= total_errors + 32'd1;
      end
    end
  end

  always_comb begin
    peak_residue = 6'd0;
    peak_count   = 32'd0;
    for (int i = 0; i < N_RESIDUES; i++)
      if (c_by_residue[i] > peak_count) begin
        peak_count   = c_by_residue[i];
        peak_residue = 6'(i);
      end
    // A uniform distribution puts 1/64 of the errors in the peak bin.
    // Half of them in one bin is a comb, and a comb is a logic fault
    // in exactly one of Chapter 19.4's 64 paths.
    comb_detected = (total_errors > 32'd256) &&
                    (peak_count > (total_errors >> 1));
  end
endmodule

Classification: a 64-bin histogram whose interesting output is a single concentration test.

What it teaches: that Chapter 19.4 §4's 64 residue classes are a diagnostic axis and not only an implementation detail. The engine has sixty-four distinct partial-word paths; a fault in one of them fails only the frames that take it, which is 1.56% of a uniform traffic mix. Nothing else in Section 1's list is residue-selective — a channel does not know about beat boundaries, and a lane-deskew fault damages every frame equally.

And it teaches that c_frames_by_residue is needed as well as c_by_residue. Traffic is not uniform over residues: Chapter 20.1 §17's fixed-size streams occupy one residue entirely, so a raw error histogram is a picture of the traffic. The concentration test should be on the rate, and this block computes it on the count — which Section 22's third complaint is about.

Deliberately simplified: the modulus is a hardcoded 64, which Chapter 20.6 §6 spent a section arguing is really the beat width — at 8 octets per beat there are 8 residue classes and this block's 64 bins are 56 dead ones. The peak search is 64 comparators, combinational. comb_detected uses a half-of-all-errors threshold where a uniform distribution's peak bin would hold about 1.56% plus noise, so the test is very conservative and will miss a fault that affects a few residues rather than one.

Production implication: the conservatism is the right default and the failure it misses is worth naming. A fault in Chapter 19.4's correction barrel affects a range of residues rather than one — the barrel shifts by a residue-dependent amount, so a stuck bit in its control affects half of them. That produces a bimodal histogram, not a comb, and comb_detected stays low while the distribution is obviously non-uniform. A chi-squared test over 64 bins would catch both, costs nothing in hardware because the bins are already exported, and belongs in the software that reads them.


8. A Comb in the Length Distribution

The check engine has sixty four partial word residue paths, one for each value of the frame's pre check length modulo the sixty four octet beat. A fault in a single path fails only the frames that take it, which are the lengths congruent to a fixed value modulo sixty four: twenty three of the one thousand four hundred and fifty five legal wire lengths, spaced exactly sixty four octets apart. On uniform traffic that is one and a half six per cent of frames, and the failing lengths form a regular comb that no other fault in the candidate list produces. A fault in the correction barrel's shift control is different: the shift amount depends on the residue, so a stuck bit affects every residue whose binary representation has that bit set, which is half of them, and half of all frames fail. The histogram then shows thirty two high bins and thirty two empty ones rather than a single spike. And a conformance suite built from sixty four, one hundred and twenty eight, two hundred and fifty six, five hundred and twelve, one thousand and twenty four and one thousand five hundred and eighteen octets reaches only two residues, because the five powers of two are all congruent modulo sixty four and land on residue sixty, while fifteen eighteen lands on forty two. Residue sixty three, the spill class where the check value crosses a beat boundary, is reached by none of them.64 residue paths(L - 4) mod 64A fault in path rL = r + 4 mod 6423 lengths, 64apart1.56% of framesA barrel controlfault32 of 64 residues50% of framesfail32 bins highThe round-numbersuite64 to 1518Reaches 2 of 64residues 60 and 42Residue 63 neverthe hardest path12
Figure 3 — one residue path, twenty-three lengths, and a suite that reaches two of sixty-four.

A check engine fault in one residue path produces a failure set with a shape nothing else produces: a regular comb in the frame length.

Chapter 19.4 §4's residue is (L − 4) mod 64, so the frames taking residue path r are exactly those with:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
L ≡ r + 4  (mod 64)
Value
legal wire lengths, 64 to 1 5181 455
lengths congruent to any fixed value mod 6422 or 23
share of all lengths1.56%
spacing between failing lengthsexactly 64 octets

Twenty-three lengths, evenly spaced sixty-four apart, and every frame of those lengths fails while every other frame passes. On a uniform size distribution that is a 1.56% error rate; on production traffic it is whatever share those lengths happen to carry.

And it is the only mechanism in Section 1's list that produces a regular pattern.

FaultThe set of failing lengths
the channelall lengths, weighted by length
the PHY's lanesall lengths, uniformly
the xMIIall lengths, uniformly
the parser's offsetsall lengths, uniformly
one residue patha comb: 23 lengths, spaced 64 apart

Which makes the diagnosis visible by eye on a scatter plot of failing lengths, and computable without one: Section 7's histogram concentrates in one bin, and no other fault concentrates anywhere.

Two related signatures worth knowing, because the barrel is the more likely fault.

Chapter 19.4's correction barrel shifts the accumulated remainder by an amount that depends on the residue. A stuck bit in the shift amount affects every residue whose binary representation has that bit set — which is half of them, and the failing set is then:

A single-path faultA barrel control fault
residues affected1 of 6432 of 64
error rate on uniform traffic1.56%50%
histogram shapeone spikehalf the bins high, half at zero
comb_detected fires?yesno — no single peak
is it obvious?only with the histogramyes — half of all frames fail

Row five is why the barrel fault is the easier of the two in practice and the harder for Section 7's test. A 50% error rate is impossible to miss; the histogram's shape is what says it is the barrel rather than the parser, and comb_detected is the wrong test for it. A chi-squared statistic over the 64 bins detects both, and the bins are already exported.

And the third variant, which is the one that survives for years.

A fault in the residue-63 path only
share of lengths1.56%
which lengthsL ≡ 3 (mod 64)Chapter 19.3 §20's spill sizes
how often production traffic hits themrarely — 1.6% at best
what a test suite of round sizes reachesnone of them

Residue 63 is Chapter 19.4's spill case, where the check value crosses a beat boundary — the hardest path in the engine, the one Chapter 20.1 §15 built a whole coverage goal around, and the one a test suite built from 64, 128, 256, 512, 1 024 and 1 518 octets never exercises. A fault there fails 1.56% of production frames, forever, and no conformance test finds it.

And one variant of the comb that is not a fault at all, which matters because it will be seen.

Chapter 20.1 §17 showed that a fixed-size stream has a fixed wire period, and Chapter 19.3 §6's deficit makes that period a multiple of four. So a link carrying a small number of distinct frame sizes — which is most links, because protocols use a handful of sizeshas a residue distribution that is a comb before any fault is present.

TrafficResidues occupied
a 1 518-octet bulk stream1 — residue 42
64-octet acknowledgements1 — residue 60
the two together2
a real mix with a hundred sizesup to 64, unevenly

Errors then land in the residues the traffic occupies, whatever the fault is — so an error histogram on such a link is a comb produced by the traffic and not by the engine. The correction is the one Section 7 already names: divide by c_frames_by_residue and compare rates. On a link occupying two residues the rate test still works; on a link occupying one it cannot work at all, and cross_populated's equivalent for residues is the check that should gate it.

A comb at every 64th length is a check engine. A slope with length is a channel. A uniform 100% is a parser. Those three sentences are the chapter — and each of them is a rate, never a count.


9. RTL 4 — The Burst Detector

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// error_burst_detector -- are the failures spread in time or clustered?
//
// Section 10: a random channel produces independent failures; a
// failing FEC block, a connector that moves, or an interference source
// produces clusters. The distinction is not about which SITE is at
// fault -- both are the channel -- it is about what to do next.
// ---------------------------------------------------------------------
module error_burst_detector
  import crcdiag_pkg::*;
#(
  parameter int WINDOW_FRAMES = 1024
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_end,
  input  logic        fcs_mismatch,

  output logic [31:0] c_errors,
  output logic [31:0] c_windows,
  output logic [31:0] c_windows_with_error,
  output logic [31:0] max_in_window,
  output logic        clustered,
  output logic [31:0] expected_windows_x100
);
  logic [31:0] frames_in_window, errors_in_window;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_errors <= '0; c_windows <= '0; c_windows_with_error <= '0;
      max_in_window <= '0;
      frames_in_window <= '0; errors_in_window <= '0;
    end else if (frame_end) begin
      frames_in_window <= frames_in_window + 32'd1;
      if (fcs_mismatch) begin
        c_errors         <= c_errors + 32'd1;
        errors_in_window <= errors_in_window + 32'd1;
      end
      if (frames_in_window == 32'(WINDOW_FRAMES) - 32'd1) begin
        c_windows <= c_windows + 32'd1;
        if (errors_in_window != 32'd0)
          c_windows_with_error <= c_windows_with_error + 32'd1;
        if (errors_in_window > max_in_window)
          max_in_window <= errors_in_window;
        frames_in_window <= 32'd0;
        errors_in_window <= 32'd0;
      end
    end
  end

  // Independent errors spread across windows: with E errors and W
  // windows, about min(E, W) windows are touched. Clustering puts the
  // same E errors into far fewer.
  always_comb begin
    expected_windows_x100 = (c_errors < c_windows)
                          ? (c_errors * 32'd100)
                          : (c_windows * 32'd100);
    clustered = (c_errors > 32'd64) &&
                ((c_windows_with_error * 32'd400) < expected_windows_x100);
  end
endmodule

Classification: a clustering test, and the only block in the chapter that measures time rather than shape.

What it teaches: that clustering does not change which site is at fault and does change what to do about it. Random and bursty damage are both the channel — Section 4's slope is high in both cases. But random damage means a marginal signal-to-noise ratio, which is a cable, a connector or an optic, and clustered damage means an intermittent event: a connector that moves, an interference source, a FEC block that fails under a specific pattern. One replaces a component; the other looks for a correlation with something else.

And it teaches why the test is "how many windows were touched" rather than a variance. With E independent errors spread over W windows, about min(E, W) windows contain at least one. Clustering puts the same errors into far fewer windows, and the ratio needs no distributional assumption at all — which matters because the alternative, a variance test, needs one.

Deliberately simplified: the window is a fixed number of frames, not of time, so a link whose rate varies has windows of varying duration — and an interference source correlates with time rather than with frames. The factor of four in clustered is a judgement. max_in_window is recorded and never used. And the block cannot distinguish a burst of errors inside one frame — which the check sequence reports as one failure — from separate frames failing.

Production implication: the last simplification is a real limit and it points at where the evidence actually is. A single 5 140-bit FEC block spans several minimum-size frames at 100 Gb/s, so one failed correction damages several consecutive frames — which this block sees as a cluster and which the PHY's own uncorrected-block counter sees directly. Chapter 3.7's FEC counters are the better evidence and they are behind MDIO, which is the same reason Chapter 21.2 §9's symbol counters go unread.


10. Random Bits, Bursts, and Failed FEC

The check sequence's detection guarantee is conditional on the shape of the damage, and the shape a modern link produces is the one the guarantee does not cover.

DamageCRC-32 detectsWhy
one bitalwaysHamming distance
any odd number of bitsalwaysthe polynomial has x + 1 as a factor
a burst of ≤ 32 bitsalwaysthe remainder cannot be zero
a burst of > 32 bitswith probability 1 − 2^-32it falls back to chance

Rows three and four are the transition, and where a link sits on it is decided by its line coding.

LinkThe damage unitBitsInside the burst guarantee?
1 Gb/s, 8B/10Ba code group8 to 10yes
10 Gb/s, 64B/66Ba block64NO
25 Gb/s and above, with RS-FECan uncorrectable codewordthousandsNO

A 1 Gb/s copper link damages octets and CRC-32 catches every such burst with certainty. A 25 Gb/s link with Reed-Solomon FEC either corrects the damage completely or fails a whole codeword, and a failed codeword is damage across thousands of bits — far outside the 32-bit guarantee, so detection falls back to 1 − 2^-32.

Which changes what a check sequence means as the rate rises.

At 1 Gb/sAt 100 Gb/s with FEC
channel damage the MAC seessmall bursts, always detectedcorrected, or enormous and probabilistically detected
an FCS error therefore suggeststhe channelsomething past the FEC
the first counter to readc_crc_errorsthe PHY's uncorrected-codeword count

Row two is Chapter 21.2 §17's inversion stated as a diagnostic step. On a FEC-protected link the channel's errors are corrected before the MAC sees them — so a frame arriving with damage arrived after correction, which points inward. The exception is a codeword the FEC could not correct, and that has its own counter, in the PHY.

And the shape of the damage explains a signature that otherwise looks like a logic fault.

A failed FEC codeword
bits damagedthousands, contiguous
frames affected at 100 Gb/s minimum sizeseveral consecutive
Section 4's slopestill high — more frames of any size are hit
Section 9's clusteringstrongly clustered
looks likea channel fault that comes and goes

Slope high and clustering high together is the FEC signature, and it is distinguishable from a marginal channel — slope high, clustering low — by a test that costs one counter and a division.

And there is a third damage shape worth naming because its signature looks like a logic fault and is not.

A stuck or intermittent lane
what it damagesone lane of several, continuously
at 100 Gb/s, four lanesa quarter of every frame's octets
Section 4's slope1.00 — every frame is hit
alignment errorsoften, because lane order breaks too
looks likea logic fault, and it is the PHY

A lane that is dead rather than noisy damages every frame regardless of its length, so the slope reports 1.00 and the measurement points inward — correctly, because the fault is inside the PHY rather than on the fibre. The site is still 2 rather than 1, and that is the right answer: Chapter 21.1 §2's site 2 is "the PHY's lane alignment" and a dead lane is exactly that.

Which completes the slope's interpretation and it is not quite what Section 4 implied.

SlopeMeans
high, spreadthe medium itself — bits damaged in transit
high, clusteredthe medium intermittently, or failed FEC
1.00, with alignment errorsthe PHY or the interface — a per-frame mechanism
1.00, no alignment errorsthe parser or the check engine

The slope separates "damage proportional to bits on the wire" from "damage per frame" — and that is a cleaner statement than "channel versus logic," because a dead lane is neither the medium nor logic and lands correctly in row three.


11. RTL 5 — The Undetected-Error Estimator

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// fcs_escape_estimator -- how many corrupted frames got through.
//
// It is an ESTIMATE and it cannot be anything else: an undetected
// error is by definition invisible. What the block computes is the
// expected number, from the detected rate and CRC-32's 2^-32 bound --
// and the useful output is an interval, not a count.
// ---------------------------------------------------------------------
module fcs_escape_estimator
  import crcdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] c_frames,
  input  logic [31:0] c_errors,
  input  logic        bursty,          // from Section 9

  output logic [31:0] escapes_per_exa, // expected escapes per 10^18 frames
  output logic [63:0] frames_per_escape,
  output logic        escape_plausible,
  output logic        guarantee_is_probabilistic
);
  // Detected errors are the frames the checker caught. For each of
  // them, about 2^-32 of the SAME damage pattern would have escaped.
  // So the expected escape count is the detected count shifted right
  // by 32 -- which is zero for any realistic counter width, and that
  // is the point.
  always_comb begin
    escapes_per_exa = (c_frames == 32'd0) ? 32'd0
                    : 32'((c_errors * 32'd1000000000) /
                          (c_frames == 32'd0 ? 32'd1 : c_frames));
    frames_per_escape = (c_errors == 32'd0) ? 64'hFFFF_FFFF_FFFF_FFFF
                      : (64'(c_frames) << ESCAPE_SHIFT) / 64'(c_errors);

    // One escape becomes plausible once the frames seen approach the
    // frames-per-escape figure.
    escape_plausible = (64'(c_frames) > (frames_per_escape >> 4));

    // Section 10: on a burst-damaged link the 32-bit burst guarantee
    // does not apply, so the 2^-32 bound is the ONLY bound and the
    // detection is probabilistic rather than certain.
    guarantee_is_probabilistic = bursty;
  end
endmodule

Classification: an estimator whose honest output is a rate rather than a count.

What it teaches: that frames_per_escape is the number to report and it is derived, not measured. For every detected error there are about 2^-32 as many undetected ones with the same damage statistics — so the frames-per-escape figure is the frames seen, times 2^32, divided by the errors seen. A link with one error in a million frames escapes about once every 4.29 × 10¹⁵ frames, which is a number a person can reason about.

And it teaches that guarantee_is_probabilistic is the most important bit in the block. On a link whose damage is small bursts, CRC-32's guarantee is absolute and there are no escapes at all — the 2^-32 figure does not apply. On a link whose damage is failed FEC codewords, thousands of bits wide, the guarantee is gone and 2^-32 is the only bound there is. Section 9's clustering test is what decides which regime the link is in, and the escape estimate is meaningless without it.

Deliberately simplified: escapes_per_exa is computed and is wrong — the expression is the detected error rate per billion, not an escape rate, and it is left in as the block's own bug because it is the one somebody writes. The shift-by-32 in frames_per_escape overflows 64 bits for large frame counts. And the estimate assumes the escape probability of the detected damage applies to the undetected damage, which is the standard assumption and is unverifiable by construction.

Production implication: the honest use of this block is to answer a question nobody asks until an audit. "How often does a corrupted frame reach host memory with a valid check sequence?" At 100 Gb/s, 1 518-octet frames, a bit error rate of 10⁻¹⁰: 1.21 × 10⁻⁶ of frames are damaged, 2^-32 of those escape, 8.13 million frames per second — one escape every 13.8 years. That is the number that justifies a higher-layer checksum for data that matters and justifies not having one for data that does not.


12. One Escape Every Fourteen Years

The undetected-error rate is the number that decides whether the check sequence is enough, and it is computable exactly for any link.

Three quantities multiply.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
escapes per second = (frames per second)
                   × P(the frame is damaged)
                   × 2^-32
Value at 100 Gb/s, 1 518 octets, BER 10⁻¹⁰
frames per second8.127 × 10⁶
P(damaged)1.214 × 10⁻⁶
2^-322.328 × 10⁻¹⁰
escapes per second2.298 × 10⁻⁹
mean time between escapes4.35 × 10⁸ s — 13.8 years

Thirteen point eight years per port, which is why a single Ethernet link's check sequence is usually considered adequate on its own. And the number moves in ways worth knowing.

ChangeNew MTBFFactor
a worse channel: BER 10⁻⁸50 days100× worse
minimum-size frames instead13.8 years — unchanged
ten thousand ports in a fleet12 hours10 000× worse
the burst guarantee no longer appliesas computedit already assumed that

Row two is the one that surprises. Minimum-size frames are 23.72× less likely to be damaged and there are 18.3× more of them per secondand the two factors nearly cancel, because both are proportional to the frame's length. The escape rate is a property of the bits on the wire, not of the frames, and no frame-size decision changes it.

Row three is the one that matters operationally. Thirteen point eight years is a comfortable number for one link and twelve hours is not a comfortable number for a data centre. At fleet scale the check sequence is provably insufficient, which is why storage and database traffic carry their own integrity checks and why Chapter 18.4's DMA path has its own.

And there is a fourth change that is not in the table because it is not a change in the link.

Effect on the escape rate
a switch that regenerates the check sequenceeach hop gets its own 13.8 years
N hopsN times the escape rate
a store-and-forward switch with a corrupt bufferthe escape is created at the hop

Row three is the failure mode the arithmetic does not cover and is the real-world cause of most observed corruption. A switch that validates, stores, and re-appends a check sequence protects the frame's octets across each hop and not across the path — so a bit flipped in the switch's own memory leaves the switch with a perfectly valid check sequence computed over corrupt data. No amount of link-level integrity detects it, and the span argument of Section 2 is exactly why.

A check sequence's 13.8 years is per link, per port, and per hop — and it protects a span that ends at every switch.

One more comparison, because it is the one that decides whether a higher-layer check is worth its cost.

MechanismWidthEscape probabilitySpan
the Ethernet FCS32 bits2^-32one hop
the IPv4 header checksum16 bits2^-16end to end, header only
the TCP checksum16 bits2^-16end to end, header and payload
a 32-bit application digest32 bits2^-32end to end

Rows two and three are sixteen bits and escape sixty-five thousand times more often than the FCSone in 65 536 of the corrupted frames they seeand they are the only mechanisms in the table whose span is end to end. So the layering is not redundant: the strong check covers a short span and the weak checks cover a long one, which is exactly backwards from what a designer starting today would choose and is what history produced.

Consequence
corruption inside a hopcaught by the FCS, 2^-32
corruption inside a switch's buffercaught only by TCP, 2^-16
a fleet of switchesthe weak check is the one that matters

Row two is why a storage or database workload carries its own digest and why the argument for one is not "Ethernet is unreliable." Ethernet's check is the strong one; it is the span that is short — and the mechanism protecting the long span is the sixteen-bit one, which escapes once in every 65 536 corrupted segments.


13. RTL 6 — The Self-Check, Used as a Diagnostic

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// crc_equivalence_probe -- Chapter 19.4 Section 14's equivalence
// checker, repurposed.
//
// In that chapter it proves a parallel implementation matches a serial
// reference. Here it answers a diagnostic question: is the check
// engine computing the right value for THIS frame's residue class?
// One known-good frame per residue is enough, and there are 64.
// ---------------------------------------------------------------------
module crc_equivalence_probe
  import crcdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        probe_en,
  input  logic [5:0]  probe_residue,     // which of the 64 to test
  input  logic        probe_frame_end,
  input  logic [31:0] engine_result,     // what Chapter 19.4 computed
  input  logic [31:0] reference_result,  // a serial reference

  output logic [63:0] residue_tested,
  output logic [63:0] residue_failed,
  output logic [5:0]  first_bad_residue,
  output logic        engine_faulty,
  output logic        probe_complete
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      residue_tested <= '0; residue_failed <= '0;
      first_bad_residue <= 6'd0; engine_faulty <= 1'b0;
    end else if (probe_en && probe_frame_end) begin
      residue_tested[probe_residue] <= 1'b1;
      if (engine_result != reference_result) begin
        residue_failed[probe_residue] <= 1'b1;
        if (!engine_faulty) first_bad_residue <= probe_residue;
        engine_faulty <= 1'b1;
      end
    end
  end

  // Section 8: all 64 residue classes, or the probe has not tested
  // the one that matters. The spill class, residue 63, is the one a
  // round-number test suite never reaches.
  assign probe_complete = (residue_tested == 64'hFFFF_FFFF_FFFF_FFFF);
endmodule

Classification: an equivalence checker used as an instrument rather than as a verification component.

What it teaches: that the check engine is the one site in Section 1's list that can be tested directly, and that the test is sixty-four frames. Chapter 19.4 §14's reference already exists in the verification environment; running it against the shipped engine on one frame per residue class settles site 6 completely, with no inference and no statistics. None of the other four sites has an equivalent — you cannot ask a cable whether it is working.

And it teaches that probe_complete is the output that decides whether a negative result means anything. Sixty-three residues tested and clean says nothing about the sixty-fourth, and Chapter 19.3 §20's spill class — residue 63 — is the hardest path and the one a suite of round frame sizes never reaches. A probe that reports "no faults found" without probe_complete has tested the easy paths.

Deliberately simplified: the block needs a serial reference implementation present in the design, which no shipping MAC contains — so this is a bring-up and a test-mode instrument, not a field one. residue_tested is set on any probe frame, with no check that the frame actually had that residue. And engine_faulty is sticky with no way to clear it short of reset, which is correct for a verdict and inconvenient for a test loop.

Production implication: the absence of a reference in shipped silicon is the whole reason this chapter's other five sections exist. If a MAC carried a second, differently-implemented check engine, site 6 would be decidable in one cycle — and the cost would be Chapter 19.4's 8 512 XOR terms again, which nobody will spend. So site 6 is diagnosed statistically, by Section 7's histogram, and the histogram is 64 counters against a second engine's thousands of gates — which is the trade the chapter is built on.


14. What a CRC Diagnosis Must Never Do

Six prohibitions. Three are about the span and three are about the statistics, and every one of them produces a confident wrong site.

NeverBecause
1read a failing check as "the channel"it is five sites, four of them inside the chip
2read a passing check as "the frame is correct"Section 2 — the span starts after the transmitter's memory
3compare error counts across size bucketsthe count follows the traffic mix — Section 3
4run the slope test on a fixed-size streamone bucket, no slope
5conclude from fewer than a few dozen errorsthe ratio is noise
6apply the 2^-32 escape bound on a small-burst linkthere, detection is certain — Section 10

Row two is the one with consequences outside this chapter. A valid check sequence means these octets are the octets that were present at the append pointnot that they were the right octets then, and not that they survived the receive FIFO, the DMA and host memory. Chapter 18.4's read path is entirely outside the span, and a bit flipped there is protected by nothing and detected by nothing.

Row six is a prohibition in the opposite direction from the usual. On a 1 Gb/s copper link damaging code groups of eight to ten bits, CRC-32 detects every single burst with certaintythe escape probability is exactly zero, not 2^-32. Quoting the probabilistic bound there overstates the risk, which leads to a higher-layer checksum being added where the arithmetic did not call for one.

And the two that look like statistical pedantry and are not:

Why it is a prohibition
row threeminimum-size frames dominate, so the count is highest in bucket 0 either way
row fourChapter 20.1 §17 — a stress test is exactly one bucket

Both make the chapter's primary measurement return a number that is not a slope, which is what the six have in common: each one produces a value that looks like the measurement and is not it.


15. RTL 7 — Diagnostic Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// crcdiag_telemetry -- three measurements, one verdict, and the
// conditions under which the verdict is worth having.
//
// The verdict is a SITE, which is what Chapter 21.1's method wanted
// and could not get from c_crc_errors alone.
// ---------------------------------------------------------------------
module crcdiag_telemetry
  import crcdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] slope_x100,
  input  logic        slope_valid,
  input  logic        looks_like_channel,
  input  logic        looks_like_logic,
  input  logic        comb_detected,
  input  logic [5:0]  peak_residue,
  input  logic        clustered,
  input  logic [31:0] c_errors,
  input  logic [31:0] c_frames,
  input  logic        alignment_errors_present,
  input  logic        frames_in_healthy,
  input  logic        engine_faulty,

  output crc_source_e verdict,
  output logic        verdict_confident,
  output logic [15:0] error_rate_ppm,
  output logic [31:0] slope_reported,
  output logic        need_more_traffic,
  output logic        need_mdio
);
  always_comb begin
    error_rate_ppm = (c_frames == 32'd0) ? 16'd0
                   : 16'((c_errors * 32'd1000000) / c_frames);
    slope_reported = slope_x100;

    // The order is by DECREASING certainty, which is the opposite of
    // Chapter 21.2 Section 7's mistake: here the most specific
    // evidence really is the strongest, because each test is a
    // different measurement rather than a different predicate on one.
    if (engine_faulty)                       verdict = SRC_CRC_LOGIC;
    else if (comb_detected)                  verdict = SRC_CRC_LOGIC;
    else if (error_rate_ppm > 16'd900000)    verdict = SRC_PARSER;
    else if (alignment_errors_present)       verdict = SRC_PHY_LANES;
    else if (looks_like_channel && clustered)verdict = SRC_CHANNEL_BURST;
    else if (looks_like_channel)             verdict = SRC_CHANNEL_RANDOM;
    else if (looks_like_logic && frames_in_healthy) verdict = SRC_CRC_LOGIC;
    else                                     verdict = SRC_UNDECIDED;

    verdict_confident = (verdict != SRC_UNDECIDED) &&
                        (engine_faulty || comb_detected || slope_valid);

    need_more_traffic = !slope_valid && (c_errors < 32'd32);
    // Sections 2 and 3 of Chapter 21.2: separating the PHY from the
    // xMII needs the PHY's own counters, and those are behind MDIO.
    need_mdio = (verdict == SRC_PHY_LANES);
  end
endmodule

Classification: a verdict block whose priority order is by measurement strength rather than by predicate specificity.

What it teaches: that this chain's order is defensible where Chapter 21.2 §7's was not, and the difference is worth stating precisely. There, the arms tested different predicates on one object and the order decided a classification. Here each arm is a different measurement — an equivalence probe, a histogram shape, an error rate, a slope — and they genuinely differ in strength. An equivalence failure is a proof; a slope is an inference. Ordering by strength is correct; ordering predicates by specificity was not.

And it teaches that verdict_confident is not the same as verdict != SRC_UNDECIDED. The chain always produces a value; only the first three arms rest on evidence strong enough to act on alone. The alignment-error arm, for instance, is Chapter 21.1 §4's class B — two sites, not one — and reporting SRC_PHY_LANES for it is a shorthand that need_mdio immediately qualifies.

Deliberately simplified: the 90% threshold for SRC_PARSER assumes a parser fault fails essentially every frame, which a fault in one offset path would not. alignment_errors_present collapses Chapter 21.2's whole alignment class into one bit. SRC_UNDECIDED is reached by falling off the end, so it conflates "no evidence" with "contradictory evidence". And slope_reported is passed through unchanged, so a reader has to know Section 5's threshold to interpret it.

Production implication: need_mdio is the output that turns a verdict into a next action, and it is the one most likely to go unimplemented. Separating the PHY's lane alignment from the xMII needs Chapter 21.2 §9's cross-chip comparison — the PHY's symbol count against the MAC's RX_ER count — and that is an MDIO transaction. A monitoring system that does not make MDIO transactions stops at "the PHY or the interface", which is two sites, and that is the ceiling this chapter reaches on such a platform.


16. RTL 8 — The Diagnostic Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// crcdiag_conformance_monitor -- six verdicts about the DIAGNOSIS, and
// four of them say the measurement is not yet worth reading.
// ---------------------------------------------------------------------
module crcdiag_conformance_monitor
  import crcdiag_pkg::*;
#(
  parameter int MIN_ERRORS   = 32,
  parameter int MIN_BUCKETS  = 2
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] c_errors,
  input  logic [31:0] c_frames,
  input  logic        cross_populated,
  input  logic        slope_valid,
  input  logic [31:0] slope_x100,
  input  logic [63:0] residue_tested,
  input  logic        probe_complete,
  input  logic        bursty,
  input  crc_source_e verdict,

  output logic        too_few_errors,
  output logic        one_bucket_only,
  output logic        slope_ambiguous,
  output logic        probe_incomplete,
  output logic        escape_bound_misapplied,
  output logic        verdict_unsupported,
  output logic        diagnosis_sound
);
  assign too_few_errors  = (c_errors < 32'(MIN_ERRORS));
  assign one_bucket_only = !cross_populated;

  // Section 5: between 2.00 and 5.00 the measurement has not decided,
  // and saying so is the block's most useful output.
  assign slope_ambiguous = slope_valid &&
                           (slope_x100 > 32'd200) && (slope_x100 < 32'd500);

  // Section 13: residue 63 is the spill class and a round-number test
  // suite never reaches it.
  assign probe_incomplete = !probe_complete && !residue_tested[63];

  // Section 10 and Section 14's row six: on a small-burst link the
  // detection is CERTAIN and quoting 2^-32 overstates the risk.
  assign escape_bound_misapplied = !bursty;

  assign verdict_unsupported = (verdict != SRC_UNDECIDED) &&
                               (too_few_errors || one_bucket_only);

  assign diagnosis_sound = !verdict_unsupported && !slope_ambiguous;

  always_ff @(posedge clk) begin
    if (rst_n && probe_incomplete)
      $display("[crcdiag] residue 63 untested -- the spill class is the hardest path");
  end
endmodule

Classification: an auditor of a measurement rather than of a design.

What it teaches: that verdict_unsupported is the verdict that protects the chapter from itself. Section 15's chain always produces a value, and on thirty errors spread across one bucket that value is a guess with a slope computed from noise. The monitor's job is to say so — and the two conditions it checks are exactly the two Section 14's rows three, four and five prohibit.

And it teaches that probe_incomplete singles out one residue by name. Sixty-three of sixty-four tested is 98.4% complete and 0% complete on the path that matters: Chapter 19.4's spill class, where the check value crosses a beat boundary, and Chapter 19.3 §20's 23 lengths that a suite of round sizes never produces. A verification suite that is one residue short is one residue short of the hard one, not of an average one.

Deliberately simplified: escape_bound_misapplied asserts on every non-bursty link, which is most of them — it is a reminder rather than a fault, and it should be a report rather than a verdict. MIN_BUCKETS is declared and unused. And diagnosis_sound omits probe_incomplete, so a diagnosis can be sound while the strongest available test has not been run on the path most likely to fail.

Production implication: the omission in diagnosis_sound is the one to argue about. Including probe_incomplete would make almost every real diagnosis unsound, because the equivalence probe needs a reference implementation that shipped silicon does not carry — so the flag would fire always and be ignored always. Leaving it out makes the verdict usable and silently accepts that site 6 is diagnosed statistically. Section 13 is explicit about the trade; the monitor's parameter list is where a team decides whether to accept it.


17. The Five Sites, Separated

Starting from a rising check sequence error counter, which leaves five candidate sites, seven observations separate them completely. The alignment error counter splits the three physical sites, the channel, the physical layer's lane alignment and the media independent interface, from the two logical ones, the parser and the check engine. The port's frame in counter confirms the split, because the physical sites damage frames the port has already counted while the logical ones do not affect that counter at all. The overall error rate identifies the parser, whose offset fault fails essentially every frame. The slope across the size buckets identifies the channel, because only random bit damage has a rate proportional to the frame's length. The residue histogram identifies the check engine, because only a fault in one of its sixty four partial word paths concentrates the failures in a single bin. The clustering test separates a marginal channel, whose errors arrive independently, from an intermittent one or a failing forward error correction block, whose errors arrive together. And finally a management interface transaction compares the physical layer's own symbol error count with the media access controller's, which is the only evidence that separates a lane alignment fault from an interface timing fault. Three register reads, three counter derived statistics, and one management transaction.5 candidatesc_crc_errors risingc_alignment_errorsphysical vs logicalThe error ratenear 100% is theparserThe slope19.86 is the channelThe residuehistograma spike is the engineThe clusteringtestrandom vs intermittentThe MDIOcomparisonPHY vs xMIIOne siteno capture taken12
Figure 4 — seven observations, five sites, and no equipment at any step.

Everything in the chapter, as one procedure over three measurements and two counters that already exist.

StepMeasureSeparates
1c_alignment_errorssites 1, 2, 3 from sites 5, 6
2frames_insites 1, 2, 3 (counted) from 5, 6 (not)
3the error ratesite 5 — a parser fault is near 100%
4Section 4's slopesite 1 (high) from sites 2, 3, 5, 6 (flat)
5Section 7's histogramsite 6 — a comb
6Section 9's clusteringrandom channel from bursty channel
7Chapter 21.2 §9's MDIO comparisonsite 2 from site 3

And the procedure has one property worth stating before the table: every step before the last is a division, and the last is a transaction.

StepInstrumentTime
1 to 3three register readsmicroseconds
4 to 6arithmetic over counters already runningnone — the data is there
7one MDIO transactionmilliseconds

Steps 4 to 6 take no time at all, because the counters have been accumulating since the link came up — the measurement is a division over numbers already collected, and that is why the whole diagnosis can run inside a monitoring system on every port continuously. Chapter 21.1 §12 made the same argument about its first five steps; this chapter extends it to the three measurements that actually name a site.

Steps 1 to 3 are Chapter 21.1's method and cost three register reads. Steps 4 to 6 are this chapter's and cost six counters and sixty-four bins — about 594 and 6 336 flopsand no equipment at all. Step 7 is one MDIO transaction.

The full separation, as a decision table:

EvidenceSite
alignment errors present, frames_in healthy, slope highthe channel
the same, slope high and clusteredthe channel, intermittently or via FEC
alignment errors present, PHY count agreesthe PHY's lanes
alignment errors present, PHY count disagreesthe xMII
no alignment errors, rate near 100%, frames_in healthythe parser's offsets
no alignment errors, rate 1.56%, one residue binthe check engine
no alignment errors, rate 50%, half the binsthe check engine's correction barrel

Seven rows, five sites, and every discriminator is arithmetic over counters. Compare what the chapter started with:

BeforeAfter
candidates51
observations17
equipmenta capture, three hoursnone
added hardware~6 930 flops, 48.9% of the datapath

The last row is the honest cost and it is much larger than Chapter 21.1's or Chapter 21.2's, because the residue histogram is 64 counters. And that is the right place to make a choice: the six size-bucket counters are 594 flops, 4.2%, and buy the channel-versus-logic separation, which is the split that decides whether a maintenance crew is dispatched. The 64 residue bins are 6 336 flops, 44.7%, and buy the check engine's comb — a fault that occurs once in a product's life.

AdditionFlopsShareBuys
six size-bucket error counters~5944.2%channel against logic
sixty-four residue bins~6 33644.7%which residue path
one burst-window counter pair~1981.4%random against clustered

Rows one and three together are 5.6% and separate four of the five sites. Row two separates the fifth and costs eight times as much as the other two combinedwhich is the argument for exporting the residue as a field in a failure log rather than binning it in hardware. One 6-bit field per failing frame, written to the same place a MAC already logs a bad frame, and the histogram is built in software for nothing.

And the whole of Module 21, as one table, because the three chapters have been building one instrument.

FromAddsFlopsSeparates
Chapter 21.1 §19c_filtered, c_vlan_discards, c_fifo_drops, c_desc_errors~297class G's five sites
Chapter 21.2 §19c_unclassified, c_self_inflicted, c_symbol_in_gap, the order pair~426the unnamed shape, our truncation, the earliest warning
this chaptersix size-bucket rates, one burst pair~792channel from logic, random from clustered
this chapter, loggedthe residue, 6 bits per failing frame~0which residue path
total~1 515 — 10.7%all twelve sites

Ten point seven per cent of a datapath separates every fault site in the receive path, and the same twelve sites currently resolve to seven classes with the largest holding five. Chapter 21.1 §8's twelve boundary counters would take the number of observations from seven to four for another 8.4%; this table takes the answer from a class of five to a site for 10.7%. The second is worth more and neither is built.

FlopsWhat it buys
Chapter 19.4's correction barrels5 397 XOR termscorrectness — built
Chapter 19.7's shadow bank992a common reading instant — built
all of Module 21~1 515every fault site named — not built

Module 21's entire instrumentation is one and a half times Chapter 19.7's shadow bank, which was built without argument because a common reading instant is a correctness requirement. The pattern holds across all three chapters and it is the module's closing observation: the thing that decides whether a mechanism ships is not its cost and not its value — it is whether it is a correctness requirement.


18. What the Diagnosis Assumes

Nine assumptions. Three are about the damage, three about the traffic and three about the instrumentation — and the first one is false on every high-rate link.

AssumptionFromIf false
1bit errors are independentSection 4's modelthe slope holds; the escape bound does not
2the damage is inside the protected spanSection 2a valid check over corrupt data
3the traffic populates two size bucketsnothingno slope — cross_populated
4the size buckets mean what Chapter 19.7 §2 saysthe MTUVLAN tags move the top boundary
5the residue modulus is 64Chapter 19.4 §4Chapter 20.6 §6 — it is the beat width
6a parser fault fails essentially every frameSection 15a single-offset fault fails a fraction
7one fault at a timeconvenienceChapter 21.1 §17 — the earlier one masks
8the counters are not read-to-clearthe platformthe second read is a delta of a delta
9the window is long enoughChapter 21.1 §18's row ninea zero that is not a zero

Row one is false on every FEC-protected link and the consequence is asymmetric, which is the useful part. Reed-Solomon FEC either corrects a codeword or fails it entirely, so errors arrive in blocks of thousands of bits and are emphatically not independent.

Under independent bitsUnder failed codewords
Section 4's slopeexactly the length ratiostill high — a longer frame spans more codewords
Section 12's 2^-32 boundcorrectthe only bound there is, and the guarantee is gone
Section 9's clusteringlowhigh

The slope survives the assumption's failure and the escape bound depends on it, which is why Section 4 is the chapter's primary measurement and Section 12 is a caveat. A longer frame spans more codewords in exactly the same proportion as it spans more bits, so the ratio test is robust to the damage's shape; the detection probability is not.

Row five is the assumption Chapter 20.6 spent a chapter on. The residue modulus is the beat width, not the constant 64: at 8 octets per beat there are 8 residue classes, not 64, and Section 7's histogram has 56 permanently empty bins. The comb's spacing is the beat width too — 8 octets rather than 64 — so the diagnostic signature changes shape across the xMII family and the constant does not.

Beat widthResidue classesComb spacingShare of lengths per class
1 octet1none — every length100%
8 octets88 octets12.5%
64 octets6464 octets1.56%
128 octets128128 octets0.78%

At GMII the comb does not exist at all — one residue class, so a check engine fault fails everything and is indistinguishable from a parser fault by this test. The sharpest diagnostic in the chapter is sharpest at the highest rate and absent at the lowest, which is the opposite of the usual relationship and follows directly from the beat width.

And three things deliberately not assumed:

Not assumedWhy not
that a failing check means damageSection 2 — site 6 is the checker
that a passing check means integritySection 2 — the span
that the slope decides how bad the fault isSection 6 — it decides what kind

Row three is the separation the chapter keeps returning to. The slope is constant across four orders of magnitude of channel quality; the absolute rate is what moves. Two numbers, two questions — and a status page that shows one of them is showing the wrong one about half the time.

And one assumption that is not in the table because it is about the reader rather than the design.

Every measurement in this chapter is a population statistic and every instinct about CRC errors is about a frame.

A frame-level questionThe population answer
"was this frame damaged?"the check says yesand by which of five sites, unknown
"is this link bad?"unanswerable from one framethe slope and the rate together
"how big was the damage?"unknowable — the check is one bitthe clustering test infers it
"which frames fail?"the ones that failedthe comb says which lengths

Row three is the one that trips people who reach for a capture. A check sequence's verdict is one bit: it says the octets changed and not how many, not where, and not in what pattern. A capture of a hundred failing frames tells you a hundred times that one bitand the damage's shape, which is what separates a burst from random bits, is not in any of them. Chapter 21.2 §9's PHY symbol counters have it and the frames do not.

Where the shape information is
in the failing framesnowhere — the check is one bit
in the frames' lengthsthe slope and the comb
in the failures' timingthe clustering test
in the PHYthe symbol and FEC counters, over MDIO

Three of the four rows are counters and one is a capture that does not help, which is the chapter's practical thesis and the reason Chapter 21.1 §12 puts a capture at step 6.


19. The Cost, Accounted

The diagnosis is counters. Two of the three measurements are cheap and one is not.

BlockFlopsNature
crcdiag_pkg0two predicted slopes and a modulus
size_rate_cross — 7 × 2 × 32~455the slope's data
length_slope_estimator0combinational
fcs_residue_histogram — 64 × 2 × 32~4 105the comb's data
error_burst_detector~161four counters and a window
fcs_escape_estimator0combinational
crc_equivalence_probe~135two 64-bit masks
crcdiag_telemetry0combinational
crcdiag_conformance_monitor0combinational
total~4 856 flops

Four of the nine blocks are pure combinational logic and one block is 85% of the cost. The residue histogram's 128 counters — sixty-four error bins and sixty-four frame bins — are the whole expense, and Section 17 already argued they should not be counters at all.

FlopsShare of the 14 166-flop datapath
everything except the histogram~7515.3%
the histogram~4 10529.0%
the histogram as a logged field instead6 bits per failing frame~0%

Row three is the design change this chapter argues for and it is almost free. A MAC that logs a bad frame at all already has a place to put a few bits about it; adding the residue is six bits in an existing record. The histogram is then built by whatever reads the log, in software, for nothing — and the diagnostic that costs 29% of a datapath in hardware costs six bits in a log entry.

Module 21's three chapters, together:

ChapterFlops if built in hardwareFlops if logged instead
Chapter 21.1 — four optional counters~297~297 — they must be counters
Chapter 21.2 — four more~426~426
this chapter~4 856~751
total~5 579 — 39.4%~1 474 — 10.4%

Ten point four per cent buys every diagnosis in Module 21, and the difference between the two columns is entirely the decision to bin a value in hardware rather than to log it. Chapter 21.1's counters cannot be logged — they count events that have no frame to attach toand this chapter's histogram is a property of a specific failing frame and can.

Count what has no frame. Log what has one. That single rule takes Module 21's instrumentation from 39.4% of a datapath to 10.4%.


20. Properties Worth Asserting, and One Worth Refusing

Thirty-three properties and eight covers, in four groups: the span, the slope, the comb and the escape bound.

Group one — the span, which is what Section 2 is about.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A check sequence characterises a SPAN. These properties say what
// happens at each end of it and nothing about the frame's truth.
p_check_after_append: assert property (@(posedge clk) disable iff (!rst_n)
                        fcs_checked |-> $past(fcs_appended_upstream));

p_span_ends_here:     assert property (@(posedge clk) disable iff (!rst_n)
                        fcs_checked |-> !fifo_write_done);

p_valid_not_truth:    assert property (@(posedge clk) disable iff (!rst_n)
                        (fcs_ok && tx_memory_corrupt) |-> 1'b1);   // deliberately vacuous

p_mismatch_5_sites:   assert property (@(posedge clk) disable iff (!rst_n)
                        fcs_mismatch |-> (candidate_sites == 12'b0000_0011_0111));

p_engine_in_span:     assert property (@(posedge clk) disable iff (!rst_n)
                        engine_faulty |-> fcs_mismatch_possible_without_damage);

p_post_check_unprot:  assert property (@(posedge clk) disable iff (!rst_n)
                        fifo_write_done |-> !fcs_protects_from_here);

p_hop_scoped:         assert property (@(posedge clk) disable iff (!rst_n)
                        switch_regenerates_fcs |-> new_span_begins);

p_probe_needs_ref:    assert property (@(posedge clk) disable iff (!rst_n)
                        probe_en |-> reference_present);

Group two — the slope.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_rate_not_count:     assert property (@(posedge clk) disable iff (!rst_n)
                        slope_valid |-> (rate_ppm_in_bucket[hi_bucket] != 32'd0));

p_slope_needs_two:    assert property (@(posedge clk) disable iff (!rst_n)
                        slope_valid |-> cross_populated);

p_slope_needs_errors: assert property (@(posedge clk) disable iff (!rst_n)
                        slope_valid |-> (c_errors >= 32'd32));

p_channel_excl_logic: assert property (@(posedge clk) disable iff (!rst_n)
                        !(looks_like_channel && looks_like_logic));

p_gap_is_undecided:   assert property (@(posedge clk) disable iff (!rst_n)
                        (slope_valid && slope_x100 > 32'd200 && slope_x100 < 32'd500)
                          |-> slope_ambiguous);

p_buckets_ordered:    assert property (@(posedge clk) disable iff (!rst_n)
                        slope_valid |-> (hi_bucket > lo_bucket));

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

p_one_bucket_flagged: assert property (@(posedge clk) disable iff (!rst_n)
                        !cross_populated |-> one_bucket_only);

p_slope_stable:       assert property (@(posedge clk) disable iff (!rst_n)
                        ($stable(rate_ppm_in_bucket) && $stable(c_errors_in_bucket))
                          |-> $stable(slope_x100));

Group three — the comb.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p_residue_range:      assert property (@(posedge clk) disable iff (!rst_n)
                        frame_end |-> (residue < 6'(N_RESIDUES)));

p_residue_is_len:     assert property (@(posedge clk) disable iff (!rst_n)
                        frame_end |-> (residue == 6'((wire_len - 14'd4) % 14'd64)));

p_bins_sum:           assert property (@(posedge clk) disable iff (!rst_n)
                        (c_by_residue.sum() == total_errors));

p_peak_is_max:        assert property (@(posedge clk) disable iff (!rst_n)
                        (c_by_residue[peak_residue] == peak_count));

p_comb_needs_peak:    assert property (@(posedge clk) disable iff (!rst_n)
                        comb_detected |-> (peak_count > (total_errors >> 1)));

p_comb_needs_data:    assert property (@(posedge clk) disable iff (!rst_n)
                        comb_detected |-> (total_errors > 32'd256));

p_probe_marks:        assert property (@(posedge clk) disable iff (!rst_n)
                        (probe_en && probe_frame_end) |=>
                          residue_tested[$past(probe_residue)]);

p_probe_63:           assert property (@(posedge clk) disable iff (!rst_n)
                        probe_complete |-> residue_tested[63]);

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

Group four — the escape bound and the verdict.

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

p_small_burst_certain:assert property (@(posedge clk) disable iff (!rst_n)
                        (burst_bits <= 32) |-> fcs_mismatch);

p_odd_errors_caught:  assert property (@(posedge clk) disable iff (!rst_n)
                        (damaged_bits[0] == 1'b1) |-> fcs_mismatch);

p_single_bit_caught:  assert property (@(posedge clk) disable iff (!rst_n)
                        (n_damaged_bits == 32'd1) |-> fcs_mismatch);

p_verdict_supported:  assert property (@(posedge clk) disable iff (!rst_n)
                        (verdict != SRC_UNDECIDED) |-> !verdict_unsupported);

p_confident_needs_ev: assert property (@(posedge clk) disable iff (!rst_n)
                        verdict_confident |->
                          (engine_faulty || comb_detected || slope_valid));

p_mdio_for_phy:       assert property (@(posedge clk) disable iff (!rst_n)
                        (verdict == SRC_PHY_LANES) |-> need_mdio);

p_sound_excludes:     assert property (@(posedge clk) disable iff (!rst_n)
                        diagnosis_sound |-> (!verdict_unsupported && !slope_ambiguous));

p_parser_is_total:    assert property (@(posedge clk) disable iff (!rst_n)
                        (verdict == SRC_PARSER) |-> (error_rate_ppm > 16'd900000));

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_slope_high:      cover property (@(posedge clk) looks_like_channel);
c_slope_flat:      cover property (@(posedge clk) looks_like_logic);
c_slope_middle:    cover property (@(posedge clk) slope_ambiguous);
c_comb:            cover property (@(posedge clk) comb_detected);
c_residue_63:      cover property (@(posedge clk) residue == 6'd63 && fcs_mismatch);
c_clustered:       cover property (@(posedge clk) clustered);
c_probe_all_64:    cover property (@(posedge clk) probe_complete);
c_every_verdict:   cover property (@(posedge clk) verdicts_seen == 7'h7F);

21. Verification Scenarios

Fifty-eight scenarios, plus a five-run directed test that a random generator cannot produce because its variable is a fault mechanism.

The span — 9 scenarios.

#ScenarioExpected
1a frame corrupted before the appendthe check passes; the data is wrong
2a frame corrupted on the wirethe check fails
3a frame corrupted after the checkthe check passed; the data is wrong
4a check engine that inverts its resultevery frame fails; none was damaged
5a switch that regenerates the FCSa new span begins
6a bit flipped in that switch's buffervalid check over corrupt data
7N hopsN independent spans, N times the escape rate
8a frame that never reaches the checkno verdict — not a pass
9the probe with no reference presentprobe_en must not assert

The slope — 12 scenarios.

#ScenarioExpected
10BER 10⁻¹⁰, mixed sizesslope ≈ 19.86; looks_like_channel
11BER 10⁻⁸, the same mixslope ≈ 19.86 — unchanged
12BER 10⁻¹², the same mixslope ≈ 19.86, too few errors
13a lane-deskew faultslope ≈ 1.00; looks_like_logic
14a parser offset faultslope ≈ 1.00; rate near 100%
15a single residue path faultslope ≈ 1.00; rate 1.56%
16all 1 518-octet framesone bucket; !cross_populated
17all 64-octet framesone bucket; no slope
18two buckets, 8 errorstoo_few_errors
19two buckets, 40 errorsa slope, and it may be noisy
20slope of 3.5slope_ambiguous — no verdict
21buckets 1 and 4 chosenpredicted 7.99, not 19.86 — Section 5's defect

The comb — 11 scenarios.

#ScenarioExpected
22a fault in residue path 17all errors in bin 17
23the failing lengthsL ≡ 21 (mod 64)
24how many legal lengths23 of 1 455 — 1.56%
25comb_detectedasserts above 256 errors
26a barrel control fault32 bins high, 32 empty
27comb_detected on thatdoes NOT assert — no single peak
28a chi-squared test on thatdetects it
29a fixed-size stream, healthyone bin, all frames, no errors
30a fixed-size stream, channel faultone bin, and it looks like a comb
31the rate per bin instead of the countuniform — the fix for scenario 30
32a fault in residue 63 onlythe spill class; a round-size suite misses it

Bursts and escapes — 10 scenarios.

#ScenarioExpected
33independent bit errorsclustered low
34one failed FEC codewordseveral consecutive frames fail
35clustered on thatasserts
36slope on thatstill high — it is still the channel
37an 8-bit burstalways detected — no escape
38a 32-bit burstalways detected
39a 33-bit burstdetected with probability 1 − 2^-32
40an odd number of flipped bitsalways detected
41escape_bound_misapplied on a 1 Gb/s linkasserts — detection is certain there
42the escape MTBF at 100 Gb/s, BER 10⁻¹⁰13.8 years

The verdict — 8 scenarios.

#ScenarioExpected
43engine_faulty setSRC_CRC_LOGIC, confident
44comb, no probeSRC_CRC_LOGIC, confident
45rate 95%, frames_in healthySRC_PARSER
46alignment errors presentSRC_PHY_LANES, need_mdio
47slope high, clusteredSRC_CHANNEL_BURST
48slope high, not clusteredSRC_CHANNEL_RANDOM
4920 errors, one bucketSRC_UNDECIDED, verdict_unsupported
50slope 3.5 and no combSRC_UNDECIDED

The probe and the cost — 8 scenarios.

#ScenarioExpected
5164 probe frames, one per residueprobe_complete
5263 of them, missing residue 63probe_incomplete, and a $display
53a round-number size suitereaches 2 of 64 residues — 3.1%
54engine_faulty then resetclears; sticky within a run
55the histogram in hardware~4 105 flops, 29.0%
56the residue as a 6-bit log fieldeffectively free
57the size-bucket cross only~594 flops, 4.2%, four of five sites
58at 8 octets per beat8 residue classes; 56 bins dead

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

The case: one link, five fault mechanisms, and a diagnosis that must name a different site each time using only counters.

A constrained-random generator cannot reach this, because the variable is not a field of any frame — it is which component is broken. Each run needs a different injected defect, and three of the five are inside the design rather than on the wire: Chapter 20.5 §10's split, arriving in a diagnostic chapter.

RunThe injected faultWhere it lives
Arandom bit errors at BER 10⁻⁸the wire — an impairment
Bone FEC codeword in 10⁶ uncorrectablethe wire — a patterned impairment
Ca lane-deskew error in the PHY modelthe PHY model
Da stuck bit in the parser's offsetinside the design
Ea wrong constant in residue path 17inside the design

The oracle is four-part and no part of it requires knowing which run is which.

PartABCDE
slope19.86high1.001.001.00
clusteringlowHIGHlowlowlow
error rate1.2 × 10⁻⁴~10⁻⁶high~100%1.56%
residue histogramuniformuniformuniformuniformONE BIN

Every column is distinct and no two rows are needed to separate all five, which is the test's point: four measurements, five faults, and a unique signature for each. Runs A and B differ only in clustering; C and D differ only in rate; D and E differ only in rate and histogram. Remove any one row and two runs collapse.

Runs D and E are the two that need a fault injected into the design rather than onto the wire, which is Chapter 20.5 §10's category and is why this test belongs to a verification environment rather than to a lab bench. Runs A, B and C can be produced with a channel impairment and a PHY model; D and E need a parameter in the RTL.

And run E is the one that would never be run. A wrong constant in one of sixty-four residue paths fails 1.56% of production frames, passes every conformance suite built from round frame sizes, and is exactly what Section 13's equivalence probe exists to findone frame per residue, sixty-four frames, and the thing is settled.


22. Debugging a CRC Diagnosis

Six complaints.

Complaint 1 — "the slope says channel but we replaced the cable and nothing changed."

CheckIf yesMeaning
is the link FEC-protected?yesthe channel's errors are corrected — Section 10
is the PHY's uncorrected-codeword count moving?nothen the channel is fine
is the slope still high after the swap?yesit is not the cable
is the damage inside the PHY, past the FEC?likelya lane, an internal path, a SerDes

Row four is the one the slope cannot see and it is the chapter's main blind spot. The slope separates length-dependent damage from per-frame damage; it does not say where the length-dependent damage happens. A fault inside the PHY that damages bits — after the FEC and before the xMII — produces a perfect channel signature and is not the channel. The separating evidence is the PHY's own counters, over MDIO.

Complaint 2 — "the histogram shows a comb but the engine is fine."

CheckIf yesMeaning
is the traffic one frame size?yesone residue bin, always — scenario 30
is c_frames_by_residue uniform?nothe histogram is a picture of the traffic
compute the rate per binuniformno comb at all
cross_populated?irrelevant herethis is a residue problem, not a size one

The comb test measures a count where it should measure a rate, and Section 7 names this as a simplification. A 1 518-octet fixed-size stream puts every frame in residue bin 42(1518 − 4) mod 64so every error lands there too and comb_detected fires on a channel fault. Dividing by c_frames_by_residue removes it entirely.

Complaint 3 — "the slope is 3.5 and we need an answer."

CheckIf yesMeaning
how many errors?40barely above the floor
which buckets?1 and 4predicted channel slope is 7.99, not 19.86
is 3.5 nearer 7.99 or 1.00?nearer 1.00but not by much
wait for more traffic?yesthe only correct answer

Row two is Section 5's known defect and it changes the reading. The threshold of 5.00 was chosen against a predicted 19.86; with buckets 1 and 4 the prediction is 7.99 and the midpoint is about 4.5. A slope of 3.5 is then weak evidence for logic, not the strong evidence the fixed threshold implies — and the fix is to compute the prediction from the buckets in use.

Complaint 4 — "the FCS error rate doubled but the link is unchanged."

CheckIf yesMeaning
did the traffic's size mix change?yesthe rate follows the mix
did the slope change?nothe fault is the same fault
did the per-bucket rates change?nonothing about the link moved
what changed?more large framesand large frames fail 23.7× as often

An aggregate error rate is a weighted average over the size distribution, so a traffic mix that shifts towards large frames doubles the reported rate with no change in the channel at all. The per-bucket rates are invariant under that change and the aggregate is not — which is the same argument Chapter 20.4 §19 made about denominators, in a different vocabulary.

Complaint 5 — "we see corruption in host memory and the FCS never fails."

CheckIf yesMeaning
is the corruption after the check point?likelySection 2 — outside the span
how many hops?threethree spans, none end-to-end
does any switch store and forward?yesits buffer is unprotected
is the escape bound plausible?13.8 years per portso it is not an escape

Row four is the arithmetic that rules out the explanation everybody reaches for. An undetected error is once every 13.8 years per port; corruption seen weekly is not a 2^-32 event. It is unprotected memory somewhere in the path — a switch's buffer, a DMA, a host page — and Section 2's span table says which stages those are.

Complaint 6 — "the equivalence probe passes and frames still fail."

CheckIf yesMeaning
probe_complete?nosome residue was not tested
residue_tested[63]?nothe spill class, and it is the hard one
what sizes did the probe use?round numberswhich reach two residues, not sixty-four
add one frame of 1 475 octetsresidue 63 is coveredChapter 19.3 §20's size

Sixty-three of sixty-four is 98.4% and zero per cent of the path that matters — and a round-number suite does far worse than that.

SizeResidue (L − 4) mod 64
6460
12860
25660
51260
1 02460
1 51842

Six sizes, two residues. The five powers of two all land on residue 60 because they are congruent modulo 64, so the classic suite exercises 2 of the engine's 64 paths — 3.1% — and reaches residue 63 never. The 23 lengths that do are 67, 131, 195 and so on to 1 475, and no test suite anybody writes contains one.

Complaint 7 — "the residue histogram is completely flat and the engine is definitely broken."

CheckIf yesMeaning
is the error rate about 50%?yesa barrel control fault — Section 8
are exactly half the bins high?look againflat and bimodal look alike on a bar chart
sort the bins by valuetwo clustersthe finding
is comb_detected low?yes, correctlyit tests for one peak, not two clusters

A correction-barrel fault affects every residue whose binary representation has a particular bit set, which is thirty-two of sixty-four — so the histogram is two flat plateaus rather than a comb, and a test looking for a single spike reports nothing. Sorting the bins makes it obvious in one step, and a chi-squared statistic over the 64 bins detects both shapes. The bins are already exported; the test belongs in software.

Complaint 8 — "the diagnosis says the check engine and we replaced the whole ASIC."

CheckIf yesMeaning
did the new part behave the same?yesit is not a manufacturing defect
is it the same RTL?yesso it is a logic bug, in every part ever made
which residue?63the spill class
does a firmware path exist?sometimesa cut-through disable, an MTU change

A fault in one residue path is a design bug and not a part bug, so every device from that mask set has it. Replacing the part changes nothing, and the useful responses are a workaround — avoid the 23 frame lengths, which usually means an MTU changeor an erratum. Section 8's comb is the evidence that distinguishes a design bug from a defective unit, and it is the only evidence available without a lab.

And the three symptoms this chapter is systematically blamed for:

SymptomBlamed onUsually is
FCS errors on a good cablethe cablefour sites inside the chip
a rate that changed with no causethe linkthe traffic's size mix
corruption with a valid checkthe check sequencea stage outside its span

23. Misconceptions

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

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

What it costs: c_crc_errors moves for five sites and four of them are inside the chip — the PHY's lane alignment, the xMII, the parser's offsets and the check engine's own logic. Replacing a cable is right about one time in five, and the four wrong times each consume a maintenance window. On a FEC-protected link it is worse than one in five, because the channel's errors are corrected before the MAC sees them.

The corrected model: a failing check narrows the space to five and the slope narrows it to one. Section 4's ratio is 19.86 for the channel and 1.00 for every logic fault, a factor of twenty, measurable on counters that already exist.

Misconception 2 — "a valid check sequence means the frame is correct."

The wrong model: the FCS is an integrity guarantee, so a frame that passes it is intact.

What it costs: the span begins at Chapter 19.3 §4's append point and ends at the check. Eight of the twelve stages in the path are outside it — the host's memory, the transmit DMA, the receive FIFO, the receive DMA, host memory — and a switch that regenerates the check sequence starts a new span at every hop. A bit flipped in a store-and-forward switch's buffer leaves with a perfectly valid check sequence over corrupt data.

The corrected model: a matching check says these octets are the octets that were present when the value was appended. Not that they were right then; not that they survive afterwards. Section 2's table is which stages are covered and which are not.

Misconception 3 — "undetected errors are why we see corruption."

The wrong model: CRC-32 escapes at 2^-32, so some corruption is inevitable and unexplained corruption is that.

What it costs: the arithmetic does not support it. At 100 Gb/s, 1 518-octet frames, a bit error rate of 10⁻¹⁰: one escape every 13.8 years per port. Corruption seen weekly is not a 2^-32 event, and attributing it to one stops the investigation at the only explanation that requires no action.

The corrected model: compute the escape rate before invoking it. It rises with the error rate and with the fleet size — ten thousand ports is twelve hours — and it does not rise with the traffic's frame size at all, because the two size effects cancel. When the number does not support the explanation, the corruption is in an unprotected stage.

Misconception 4 — "a bigger slope means a worse channel."

The wrong model: the slope is a measure of the fault's severity.

What it costs: the slope is 23.72 at a bit error rate of 10⁻¹² and 23.72 at 10⁻⁸ — a factor of ten thousand in link quality, identical slope. It says what kind of fault; it says nothing about how bad. A team that escalates on the slope escalates on a number that does not move with severity, and one that ignores the absolute rate ignores the only number that does.

The corrected model: two numbers. The slope decides channel-or-logic; the absolute rate in any bucket decides how urgent. Together they are a diagnosis and a priority, and neither alone is either.

Misconception 5 — "CRC-32 catches all burst errors up to 32 bits, so bursts are fine."

The wrong model: the burst guarantee covers the damage a real link produces.

What it costs: it did at 1 Gb/s, where 8B/10B damage is eight to ten bits. At 25 Gb/s and above, Reed-Solomon FEC either corrects a codeword or fails it entirely, and a failed codeword is damage across thousands of bits — far outside the guarantee, where detection falls back to 1 − 2^-32. The damage mode a high-rate link produces is exactly the one the guarantee does not cover.

The corrected model: the guarantee is a function of the damage's shape, and the shape is set by the line coding. Section 9's clustering test says which regime a link is in, and the escape bound applies only in one of them.

Misconception 6 — "our conformance suite covers the check engine."

The wrong model: a suite of 64, 128, 256, 512, 1 024 and 1 518 octets exercises the frame sizes that matter.

What it costs: those six sizes reach two of Chapter 19.4 §4's sixty-four residue paths — 3.1% — because the five powers of two are all congruent modulo 64 and land on residue 60. Residue 63, the spill class where the check value crosses a beat boundary, is the engine's hardest path and is reached by none of them. A fault there fails 1.56% of production frames forever.

The corrected model: cover the residues, not the sizes. Sixty-four frames, one per class, is Chapter 19.4 §14's equivalence check run to completion — and Chapter 20.1 §15's residues_count == 64 is the coverage goal that says so.

And the same argument one step further, because it explains why the goal was written in the first place.

Frames needed
six round sizes6, reaching 2 residues
a uniform sweep to all 64 residuesChapter 20.1 §6's 304 frames
the same with a 50% weight on the spill set172
the coupon-collection ceiling86

Three hundred and four frames reaches every residue path in the engine, which is a fraction of a second of traffic at any rate — and the six-size suite that reaches two has been the industry default for thirty years. The goal in Chapter 20.1 §15's five-bit list exists because somebody counted the paths and then counted what a conventional suite reaches, and the gap between 64 and 2 is the entire justification.


24. Interview Questions

Question 1 — "The CRC error counter is climbing. Where do you look?"

What the answer should establish: five places, and four of them are inside the chip. A strong answer immediately narrows: c_alignment_errors separates the three physical sites from the two logical ones, and frames_in separates those counted at the port from those that were not. The strongest answer names the measurement that finishes the job: cross the errors with the RMON size histogram and read the slope — 19.86 for the channel, 1.00 for anything else.

Question 2 — "Why is the error rate proportional to frame length for a channel fault?"

What the answer should establish: each bit is an independent opportunity to fail, so a frame of L octets fails with probability about 8·L·p. The ratio between 1 518 and 64 octets is exactly 23.72 — the length ratio — at every bit error rate, which is what makes it usable: you do not need to know p. A strong answer contrasts: every logic fault is per-frame and predicts 1.00, a factor of twenty away.

Question 3 — "What does a valid frame check sequence guarantee?"

What the answer should establish: a span, not a frame. These octets are the octets that were present at the transmitter's append point; nothing about whether they were right then, and nothing about what happens after the check. A strong answer names what is outside: the host's memory, both DMA paths, the receive FIFO — eight of twelve stagesand a new span at every store-and-forward hop. The strongest answer draws the operational conclusion: end-to-end integrity needs an end-to-end check.

Question 4 — "How often does a corrupted frame get through with a valid check?"

What the answer should establish: frames per second, times the probability the frame is damaged, times 2^-32. At 100 Gb/s with 1 518-octet frames and a bit error rate of 10⁻¹⁰ that is one every 13.8 years per port. A strong answer notes what moves it: a hundred times worse channel is fifty days; ten thousand ports is twelve hours; the frame size does not move it at all, because the two size effects cancel exactly.

Question 5 — "Your check engine has a fault in one residue path. What do you see?"

What the answer should establish: a comb. Chapter 19.4 §4's residue is (L − 4) mod 64, so the failing frames are those whose length is congruent to a fixed value modulo 6423 of the 1 455 legal sizes, evenly spaced, 1.56% of uniform traffic. A strong answer says why it survives: a round-number test suite reaches two residues of sixty-four, so the fault passes conformance and fails in production forever.

Question 6 — "Which of the five sites can you test directly?"

What the answer should establish: only the check engine, and only with a reference implementation present. Chapter 19.4 §14's equivalence checker against the shipped engine, one frame per residue class, sixty-four frames, and site 6 is settled with no statistics at all. A strong answer notes the catch: shipped silicon does not carry a second engine, so in the field site 6 is diagnosed by Section 7's histogram — 64 counters instead of 8 512 XOR terms, which is the trade the whole chapter rests on.


25. Questions and Answers


26. What's Next

Module 21 has six chapters left and this one has set the pattern for all of them: take a class from Chapter 21.2's table, find the measurements that separate its causes, and price them.

ChapterTakesThe separation it needs
Chapter 21.4a 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 Chapter 21.1 could not separate
Chapter 21.7the DMA pathChapter 19.6's descriptors
Chapter 21.8a healthy link that is slowno errors at all, which is a different problem
Chapter 21.9the capturestep 6, and what it cannot show

Chapter 21.4 is the interesting inversion. Every measurement in this chapter is a statistic over frames; a link that will not come up has no frames, so the whole apparatus of slopes, combs and clustering is unavailable. The evidence there is the PHY's state machine and the counters behind MDIOChapter 4.5which is the one source this chapter kept needing and kept noting was unread.

Chapter 21.8 is the other inversion and the harder one. A link with no errors that is nonetheless slow has no error counter to read at all; the evidence is Chapter 8.3's efficiency arithmetic, Chapter 19.5's occupancy and Chapter 19.6's outstanding countsa completely different instrument set for a complaint that arrives through the same ticket queue.

And the series is now ninety-six classes long. Chapter 20.2 §8's six-group taxonomy has taken four extensions in six chapters, and the last four — 93, 94, 95 and 96 — form the eighth group Chapter 21.2 §26 named: a property bound to the wrong side of a function. A width at a seam; a search against its sensors; a classification's output against its input; and now a detector's verdict against the object rather than the span. Four members in four consecutive chapters is not a coincidence — it is what happens when a track stops writing properties about a design and starts writing them about the instruments that observe one.


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.