Skip to content
VLSI Mentor

Ethernet · Module 21

Packet Capture Analysis

A capture reaches 14 of Module 21's 57 faults, holds 84 milliseconds per gigabyte, and drops 12% of them itself — and it is still the only instrument that shows relationships between frames.

Every chapter in Module 21 put the capture last and none of them used it. This one says why, and what it is actually for.

Value
faults Module 21's six enumerating chapters named57
a wire capture reaches outright14 — 24.6%
partially, needing a second observation9 — 15.8%
cannot reach at all34 — 59.6%
frames in a 1 GB capture, minimum size1.25 × 10⁷
frames a counter has seen in a week9.00 × 10¹³
the ratio7.2 million

Row seven is Chapter 21.1 §12's "three hours for about a bit" with a number attached. A capture's sample is 7.2 million times smaller than the counters already running on the port, and it costs three hours and a maintenance window while they cost nothing.

And the apparatus drops frames of its own.

At 100 Gb/s
arrival rate12.50 GB/s
a sustained NVMe write3.00 GB/s — 24.0% of line
an 8 GB ring bufferfull after 842 ms
fraction of a 1-second capture the apparatus drops12.0%

That is Chapter 19.5's buffer problem one level up, with one difference that matters more than the arithmetic: the capture's own drops are indistinguishable from the link's. A capture taken to diagnose Chapter 21.6's silent drops silently drops frames.

What it does show, uniquely, is two things.

Which no counter has
contentsevery octet — and Chapter 21.7 §8's three ordering failures corrupt contents
relationships between framesorder, spacing, and pairing, all of which a counter aggregates away

And both come with a condition. Contents diagnose Chapter 21.7's group only when compared against a second observation of the same frames at the destination — which is Chapter 20.6's join problem arriving in a data centre instead of a testbench.


1. Scope, and Eight Chapters That Deferred to This One

Scope: what a packet capture observes, which of Module 21's faults that reaches, what it costs, and what the apparatus does to the thing it is measuring.

Not in scope: the faults themselves. Every one has a chapterChapter 21.1 through Chapter 21.8and this chapter is about an instrument rather than about a failure. That makes it the only chapter in Module 21 whose subject is a tool, and the reason it comes last is that eight chapters had to establish what the tool is competing with.

Start with what those eight said about it.

ChapterSaid
Chapter 21.1 §12step 6 — three hours for an observation worth about one bit
Chapter 21.3 §18a hundred failing frames report the same one bit a hundred times
Chapter 21.4 §1on a down link there are no frames to capture
Chapter 21.6 §1the frames in question did not arrive anywhere
Chapter 21.7 §8three failures deliver every frame and corrupt the contents
Chapter 21.8 §14there is no event to count and none to capture

Five of those six are reasons a capture will not help and the sixth — Chapter 21.7 §8 — is the one place in the module where no counter can help either. That asymmetry is the chapter's whole content: the capture is the wrong instrument for almost everything Module 21 diagnoses and the only instrument for one specific group.

Chapter 21.3 §18's argument is worth restating exactly, because it is the sharpest.

A check sequence's verdict is one bit. A capture of a hundred failing frames tells you that one bit a hundred times, and the damage's shape — which is what separates a burst from random bit errors — is not in any of them.

So a capture's value cannot be "it shows you the frames." It has to be something the frames carry that a counter discards, and Sections 6 and 8 are the two candidates.

Three things this chapter is and is not.

Choice
it counts faults, not features57 of them, from six chapters that enumerated
it prices the apparatusincluding the frames the apparatus itself loses
it treats the capture as part of the systembecause a span port adds load and a tap adds loss

Row three is the chapter's structural claim and Section 20's rejected property. A capture is not an outside observer: a mirroring port consumes fabric bandwidth, an inline tap inserts loss, and a capture host's storage is slower than the wire. The instrument is inside the system it measures, and a property that assumes otherwise records the instrument's limits as the subject's.


2. What a Capture Point Actually Observes

A frame travels from the far end across a channel, through our physical layer device, through the media independent interface, and into our media access control block and then the host. Three capture arrangements observe it from different places. A passive optical tap sits on the fibre before our physical layer, so it records the light as it arrived and misses everything our physical layer does to it, and it inserts optical loss of roughly three decibels for a fifty fifty split, against a channel insertion loss allowance of under two decibels for a hundred gigabit short reach link over a hundred metres of OM4 fibre. A mirrored or span port sits inside the switch and records frames after the physical layer and after the switch's parser, so it shows what our device made of the light, and it perturbs by adding a full copy of the traffic to a real fabric where the mirror destination is usually smaller than the source. An inline device sees everything and adds latency and a new failure point. Below the frame, and invisible to all three, sit the physical coding sublayer's symbol errors, the forward error correction decoder's per codeword symbol correction counts, block lock state, and lane skew. Above the frame, and equally invisible, sit the host's descriptor ownership bits and ring indices in main memory.The far endframes on the fibrePassive opticaltapbefore our PHY — costs3 dBOur PHYFEC repairs hereBelow the framesymbols, codewords,lockSpan portafter the parser —adds loadOur MAC19.7's 27 countersInline devicesees all, adds afailureThe hostdescriptors, ringindicesAbove the framein memory, on no wireThe capture fileframes only12
Figure 1 — three places to stand, and each one trades what it sees against what it disturbs.

A capture records frames. Enumerating precisely what that gives is the first half of deciding what it is for.

ObservableAvailable?From
every octet of every captured frameyesthe frame itself
the frame's lengthyesthe capture
the arrival timestampyes, to the capture clock's resolutionthe apparatus
the inter-frame spacingderived from two timestampsthe apparatus
the order of framesyes, at this pointthe capture
the check sequence's verdictrecomputable from the octetsthe frame
trailing bits — Chapter 7.3 §7's countusually NOTdiscarded by most capture paths
the interframe gap in octetsNOT — only a timestamp differencethe apparatus
symbol errorsNOT — a PCS event below the framethe PHY

Rows seven, eight and nine are the ones people assume and do not get. A capture is a record of frames, and three of the things Module 21's chapters depend on are not frames: Chapter 21.2 §9's symbol errors in the interframe gap, Chapter 7.3 §7's trailing-bit count, and Chapter 5.9's gap in octets — which Chapter 21.8 §13 needed and which a timestamp difference approximates badly at 100 Gb/s.

And the capture point matters as much as the capture.

PointSeesMisses
a passive optical tapthe light on the fibre, before our PHYanything our PHY does to it
a mirrored (span) portframes after our PHY and after the switch's parserwhatever the PHY corrected or mangled
an inline deviceeverything, and adds latency and a failure point

Row one against row two is the difference that decides Chapter 21.1's sites 2 and 3. A passive tap shows what arrived; a span port shows what our PHY made of itso the two disagree exactly when the PHY or the xMII is at fault, which is the pair Chapter 21.4 §6 found fused under every MDIO reading.

Two capture points, one on each side of the PHY, separate a lane-alignment fault from a channel fault — and almost nobody has both.

Row two also has a property row one does not: it perturbs.

A passive tapA span port
adds load to the fabricnoyes — the mirrored copy is real traffic
can itself dropnoyes, when the mirror destination is oversubscribed
inserts optical lossyes, a few dBno
is in the failure pathnono
shows the frames the switch discardedno — it is upstreamdepends where the mirror is taken

Row five is the one that matters for Chapter 21.6's class G. A mirror taken at ingress shows frames that were subsequently dropped; one taken at egress does not. So the same instrument answers or fails to answer the module's hardest question depending on a configuration line, and the line is rarely documented.


3. RTL 1 — The Capture Package and the Observable Set

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capdiag_pkg -- what a capture point observes, what it costs, and the
// one structural fact: the apparatus is inside the system it measures.
//
// Chapter 19.5's receive FIFO had an arrival rate bounded by the line
// rate and a drain rate above it, so it emptied. A capture's sink is
// BELOW the line rate, so it does not -- and Section 10 is that
// arithmetic run the other way.
// ---------------------------------------------------------------------
package capdiag_pkg;

  // Section 2's table, as a set. Three of the nine are not frames and
  // are therefore not in a capture at all.
  typedef enum logic [3:0] {
    OBS_OCTETS     = 4'd0,
    OBS_LENGTH     = 4'd1,
    OBS_TIMESTAMP  = 4'd2,
    OBS_SPACING    = 4'd3,   // derived from two timestamps
    OBS_ORDER      = 4'd4,
    OBS_FCS_VERDICT= 4'd5,   // recomputable from the octets
    OBS_TRAILING   = 4'd6,   // usually discarded
    OBS_GAP_OCTETS = 4'd7,   // NOT available -- a timestamp is not a gap
    OBS_SYMBOL     = 4'd8    // NOT available -- below the frame
  } observable_e;

  localparam logic [8:0] IN_A_CAPTURE   = 9'b0_0011_1111;
  localparam logic [8:0] NOT_IN_CAPTURE = 9'b1_1100_0000;

  typedef enum logic [1:0] {
    PT_TAP    = 2'd0,   // passive, upstream of our PHY
    PT_SPAN   = 2'd1,   // mirrored, downstream of it, and it perturbs
    PT_INLINE = 2'd2
  } capture_point_e;

  // Chapter 19.5 Section 3's arithmetic, inverted. At 100 Gb/s the
  // wire delivers 12.5 GB/s and a sink does not.
  localparam int LINE_MBPS      = 100000;
  localparam int WIRE_MBYTES_S  = 12500;
  localparam int SINK_MBYTES_S  = 3000;    // a sustained NVMe write
  localparam int BUFFER_MBYTES  = 8000;

  // Section 10: the buffer lasts BUFFER / (arrival - drain).
  function automatic int buffer_ms(int buf_mb, int arr_mbs, int sink_mbs);
    if (sink_mbs >= arr_mbs) return 1000000;    // never overflows
    return (buf_mb * 1000) / (arr_mbs - sink_mbs);
  endfunction

  // Section 12: a filter on a fault of rate 1-in-N multiplies the
  // window a fixed store covers by N.
  function automatic int window_gain(int one_in_n);
    return one_in_n;
  endfunction

  // Section 4's verdicts.
  typedef enum logic [1:0] {
    REACH_YES     = 2'd0,   // the evidence is a property of the frames
    REACH_PARTIAL = 2'd1,   // needs a second point, or a lost field
    REACH_NO      = 2'd2
  } reach_e;

endpackage

Classification: a package whose two constants are a rate the apparatus cannot keep up with and a mask of three things a capture does not contain.

What it teaches: that NOT_IN_CAPTURE has three members and all three are load-bearing elsewhere in Module 21. Chapter 21.2 §9 built a whole section on symbol errors in the interframe gap — the earliest channel warning available — and they are not frames. Chapter 7.3 §7's trailing-bit count decides the alignment class and most capture paths discard it. Chapter 21.8 §13's gap in octets is the only evidence about the offered load and a capture gives a timestamp difference instead.

And it teaches that SINK_MBYTES_S being below WIRE_MBYTES_S is the chapter's structural fact rather than a configuration detail. Chapter 19.5 §4 established that the MAC's receive FIFO empties because its drain rate exceeds the line rate; the capture's does not, so its buffer is a countdown rather than a cushion.

Deliberately simplified: SINK_MBYTES_S at 3 000 is one plausible storage path and the range in practice is an order of magnitude either way. window_gain is the identity function, which is correct and is written out because the result is surprising: filtering on a 1-in-10⁶ fault makes a 1 GB store cover 22 hours instead of 80 milliseconds. IN_A_CAPTURE is a literal rather than derived. And capture_point_e has no representation of where a span port is mirrored from, which Section 2 showed decides whether it sees dropped frames.

Production implication: the missing mirror-point representation is the configuration question to ask first and it is one line in a switch's configuration. A mirror taken at ingress records the frames the switch subsequently discarded; one taken at egress records only what survived. For Chapter 21.6's class G — five sites whose entire symptom is a frame that vanished — that single line decides whether the capture contains the evidence or provably cannot. It is worth more than the choice of capture tool.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// observable_set -- given a capture point, say what is recorded and
// what is not, and refuse to answer questions about the rest.
//
// The block's purpose is to make an ABSENCE explicit. A capture that
// silently lacks the trailing-bit count produces an alignment
// diagnosis that cannot be right.
// ---------------------------------------------------------------------
module observable_set
  import capdiag_pkg::*;
#(
  parameter capture_point_e POINT = PT_TAP
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_captured,
  input  logic        trailing_bits_recorded,
  input  logic        timestamp_ns_resolution,

  output logic [8:0]  available,
  output logic        sees_before_phy,
  output logic        perturbs_the_system,
  output logic        gap_is_a_timestamp_difference,
  output logic [31:0] c_frames_captured,
  output logic        alignment_undiagnosable
);
  always_comb begin
    available = IN_A_CAPTURE;
    // Section 2: most capture paths discard the trailing-bit count,
    // and Chapter 7.3 Section 3's alignment class needs it.
    if (!trailing_bits_recorded) available[OBS_TRAILING] = 1'b0;

    sees_before_phy     = (POINT == PT_TAP);
    perturbs_the_system = (POINT == PT_SPAN) || (POINT == PT_INLINE);

    // A timestamp difference is not a gap. At 100 Gb/s a 12-octet gap
    // is 0.96 ns and a capture timestamp is rarely finer than 1 ns.
    gap_is_a_timestamp_difference = 1'b1;

    alignment_undiagnosable = !available[OBS_TRAILING];
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)              c_frames_captured <= '0;
    else if (frame_captured) c_frames_captured <= c_frames_captured + 32'd1;
  end
endmodule

Classification: a capability descriptor, and its useful outputs are all negations.

What it teaches: that gap_is_a_timestamp_difference is unconditionally true and is the chapter's quietest limitation. Chapter 5.9's minimum gap is twelve octets, which at 100 Gb/s is 0.96 nanosecondsbelow the resolution of almost every capture timestamp. So Chapter 21.8 §13's offered-load test, which counts frames arriving at the gap floor, cannot be run from a capture at 100 Gb/s at all, and can be run at 1 Gb/s where the same gap is 96 ns.

And it teaches that alignment_undiagnosable is a property of the capture path rather than of the link. Chapter 21.2 §3's alignment class is not whole octets and the check failed; a capture that records whole octets only can see the second condition and not the first — so it reports a check-sequence error where the design reported an alignment error, and the disagreement is the capture's.

Deliberately simplified: POINT is a parameter rather than a discovered property, so a tool that does not know how it was connected cannot fill it in. available is combinational over one input. And perturbs_the_system is a boolean where Section 2 showed a span port's perturbation is a quantity — the mirrored copy is real traffic on a real fabric, and how much depends on the mirrored port's load.

Production implication: the 0.96-nanosecond gap is the limitation that most changes what a capture can be used for, and it is rate-dependent in the direction nobody expects. At 1 Gb/s the minimum gap is 96 ns and an ordinary capture timestamp resolves it; at 100 Gb/s it is 0.96 ns and nothing in an ordinary capture path does. So every timing-based analysis a capture supports gets harder by a factor of a hundred across the rates this track covers, which is Chapter 20.6 §8's argument arriving in an instrument rather than in a coverage model.


4. Module 21's Faults, Against the Capture's Reach

Six chapters of Module 21 enumerated their fault spaces. This section runs a capture against all of them.

The rule is one line: a wire capture reaches a fault if and only if the fault's evidence is a property of the frames on the wire.

ChapterFaultsReachedPartialNot reached
Chapter 21.1 — receive-path sites12318
Chapter 21.2 — error classes13733
Chapter 21.4 — link sites140014
Chapter 21.6 — class-G mechanisms6213
Chapter 21.7 — descriptor failures8035
Chapter 21.8 — ceilings4211
total5714934

Twenty-four point six per cent reached outright, 15.8% partially, and 59.6% not at all.

The three rows worth reading individually.

Chapter 21.4 is fourteen of fourteen unreachable and the reason is definitional. A link that will not come up produces no frames, so there is nothing to capture. A capture on such a link records an empty file, and the empty file is consistent with all fourteen sites — which is Chapter 21.4 §1's whole argument, restated from the instrument's side.

Chapter 21.2 is the capture's best chapter — seven of thirteen outright — because six of its thirteen classes are predicates over the octets of a frame, which is exactly what a capture contains. The three partials are the three that need a field a capture path discards (trailing bits) or a distinction the wire cannot carry (our truncation against the far end's).

Chapter 21.7 is zero outright and three partial, and the three partials are the chapter's whole point. Its ordering failures corrupt the frame after it leaves the wire, so a wire capture records the frame as it was — correct — and that correct record is the reference a second observation is compared against. The capture is half an instrument and Section 6 is the other half.

And the partial column has a structure worth naming.

Why a fault is only partialCount
needs a second observation point4
needs a field the capture path discarded3
shows the symptom, not the cause2

Row one is four of the nine and it is Chapter 20.6's join problem arriving in a data centre. Two captures, at two points, of the same frames — and nothing identifies which frame is which except its contents, which is Chapter 20.3 §3's digest and Chapter 20.6 §4's collision analysis, in a setting where neither end can mint an identifier.


5. RTL 2 — The Two-Point Comparator

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// two_point_frame_join -- the same frames observed at two places, and
// the problem of saying which is which.
//
// Chapter 20.6 Section 4 met this in a testbench and could not mint an
// identifier either: the DUT does not carry one. The resolution there
// was a CONTENT DIGEST plus an ordering assumption, and the collision
// rate was the thing to bound. In the field the same resolution
// applies and the ordering assumption is weaker, because the two
// points are separated by queues.
// ---------------------------------------------------------------------
module two_point_frame_join
  import capdiag_pkg::*;
#(
  parameter int DIGEST_W  = 32,
  parameter int WINDOW    = 64      // frames of reorder tolerance
)(
  input  logic                 clk,
  input  logic                 rst_n,

  // point A -- upstream, e.g. a tap on the fibre
  input  logic                 a_valid,
  input  logic [DIGEST_W-1:0]  a_digest,
  input  logic [63:0]          a_time_ns,

  // point B -- downstream, e.g. the host's receive queue
  input  logic                 b_valid,
  input  logic [DIGEST_W-1:0]  b_digest,
  input  logic [63:0]          b_time_ns,

  output logic                 matched,
  output logic [63:0]          transit_ns,
  output logic                 a_unmatched_aged_out,   // lost between the points
  output logic                 b_without_a,            // injected, or missed at A
  output logic                 content_differs,        // same slot, other octets
  output logic [31:0]          c_matched,
  output logic [31:0]          c_lost,
  output logic [31:0]          c_ambiguous
);
  logic [DIGEST_W-1:0] a_dig [WINDOW];
  logic [63:0]         a_t   [WINDOW];
  logic                a_occ [WINDOW];
  logic [$clog2(WINDOW)-1:0] wp;

  // Two entries in the window with the same digest make the join
  // ambiguous. Chapter 20.6 Section 4 bounded this; here it is counted,
  // because in the field the rate is a measurement rather than a
  // parameter.
  logic hit;
  int   hit_idx;
  int   hit_count;

  always_comb begin
    hit = 1'b0; hit_idx = 0; hit_count = 0;
    for (int i = 0; i < WINDOW; i++)
      if (a_occ[i] && a_dig[i] == b_digest) begin
        hit = 1'b1; hit_idx = i; hit_count++;
      end
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      wp <= '0; c_matched <= '0; c_lost <= '0; c_ambiguous <= '0;
      matched <= 1'b0; b_without_a <= 1'b0; transit_ns <= '0;
      for (int i = 0; i < WINDOW; i++) a_occ[i] <= 1'b0;
    end else begin
      matched <= 1'b0; b_without_a <= 1'b0;

      if (a_valid) begin
        // Overwriting an occupied slot IS the aged-out case: the frame
        // was seen at A and never at B.
        if (a_occ[wp]) c_lost <= c_lost + 32'd1;
        a_dig[wp] <= a_digest; a_t[wp] <= a_time_ns; a_occ[wp] <= 1'b1;
        wp <= wp + 1'b1;
      end

      if (b_valid) begin
        if (hit) begin
          matched     <= 1'b1;
          transit_ns  <= b_time_ns - a_t[hit_idx];
          a_occ[hit_idx] <= 1'b0;
          c_matched   <= c_matched + 32'd1;
          if (hit_count > 1) c_ambiguous <= c_ambiguous + 32'd1;
        end else begin
          b_without_a <= 1'b1;
        end
      end
    end
  end

  assign a_unmatched_aged_out = a_valid && a_occ[wp];
  assign content_differs      = b_valid && !hit;
endmodule

Classification: a join across two observation points, with the ambiguity counted rather than assumed away.

What it teaches: that a capture at one point answers almost nothing about delivery and a capture at two points answers a great deal, and that the step between them is a join on a key neither point can mint. Chapter 20.6 §4 met this exactly, in a testbench, where the generator could have minted one and deliberately did not — because a real link does not. Here the same constraint is not a choice.

And it teaches what the three unmatched outputs mean, which are three different faults. a_unmatched_aged_out is a frame that entered and did not leaveChapter 21.6's class G, localised to the span between the points. b_without_a is a frame that left and did not enter, which is either an injection or a miss at A — and at a span port, a miss at A is far more likely than an injection. content_differs is Chapter 21.7's corruption class and it is the only one of the three that no counter anywhere can report.

Deliberately simplified: the window is a flat array searched combinationally, which at WINDOW = 64 is a 64-way comparator and is fine in a testbench and not at rate. hit_count > 1 counts ambiguity and then proceeds anyway, picking the last match, which is a decision the block should expose rather than make. And content_differs is b_valid && !hit, which conflates corrupted with never seen at A — a real comparator needs a weaker key (a flow tuple plus a sequence field) to distinguish them, and Chapter 21.6 §7 showed why no such field is guaranteed.

Production implication: the ambiguity counter is what makes the join trustworthy rather than merely convenient. A digest collision produces a match that is wrong, and a wrong match reports a transit time that is meaningless and a delivery that did not happen — so a join with no collision measurement can report 100% delivery on a path that is losing frames. Publish c_ambiguous alongside c_matched always, and treat any run where the ratio exceeds a fraction of a per cent as uninterpretable rather than as evidence. This is Chapter 20.3 §3's digest argument with the stakes raised: in a testbench a collision costs a debug hour; here it costs a false clean bill.


6. Contents Need Two Points, and That Is the Join Again

Two observation points record the same frames and a join has to decide which record at the second point corresponds to which at the first. Ethernet carries no identifier for this, so the key is a content digest plus a reorder window, which is the same resolution the universal verification methodology agent chapter reached in a testbench and for the same reason: neither end can mint an identifier. The join has four outcomes. A frame present at both points is matched, and the difference of the timestamps is a transit time, which is meaningful only if the two clocks share a reference, since two free running capture hosts drift by microseconds over a capture and the signal being measured is also microseconds. A frame present at the first point and evicted from the window unmatched was lost between the points, which is the packet drops chapter's silent class localised to one span. A frame present at the second point with no match at the first was either injected or missed at the first point, and at a span port a miss is far more likely than an injection. And a frame whose content differs is the direct memory access chapter's corruption class, which no counter anywhere reports. The window's width is the cost: with a thirty two bit digest, a sixty four frame window gives a spurious match about once in sixty seven million frames, which is under half a second of minimum size traffic at a hundred gigabits per second, and a four thousand ninety six frame window gives one every seven milliseconds.Point Aa tap on the fibrePoint Bthe host's receivequeueNo identifierexistsdigest + windowThe join20.6's problem in thefieldAmbiguityW over 2^32Matchedtransit time — needsPTPA onlylost in the spanContent differs21.7's classFour outcomesone point gives none12
Figure 2 — one point says what was here; only two points say what happened in between.

Section 4 said a capture reaches 14 of 57 faults outright and 9 partially, and that 4 of the 9 partials need a second observation point. This section is why, and what the second point costs.

Start with the asymmetry. A capture at one point establishes what was on the wire there, at that time. That is a statement about a location, and every fault in Module 21 that is interesting is a statement about a difference between two locations.

QuestionOne pointTwo points
was this frame corrupt on the wire?yes — recompute the check sequence
did this frame arrive at the host?noyes
was it corrupted between wire and host?noyes — Chapter 21.7's class
were frames reordered in the path?noyes
how long did the path take?noyes
is the far end sending at the rate it claims?partly — from spacingyes

Rows two through five are four of Module 21's partials and every one becomes reachable with a second point. That is the chapter's most useful practical statement: the single biggest increase in a capture's diagnostic power is not a bigger buffer or a faster sink — it is a second capture point.

And the second point is expensive in a way the first is not.

CostFirst pointSecond point
hardwarea tap or a mirror configurationthe same again, elsewhere
storageas Section 12twice
clockanythe two must be comparable
the joinnone neededa digest, a window, and an ambiguity rate
analysisread the framesmatch them first

Row three is the one that surprises people. Comparing timestamps across two points needs the two clocks related, and two free-running capture hosts drift by microseconds over a capture's length — which swamps the microsecond-scale transit times the comparison exists to measure. Chapter 16.2 built an entire protocol to solve this, and a capture setup that does not use it is measuring drift and calling it transit.

Two capture points without a common time reference measure their own clock difference. The traffic contributes a term smaller than the error.

Row four is Chapter 20.6's join arriving in the field, and the field version is harder for a reason worth stating. In a testbench the two observation points are separated by the DUT and the frames between them are in order, so a digest match plus a monotonic assumption is enough. Between two points in a data centre the frames are separated by queues, load balancers and possibly multiple paths — so the window has to absorb real reordering, and a wider window raises the digest collision probability quadratically. The join gets less reliable exactly as the network gets more interesting.

The arithmetic is worth one line. With a 32-bit digest and a window of W frames, the chance that a given lookup finds a spurious match is about W / 2³². At W = 64 that is 1.5 × 10⁻⁸ — one bad match per 67.1 million frames, which is 0.45 seconds of 100 Gb/s minimum-size traffic. At W = 4 096, to absorb a badly reordered path, it is 9.5 × 10⁻⁷ — one per 1.05 million frames, or every 7.0 milliseconds. The window is not free.


7. RTL 3 — The Relationship Extractor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// frame_relationship_extractor -- the properties of a SEQUENCE of
// frames, which is the half of a capture's content no counter holds.
//
// Chapter 19.7's counter block spent 2672 flops on 27 counters, and
// every one of them is a SUM. A sum destroys order, destroys spacing,
// and destroys pairing. This block recovers the three, from the same
// frames, and the cost of doing so is the point of Section 19.
// ---------------------------------------------------------------------
module frame_relationship_extractor
  import capdiag_pkg::*;
#(
  parameter int BURST_NS   = 100000,   // 100 us -- Section 8's window
  parameter int SEQ_W      = 32
)(
  input  logic              clk,
  input  logic              rst_n,

  input  logic              f_valid,
  input  logic [63:0]       f_time_ns,
  input  logic [15:0]       f_len,
  input  logic [31:0]       f_flow,
  input  logic [SEQ_W-1:0]  f_seq,        // from an upper layer, if present
  input  logic              f_seq_valid,

  // relationship 1 -- ORDER
  output logic              out_of_order,
  output logic [31:0]       c_out_of_order,
  // relationship 2 -- SPACING, and the burst it reveals
  output logic [63:0]       gap_ns,
  output logic              back_to_back,
  output logic [31:0]       burst_peak_bytes,
  // relationship 3 -- PAIRING / repetition
  output logic              duplicate_seq,
  output logic [31:0]       c_duplicate,

  output logic [31:0]       c_frames
);
  logic [63:0]       last_time;
  logic [SEQ_W-1:0]  last_seq;
  logic              have_last;

  // A rolling byte count over BURST_NS. A one-second counter average
  // cannot represent this: Section 8 shows an 88.9 us burst that a
  // one-second average records as 0.0089% utilisation.
  logic [63:0] win_start;
  logic [31:0] win_bytes;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      have_last <= 1'b0; c_frames <= '0; c_out_of_order <= '0;
      c_duplicate <= '0; burst_peak_bytes <= '0; win_bytes <= '0;
      win_start <= '0; out_of_order <= 1'b0; duplicate_seq <= 1'b0;
      gap_ns <= '0; back_to_back <= 1'b0;
    end else begin
      out_of_order  <= 1'b0;
      duplicate_seq <= 1'b0;
      back_to_back  <= 1'b0;

      if (f_valid) begin
        c_frames <= c_frames + 32'd1;

        // ORDER -- meaningful only with an upper-layer sequence. Chapter
        // 21.6 Section 7 established that Ethernet itself carries none,
        // which is why this output is gated on f_seq_valid.
        if (have_last && f_seq_valid) begin
          if (f_seq < last_seq) begin
            out_of_order   <= 1'b1;
            c_out_of_order <= c_out_of_order + 32'd1;
          end else if (f_seq == last_seq) begin
            duplicate_seq <= 1'b1;
            c_duplicate   <= c_duplicate + 32'd1;
          end
          last_seq <= f_seq;
        end else if (f_seq_valid) begin
          last_seq <= f_seq;
        end

        // SPACING
        if (have_last) begin
          gap_ns       <= f_time_ns - last_time;
          // At 100 Gb/s a minimum frame plus its gap is 6.72 ns.
          back_to_back <= ((f_time_ns - last_time) <= 64'd7);
        end
        last_time <= f_time_ns;
        have_last <= 1'b1;

        // BURSTINESS
        if ((f_time_ns - win_start) > BURST_NS) begin
          win_start <= f_time_ns;
          win_bytes <= {16'b0, f_len};
        end else begin
          win_bytes <= win_bytes + {16'b0, f_len};
          if ((win_bytes + f_len) > burst_peak_bytes)
            burst_peak_bytes <= win_bytes + {16'b0, f_len};
        end
      end
    end
  end
endmodule

Classification: three relationship extractors sharing one frame stream, and each recovers something a sum destroyed.

What it teaches: that the three relationships are not equally available. Spacing and burstiness come from timestamps the apparatus adds and are always available at the resolution the apparatus offers. Order and duplication come from a sequence field the frame may not carry — which is Chapter 21.6 §7's finding, and the reason out_of_order is gated on f_seq_valid rather than computed unconditionally. A capture of frames with no upper-layer sequence cannot tell you they were reordered, only what order this point saw.

And it teaches why burst_peak_bytes is the output with the most practical value in the module. Chapter 19.7 §19's counter block is 27 sums; a sum over a one-second interval represents an 88.9-microsecond burst as 0.0089% utilisation. The burst is 1.11 MB delivered at line rate — enough to overrun a 1 MB egress buffer completely — and the counter that would have to see it is averaging over an interval a hundred million times longer than the event.

Deliberately simplified: f_seq < last_seq treats a sequence space as unwrapped, which is wrong at the wrap and is Chapter 18.2's ring-index wrap in miniature. back_to_back compares against 7 ns, a constant baked for 100 Gb/s, where the same test at 1 Gb/s needs 672. The burst window is a jumping window rather than a sliding one, so a burst straddling the boundary is reported as two half-bursts. And f_flow is an input and is never used, which is deliberate: a real extractor keeps per-flow state and the storage for that is the module's real cost.

Production implication: the unused f_flow port is the honest signal of what this costs. Per-flow relationship state is the difference between a block that fits in a corner of an FPGA and one that does not — one last_seq, one last_time, one window per flow, at roughly 130 flops each, means 10 000 flows is 1.3 million flops, which is 92 times Chapter 19.7 §19's entire 14 166-flop datapath. That ratio is why relationship analysis lives on a capture host with gigabytes of DRAM and not in the MAC, and it is the real reason Chapter 19.7's counters are sums — not because sums are informative, but because sums are the only thing that fits.


8. What Only a Capture Shows: Relationships Between Frames

Section 4 established that a capture reaches 24.6% of Module 21's faults. This section establishes that for one group of questions it is the only instrument there is.

The argument is structural rather than empirical. Chapter 19.7's statistics block is 27 counters and every counter is a sum over an interval. A sum is a function that is not injective: many sequences of frames map to the same total. So the information a sum destroys cannot be recovered from the sum, no matter how many sums are kept, and the only way to have it is to have kept the frames.

Three kinds of information are destroyed, and they are the three the extractor recovers.

RelationshipWhat a counter showsWhat a capture shows
ordernothing — a sum is commutativethe sequence, at this point
spacingan average over the intervalevery gap individually
pairingtwo independent totalswhich response answered which request

Row one's phrasing is exact and worth pausing on. A sum is commutative: the total of a thousand frames is identical whichever order they arrived in. So no counter, of any kind, at any resolution, can report a reordering — this is not a limitation of Chapter 19.7's implementation but a property of the function. A reordering counter would have to be a different kind of object, and the block that computes it is the one in Section 7.

Row two has the numbers.

Value
a burst filling a 1 MB egress buffer (100 Gb/s in, 10 Gb/s out)88.9 µs
bytes delivered in it1.11 MB
frames, at 1 518 octets659
what a one-second byte counter reports0.0089% utilisation
ratio of counter interval to event11 250 : 1

Row four is the whole case for a capture in one number. A port that overran its buffer and dropped frames reports a hundredth of one per cent utilisation, because the drop happened in eighty-nine microseconds and the counter is averaging over a second. Chapter 21.8 §14 named the symptom — a link with no errors that is slowand the microburst is one of its two commonest causes. The capture is the instrument because the capture keeps the timestamps.

Row three — pairing — is the least discussed and is the one that matters for Chapter 21.7.

A request counter and a response counter that agree prove that the totals match. They do not prove that any particular request was answered, and a system that answers every request with the previous request's response satisfies both counters exactly.

That is not a hypothetical. Chapter 21.7 §8's three ordering failures — a descriptor written back before its data, a completion visible before its payload, and a ring index advanced past an incomplete write — all preserve totals and corrupt pairing. Every counter in Chapter 19.7's 27 is blind to them by construction, and so is every counter that could be added, because adding a counter adds a sum.

So the capture's unique contribution is precisely: the frames' relationships to each other. Contents matter too — Section 6 showed contents need a second point — but relationships need only one point and no counter can ever supply them. That is the honest answer to the question eight chapters deferred.


9. RTL 4 — The Capture Sink and Its Buffer

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capture_sink_model -- the apparatus as a queue, with an arrival rate
// it cannot match.
//
// Chapter 19.5 Section 4's receive FIFO empties because its drain rate
// EXCEEDS the line rate; the FIFO exists to absorb a burst, not a
// sustained excess. This queue's drain rate is BELOW the line rate, so
// the buffer is a countdown. Same structure, opposite sign, and the
// consequence is that the apparatus drops.
// ---------------------------------------------------------------------
module capture_sink_model
  import capdiag_pkg::*;
#(
  parameter int ARRIVE_MBYTES_S = WIRE_MBYTES_S,   // 12500 at 100 Gb/s
  parameter int SINK_MBYTES_S_P = SINK_MBYTES_S,   // 3000, an NVMe write
  parameter int BUFFER_MB       = BUFFER_MBYTES    // 8000
)(
  input  logic        clk,          // 1 kHz tick -- one per millisecond
  input  logic        rst_n,
  input  logic        capturing,
  input  logic [15:0] offered_mbytes_this_ms,   // may be below line rate

  output logic [31:0] occupancy_mb,
  output logic        buffer_full,
  output logic [31:0] dropped_mb,
  output logic [15:0] self_drop_pct_x10,        // tenths of a per cent
  output logic [31:0] ms_until_full,
  output logic        sink_keeps_up
);
  localparam int SINK_MB_PER_MS = SINK_MBYTES_S_P / 1000;   // 3 MB/ms

  logic [31:0] total_offered_mb;

  assign sink_keeps_up = (SINK_MBYTES_S_P >= ARRIVE_MBYTES_S);

  always_comb begin
    // Section 10's headline: buffer / (arrival - drain).
    if (sink_keeps_up) ms_until_full = 32'hFFFF_FFFF;
    else               ms_until_full =
      (BUFFER_MB - occupancy_mb) /
      ((ARRIVE_MBYTES_S - SINK_MBYTES_S_P) / 1000);
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      occupancy_mb <= '0; dropped_mb <= '0; total_offered_mb <= '0;
      buffer_full <= 1'b0; self_drop_pct_x10 <= '0;
    end else if (capturing) begin
      automatic int net;
      automatic int drained;

      total_offered_mb <= total_offered_mb + offered_mbytes_this_ms;

      drained = (occupancy_mb > SINK_MB_PER_MS) ? SINK_MB_PER_MS
                                                : occupancy_mb;
      net = occupancy_mb - drained + offered_mbytes_this_ms;

      if (net > BUFFER_MB) begin
        // THIS is the line that makes the apparatus part of the system.
        // The excess is discarded, and nothing in the capture file
        // distinguishes it from a frame the LINK dropped.
        dropped_mb   <= dropped_mb + (net - BUFFER_MB);
        occupancy_mb <= BUFFER_MB;
        buffer_full  <= 1'b1;
      end else begin
        occupancy_mb <= net;
        buffer_full  <= 1'b0;
      end

      if (total_offered_mb != 0)
        self_drop_pct_x10 <= 16'((dropped_mb * 1000) / total_offered_mb);
    end
  end
endmodule

Classification: a leaky-bucket model of a storage path, with the leak too small.

What it teaches: that the capture apparatus is Chapter 19.5's FIFO with the inequality reversed, and that reversal changes everything. Chapter 19.5 §4's receive FIFO exists to absorb a burst: arrivals exceed the drain briefly and the FIFO empties afterwards, so its depth is sized against a burst length. A capture's sink never catches up, so its buffer is sized against the whole capture — and Section 10 shows that at 100 Gb/s no affordable buffer is large enough for a capture longer than a few seconds.

And it teaches which line makes this a chapter about an instrument rather than about a disk. dropped_mb <= dropped_mb + (net - BUFFER_MB) discards frames that were on the wire, and the capture file it produces is indistinguishable from a capture of a link that dropped them. A person diagnosing Chapter 21.6's class G with this apparatus sees exactly the evidence they came looking for, and the apparatus manufactured it.

Deliberately simplified: the model works in megabytes per millisecond, so a burst finer than a millisecond is invisible — which is ironic given Section 8's 88.9-microsecond burst and is the right simplification for a model whose subject is a storage path rather than a link. SINK_MBYTES_S_P is constant, where a real NVMe write rate falls by a factor of two or more once its SLC cache is exhausted, typically tens of seconds in. And self_drop_pct_x10 is computed from the previous cycle's dropped_mb, so it lags by a millisecond.

Production implication: the missing SLC-cache behaviour is the one that catches people, because it makes the apparatus's drop rate a function of how long the capture has been running. A ten-second capture on a fresh device drops nothing; the same capture at minute three drops several per cent; and the difference looks exactly like a fault that gets worse under sustained load — which is Chapter 21.8's entire symptom class. Always capture the apparatus's own drop counter alongside the frames, and treat a capture with no such counter as evidence of nothing at all.


10. The Capture's Own Drop Rate

At a hundred gigabits per second the wire delivers twelve and a half gigabytes per second and a capture stores the frame plus a sixteen octet record header, so stored bytes track wire bytes within five per cent. The storage path does not keep up. A sustained solid state write sustains three gigabytes per second, which is twenty four per cent of line rate, and an eight gigabyte buffer therefore fills in eight hundred and forty two milliseconds. A ten gigabit export link at eighty per cent sustained gives one gigabyte per second, eight per cent, and fills the same buffer in six hundred and ninety six milliseconds. A one gigabit management link gives an eighth of a gigabyte per second, one per cent, and fills it in six hundred and forty six milliseconds. Over a one second capture the excess is discarded: one and a half gigabytes or twelve per cent with the solid state sink, three and a half gigabytes or twenty eight per cent with the export link, and four point three seven gigabytes or thirty five per cent with the management link. This is the receive first in first out chapter's structure with the inequality reversed. There a burst was absorbed because the drain exceeded the line rate, so the queue emptied. Here the drain is below the line rate permanently, so the buffer is a countdown rather than a cushion, and the frames the apparatus discards are indistinguishable in the capture file from frames the link itself dropped.12.50 GB/sarriving100 Gb/s, always8 GB buffera countdown3.00 GB/s sink24.0% — full at 842 ms1.00 GB/s export8.0% — full at 696 ms0.125 GB/s link1.0% — full at 646 ms12.0% dropped1.50 GB in one second28.0% dropped3.50 GB35.0% dropped4.37 GBMarked nowherereads as link loss12
Figure 3 — the receive FIFO's inequality, reversed: the drain is below the arrival, so the buffer is a countdown.

The apparatus drops. This section prices it, and the price is high enough to change what a capture can be used for.

The arithmetic is two lines. At 100 Gb/s the wire delivers 12.50 gigabytes per second. A capture stores the frame plus a per-record header — sixteen octets in the usual formats — so stored bytes track wire bytes closely: 80 stored against 84 on the wire at minimum size (95.2%), 1 534 against 1 538 at maximum (99.7%). Within five per cent, storing a 100 Gb/s link costs 12.5 GB/s of write bandwidth.

And nothing ordinary provides it.

SinkGB/sFraction of line rate8 GB buffer lasts
a 1 Gb/s management link, exported live0.1251.0%646 ms
a 10 Gb/s export link, 80% sustained1.008.0%696 ms
a sustained NVMe write3.0024.0%842 ms
a striped NVMe array6.0048.0%1 231 ms
host DRAM, no persistence> 12.5> 100%never — but the buffer is the capture

Row five is the honest one and it is why ring-buffer captures into RAM are the normal practice. DRAM keeps up, so nothing is dropped while it is being written — and the capture's length is then simply the buffer divided by 12.5 GB/s, which Section 12 shows is 640 milliseconds for eight gigabytes.

Rows one to four are what happens when a capture is asked to run longer than that.

Over a 1-second capture, 8 GB bufferStoredDroppedSelf-drop
3.00 GB/s sink11.0 GB1.50 GB12.0%
1.00 GB/s sink9.00 GB3.50 GB28.0%
0.125 GB/s sink8.13 GB4.37 GB35.0%

Twelve to thirty-five per cent, and none of it appears in the capture file as a loss.

A one-second capture taken to find out why frames are being dropped drops between one frame in eight and one in three, itself, and reports them as missing from the link.

That is the chapter's most important practical result, and it has a clean statement as a rule.

RuleConsequence
a capture is trustworthy only while its buffer is not fullso its trustworthy length is buffer ÷ 12.5 GB/s
beyond that length the loss rate is (12.5 − sink) ÷ 12.5and it is invisible in the frames
a filter moves the arrival rate, not the line rateso Section 12's filter is the only way to run long

Row three is the escape and it is the subject of Section 12. The 12.5 GB/s figure is the rate of frames the apparatus is asked to store; a filter applied before the store reduces it. A filter applied after — in the analysis tool, on the file — does not, and the distinction between a hardware or kernel-level filter and a display filter is therefore the difference between a capture that runs for hours and one that runs for six hundred milliseconds.

One more comparison closes the section, because it is Chapter 19.5's section read backwards.

Chapter 19.5's receive FIFOthe capture sink
arrival≤ line rate= line rate
drain> line rate< line rate, by 4× to 100×
what the depth absorbsa bursta fraction of the whole capture
overflow behaviourdrop, and count it in a visible counterdrop, and usually count it nowhere
who sees the lossChapter 19.7's countersnobody

Row four is the design lesson. Chapter 19.5's FIFO drops and reports; that report is Chapter 21.1's site 9 and the whole reason a receive overrun is diagnosable at all. A capture apparatus that drops without reporting has committed the design error that chapter spent a section preventing — and it is the commonest configuration in the field.


11. RTL 5 — The Self-Drop Detector

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capture_self_drop_detector -- make the apparatus's own loss visible
// in the apparatus's own output.
//
// Section 10's rule: a capture that drops without reporting has
// committed the error Chapter 19.5 spent a section preventing. This
// block is the minimum that prevents it, and its three signals are
// three different ways of knowing.
// ---------------------------------------------------------------------
module capture_self_drop_detector
  import capdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  // 1. the apparatus knows, if it is asked
  input  logic        sink_overflow,
  // 2. an independent frame counter at the capture point
  input  logic        point_frame_seen,
  input  logic        record_written,
  // 3. the link's own counters, read out of band
  input  logic [31:0] port_rx_frames,
  input  logic        port_counter_valid,

  output logic [31:0] c_seen,
  output logic [31:0] c_written,
  output logic [31:0] c_self_dropped,
  output logic        self_drop_active,
  output logic        apparatus_below_port,     // Section 10's signature
  output logic [15:0] coverage_pct,
  output logic        coverage_unknown          // NO counter available
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_seen <= '0; c_written <= '0; c_self_dropped <= '0;
      self_drop_active <= 1'b0;
    end else begin
      if (point_frame_seen) c_seen    <= c_seen + 32'd1;
      if (record_written)   c_written <= c_written + 32'd1;
      if (point_frame_seen && !record_written)
        c_self_dropped <= c_self_dropped + 32'd1;
      self_drop_active <= sink_overflow;
    end
  end

  always_comb begin
    // The port's own counter is the only INDEPENDENT reference. Without
    // it the apparatus is the sole witness to its own completeness,
    // which is Section 20's rejected property in one sentence.
    apparatus_below_port = port_counter_valid &&
                           (c_written < port_rx_frames);
    coverage_unknown     = !port_counter_valid;
    coverage_pct         = (port_counter_valid && port_rx_frames != 0)
                         ? 16'((c_written * 100) / port_rx_frames)
                         : 16'd0;
  end
endmodule

Classification: a completeness monitor for an instrument, and its most important output is the one that admits it cannot tell.

What it teaches: that there are three independent ways to know a capture is incomplete, and they are not equally trustworthy. sink_overflow is the apparatus reporting on itself. point_frame_seen && !record_written is a counter inside the same apparatus, one stage earlier. port_rx_frames is the switch's own counter — Chapter 19.7's block, read over the management interface — and it is the only reference not made of the thing being checked.

And it teaches that coverage_unknown is the honest default. Most captures are taken with no port counter read at the same instants, so most captures have no evidence of their own completeness at all — and the analysis proceeds as though they do. The block makes that state explicit rather than reporting 100% by omission.

Deliberately simplified: apparatus_below_port compares a local frame count with a remote counter with no common time base, so the comparison is only valid when both are sampled at the same moment — which needs Chapter 16.2's machinery or a stop-the-world read. coverage_pct is integer per cent, which cannot express the 12.0% self-drop of Section 10 to useful precision at the low end. And point_frame_seen assumes a stage that sees every frame, which for a span port is exactly the assumption in doubt: the mirror may have dropped before this point.

Production implication: the missing common time base is why this comparison is usually done wrong. A capture count taken at the end of the run compared against a port counter read a minute later includes a minute of traffic the capture never saw, and reports a self-drop rate that is entirely the minute. Read the port counter immediately before starting and immediately after stopping, and compare the difference — the procedure is Chapter 19.7 §16's snapshot discipline applied to an instrument instead of to a design, and it converts a meaningless number into the only completeness evidence a capture ever has.


12. Frames per Fault, and the Filter That Changes Everything

Chapter 21.1 §12 priced a capture at three hours. This section prices it in frames, which is the unit that decides whether the fault is in the file.

Start with how much wire time a store covers.

StoreMin-size framesMax-size framesWire time
1 GB1.25 × 10⁷6.52 × 10⁵80–84 ms
8 GB1.00 × 10⁸5.21 × 10⁶642–672 ms
64 GB8.00 × 10⁸4.17 × 10⁷5.1–5.4 s
1 TB1.25 × 10¹⁰6.52 × 10⁸80–84 s

A terabyte holds about eighty seconds of a 100 Gb/s link. That single line reframes the instrument: the largest capture most people can take is a minute and a half, and the fault has to happen inside it.

Which brings the second table, the one that decides everything.

Fault rateFrames needed to see oneStoreWire time
1 in 10³10³80 kB6.7 µs
1 in 10⁶10⁶80 MB6.7 ms
1 in 10⁹10⁹80 GB6.7 s
1 in 10¹²10¹²80 TB1 h 52 m

Row three is the boundary of the practical. A one-in-a-billion fault — which is the rate a 10⁻¹² bit error rate produces on minimum-size frames, and Chapter 21.3 §6's central caseneeds eighty gigabytes to catch one instance. Row four, a rate an in-spec optical link comfortably achieves, needs eighty terabytes, which nobody captures.

Unfiltered, a capture is an instrument for faults above about one in ten million. Below that it is an expensive way to record correct traffic.

Now the filter, which changes the exponent rather than the constant.

A filter applied before the store — in the capture NIC, in the kernel's BPF, in a hardware tap's own matching engine — reduces the arrival rate at the sink by exactly the selectivity. A fault occurring at rate p whose frames a filter can name arrives at 12.5 GB/s × p instead of 12.5 GB/s, so a fixed store covers 1/p times more wire time.

At a 1-in-10⁶ fault rate, a 1 GB store coversWire time
unfiltered84 ms
filtered on the fault84 000 s — 23.3 hours

From eighty-four milliseconds to twenty-three hours, for the same gigabyte. That is the single largest lever in the entire chapter and it costs nothing but a filter expression.

And it has a precondition that is usually the hard part.

The filter mustWhich means
name the faulty framesyou already know something about them
run before the storein hardware or in the kernel, not in the viewer
not itself dropa filter that cannot keep up drops what it was meant to keep

Row one is circular in a way worth stating plainly. A filter that isolates the fault requires a hypothesis about the fault, and Chapter 21.1's procedure exists precisely because you do not have one yet. So the capture is the last step of the procedure and not the first — which is exactly where all eight chapters put it, and this section is the arithmetic behind that placement rather than a new recommendation.

Row three is the trap. A software filter that drops under load drops the matching frames as readily as the non-matching ones, so a filtered capture that is 99.999% selective but overruns still loses the very frames the filter selected. Chapter 19.5 §9's lesson — a queue that overflows loses the head as well as the tailapplies to the filter's output queue exactly as it applies to a MAC's.


13. RTL 6 — The Trigger and the Pre-Trigger Buffer

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capture_trigger_ring -- Section 12's arithmetic says a store holds
// under a second of wire time. The only way to spend it on the right
// second is to keep writing and stop on the fault.
//
// The pre-trigger fraction is the whole design decision: a fault's
// CAUSE is before it and its CONSEQUENCES are after it, and Module 21
// has examples of both mattering.
// ---------------------------------------------------------------------
module capture_trigger_ring
  import capdiag_pkg::*;
#(
  parameter int DEPTH_FRAMES = 1024,
  parameter int PRE_PCT      = 75       // 75% before the trigger
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        f_valid,
  input  logic [15:0] f_len,

  // trigger sources, in the order Chapter 21.2's taxonomy ranks them
  input  logic        trig_fcs_bad,
  input  logic        trig_undersize,
  input  logic        trig_oversize,
  input  logic        trig_external,      // a host-side symptom
  input  logic        arm,

  output logic        armed,
  output logic        triggered,
  output logic        capture_complete,
  output logic [31:0] pre_frames_held,
  output logic [31:0] post_frames_taken,
  output logic        trigger_was_first_of_a_burst,
  output logic [31:0] c_triggers_seen,
  output logic        trigger_lost_context   // ring not yet full at trigger
);
  localparam int PRE_TARGET  = (DEPTH_FRAMES * PRE_PCT) / 100;
  localparam int POST_TARGET = DEPTH_FRAMES - PRE_TARGET;

  logic [31:0] ring_fill;
  logic        any_trig;

  assign any_trig = trig_fcs_bad | trig_undersize | trig_oversize
                  | trig_external;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      armed <= 1'b0; triggered <= 1'b0; capture_complete <= 1'b0;
      ring_fill <= '0; post_frames_taken <= '0; c_triggers_seen <= '0;
      trigger_was_first_of_a_burst <= 1'b0; trigger_lost_context <= 1'b0;
    end else begin
      if (arm) begin
        armed <= 1'b1; triggered <= 1'b0; capture_complete <= 1'b0;
        ring_fill <= '0; post_frames_taken <= '0;
      end

      if (armed && !triggered && f_valid && ring_fill < DEPTH_FRAMES)
        ring_fill <= ring_fill + 32'd1;

      if (armed && !triggered && any_trig) begin
        triggered       <= 1'b1;
        c_triggers_seen <= c_triggers_seen + 32'd1;
        // Chapter 21.3 Section 18: a hundred failing frames report the
        // same bit a hundred times. The FIRST is the one with context.
        trigger_was_first_of_a_burst <= 1'b1;
        // If the ring never filled, the pre-trigger window is short and
        // the cause may simply not be in it.
        trigger_lost_context <= (ring_fill < PRE_TARGET);
      end

      if (triggered && !capture_complete && f_valid) begin
        post_frames_taken <= post_frames_taken + 32'd1;
        if (post_frames_taken + 1 >= POST_TARGET) begin
          capture_complete <= 1'b1;
          armed            <= 1'b0;
        end
      end
    end
  end

  assign pre_frames_held = (ring_fill > PRE_TARGET) ? PRE_TARGET : ring_fill;
endmodule

Classification: a ring with a stop condition, which is the only structure that makes a sub-second store useful on a fault that happens once an hour.

What it teaches: that PRE_PCT is the design's real content and 75% is a claim about where the evidence is. Chapter 21.2's error classes are symptoms; their causes are frames or conditions that came before — a preceding oversized frame, a burst that filled a queue, a partner's transmission that collided. So most of the ring belongs to the past. The 25% after the trigger exists for the one case where the consequence matters: Chapter 21.7's corruption class, where the question is what the next frames looked like.

And it teaches why trigger_lost_context is the module's most useful output. A trigger that fires before the ring has filled produces a capture with a truncated pre-trigger window, and that capture is silently less informative than one taken later. The output says so, which is Chapter 21.6 §12's principle — an instrument should report the conditions under which its own answer is weak — applied to a trigger.

Deliberately simplified: the trigger sources are four booleans where a real trigger is an expression over frame contents, and the interesting triggers are usually combinations: a bad check sequence on a particular flow, or an undersized frame within a microsecond of an oversized one. POST_TARGET frames rather than post-trigger time, which for a bursty link makes the post-trigger window's duration vary by a factor of a thousand. And trigger_was_first_of_a_burst is set unconditionally on the first trigger rather than actually distinguishing an isolated fault from the head of a burst — which needs a quiet-period test the module does not have.

Production implication: the frames-not-time post-trigger window is a real and common bug in capture tooling. A post-trigger window of 256 frames on an idle link covers whatever interval those 256 frames take — possibly seconds; on a loaded 100 Gb/s link it covers 1.7 microseconds. The person who configured it was thinking about how much evidence, and what they got was a duration that varies with loadso the same configuration captures the aftermath on a quiet link and misses it entirely on the busy one where the fault actually occurs. Configure post-trigger in time, and derive the frame count from the worst-case rate.


14. What a Capture Must Never Do

Section 1 claimed the capture is inside the system it measures. This section is the evidence, and it is stronger than the claim.

Three capture arrangements, three perturbations.

ArrangementWhat it adds to the system
a passive optical tapoptical loss on the path it is observing
a mirrored (span) porta full copy of the traffic, onto a real fabric
capture on the host under testCPU, memory bandwidth and interrupts, taken from the subject

Row one is the sharpest because the numbers are unforgiving.

dB
a 50/50 optical tap, through path≈ 3.0 plus excess
a 70/30 tap, through path≈ 1.5 plus excess
the channel insertion-loss allowance, 100GBASE-SR4 over 100 m OM4under 2

A fifty-fifty tap consumes more than the entire link budget. Inserting it into an in-spec link makes the link out of spec, so the receiver's margin collapses and errors appear — and they appear in the capture, which is what the capture was installed to look for. Chapter 21.2 §7's channel class is manufactured by the instrument, and the instrument's output is entirely consistent with the channel having been marginal all along.

A tap that is not in the link budget does not observe the errors. It creates them, records them, and cannot tell you which it did.

That is Section 20's rejected property, stated in optics rather than in SystemVerilog.

Row two's perturbation is quantitative in a different way.

MirrorCopy trafficDestinationMirror-side loss
one 100 Gb/s port100 Gb/sa 10 Gb/s analyser port90%
one 100 Gb/s port100 Gb/sa 100 Gb/s analyser port0% at average, > 0 on any burst
eight 25 Gb/s ports200 Gb/sa 100 Gb/s analyser port50%
one direction of one port≤ 100 Gb/sa 100 Gb/s port0%

Row one is the commonest arrangement in the field and it loses nine frames in ten. The loss is not random: the mirror destination's queue drops tail, so it discards bursts preferentiallyexactly the traffic pattern Section 8 showed is the reason to take a capture in the first place. The instrument is biased against the phenomenon.

Row two deserves its own sentence. A 100 Gb/s mirror of a 100 Gb/s port is exactly full, with no headroom, so any transient — including the microburst of Section 8 — overflows it. A mirror sized at line rate is not a mirror with margin; it is a mirror with zero margin, and Chapter 19.5 §4's whole argument is that a queue with no rate headroom does not drain.

Row three is worse and is what people configure when they want to see a whole rack.

Row four is the only arrangement that is safe, and it costs you the other direction.

And row three of the first table — capturing on the host under test — is the one that ruins Chapter 21.7 and Chapter 21.8 investigations. A capture process on the host consumes memory bandwidth, which is exactly the resource Chapter 21.7 §14's descriptor stalls contend for, and it takes interrupt-handling time from the driver whose completion latency Chapter 21.8 §11's Little's-law bound depends on. Capturing a throughput problem on the machine with the throughput problem changes the number you are measuring, in the direction that confirms your hypothesis.

So the rules are three.

NeverBecause
insert a tap without re-checking the link budgetthe tap's loss is in it
mirror more traffic than the destination can carrythe loss is biased toward bursts
capture on the host whose performance is in questionthe apparatus competes for the contended resource

15. RTL 7 — Capture Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capdiag_telemetry -- everything the apparatus knows about itself,
// in one readable block, including the fields that say its answer is
// weak.
//
// Chapter 19.7 Section 16's snapshot discipline applies: a set of
// counters read one at a time across a bus is not a consistent set,
// and for a self-completeness check an inconsistent set is worse than
// none, because the inconsistency looks like loss.
// ---------------------------------------------------------------------
module capdiag_telemetry
  import capdiag_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        snapshot,

  input  logic [31:0] i_frames_seen,
  input  logic [31:0] i_records_written,
  input  logic [31:0] i_self_dropped,
  input  logic [31:0] i_matched,
  input  logic [31:0] i_join_lost,
  input  logic [31:0] i_join_ambiguous,
  input  logic [31:0] i_out_of_order,
  input  logic [31:0] i_duplicate,
  input  logic [31:0] i_burst_peak_bytes,
  input  logic [31:0] i_sink_occupancy_mb,
  input  logic [15:0] i_self_drop_pct_x10,
  input  logic        i_coverage_unknown,
  input  logic        i_seq_field_absent,
  input  logic        i_single_point_only,
  input  logic        i_trailing_bits_absent,
  input  logic        i_apparatus_perturbs,

  output logic [31:0] o_frames_seen,
  output logic [31:0] o_records_written,
  output logic [31:0] o_self_dropped,
  output logic [31:0] o_matched,
  output logic [31:0] o_join_lost,
  output logic [31:0] o_join_ambiguous,
  output logic [31:0] o_out_of_order,
  output logic [31:0] o_duplicate,
  output logic [31:0] o_burst_peak_bytes,
  output logic [31:0] o_sink_occupancy_mb,
  output logic [15:0] o_self_drop_pct_x10,
  output logic [4:0]  o_caveats,
  output logic        o_result_is_interpretable
);
  // The five caveats, in the order they invalidate a conclusion.
  localparam int CAV_COVERAGE  = 0;   // no independent completeness check
  localparam int CAV_SELFDROP  = 1;   // the apparatus dropped
  localparam int CAV_AMBIGUOUS = 2;   // the join guessed
  localparam int CAV_NO_SEQ    = 3;   // ordering unanswerable
  localparam int CAV_PERTURBS  = 4;   // the tap or mirror is in the system

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      o_frames_seen <= '0; o_records_written <= '0; o_self_dropped <= '0;
      o_matched <= '0; o_join_lost <= '0; o_join_ambiguous <= '0;
      o_out_of_order <= '0; o_duplicate <= '0; o_burst_peak_bytes <= '0;
      o_sink_occupancy_mb <= '0; o_self_drop_pct_x10 <= '0;
      o_caveats <= '0;
    end else if (snapshot) begin
      o_frames_seen       <= i_frames_seen;
      o_records_written   <= i_records_written;
      o_self_dropped      <= i_self_dropped;
      o_matched           <= i_matched;
      o_join_lost         <= i_join_lost;
      o_join_ambiguous    <= i_join_ambiguous;
      o_out_of_order      <= i_out_of_order;
      o_duplicate         <= i_duplicate;
      o_burst_peak_bytes  <= i_burst_peak_bytes;
      o_sink_occupancy_mb <= i_sink_occupancy_mb;
      o_self_drop_pct_x10 <= i_self_drop_pct_x10;

      o_caveats[CAV_COVERAGE]  <= i_coverage_unknown;
      o_caveats[CAV_SELFDROP]  <= (i_self_dropped != 32'd0);
      o_caveats[CAV_AMBIGUOUS] <= (i_join_ambiguous != 32'd0);
      o_caveats[CAV_NO_SEQ]    <= i_seq_field_absent;
      o_caveats[CAV_PERTURBS]  <= i_apparatus_perturbs;
    end
  end

  // A capture is interpretable as EVIDENCE OF LOSS only if the
  // apparatus did not lose. Everything else is a qualification; this
  // one is disqualifying.
  assign o_result_is_interpretable = !o_caveats[CAV_SELFDROP] &&
                                     !o_caveats[CAV_COVERAGE];
endmodule

Classification: a snapshot register file whose last output is a verdict on whether the rest of it means anything.

What it teaches: that o_result_is_interpretable is deliberately asymmetric across the five caveats. Three of them — ambiguity, a missing sequence field, a perturbing apparatus — qualify a conclusion: they narrow what can be said. Two of them — the apparatus dropped, and there is no independent completeness check — destroy one specific conclusion entirely, namely the link lost frames. That is the only conclusion Module 21's hardest chapter needed, and it is the one the apparatus can fake.

And it teaches the snapshot discipline's second life. Chapter 19.7 §16 made a snapshot bank so that 27 counters read across a slow bus would be mutually consistent; here the same bank makes i_frames_seen and i_records_written consistent, and without it their difference — which is the self-drop count — contains the bus read latency as a term.

Deliberately simplified: no read-clear, so a long capture needs external differencing. o_caveats is five bits where the useful artefact is five bits plus the numbers that justify them — a report saying the apparatus dropped is much less useful than one saying the apparatus dropped 12.0%. And i_single_point_only and i_trailing_bits_absent are accepted and never used, which is the honest shape of a first implementation: they belong in the caveat set and there are only five bits.

Production implication: the two unused inputs are the two caveats most often missing from real capture reports, and both change conclusions. A single-point capture cannot support any statement about delivery — Section 6's table — yet single-point captures are routinely used to argue that a path is clean. A capture with no trailing-bit count cannot distinguish Chapter 21.2's alignment class from its check-sequence class, yet the analysis tool will confidently label every one of them a check-sequence error. Both caveats belong in the output, and widening o_caveats to seven bits is one line.


16. RTL 8 — The Capture Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// capdiag_conformance_monitor -- properties about the APPARATUS, which
// is the only subject in this chapter whose behaviour we control.
//
// Section 20 explains why the tempting property -- every frame on the
// wire appears in the capture -- is the one to refuse. These are what
// remains after refusing it, and every one of them is a statement
// about the instrument rather than about the link.
// ---------------------------------------------------------------------
module capdiag_conformance_monitor
  import capdiag_pkg::*;
(
  input logic        clk,
  input logic        rst_n,

  input logic        frame_at_point,
  input logic        record_written,
  input logic        sink_overflow,
  input logic        buffer_full,
  input logic [31:0] occupancy_mb,
  input logic [31:0] c_seen,
  input logic [31:0] c_written,
  input logic [31:0] c_self_dropped,
  input logic [31:0] c_matched,
  input logic [31:0] c_ambiguous,
  input logic        coverage_unknown,
  input logic        result_is_interpretable,
  input logic        triggered,
  input logic        capture_complete,
  input logic        armed
);
  // 1. Conservation INSIDE the apparatus. Seen equals written plus
  //    self-dropped -- a statement about the instrument's own
  //    bookkeeping, which it fully controls.
  p_apparatus_conservation: assert property (@(posedge clk) disable iff (!rst_n)
    c_seen == c_written + c_self_dropped);

  // 2. A record is written only for a frame the point saw. The
  //    converse is NOT asserted: that is the refused property.
  p_no_invented_records: assert property (@(posedge clk) disable iff (!rst_n)
    record_written |-> $past(frame_at_point) || frame_at_point);

  // 3. Overflow implies the buffer was full. The apparatus may not
  //    drop for any other reason without saying so.
  p_drop_only_on_full: assert property (@(posedge clk) disable iff (!rst_n)
    sink_overflow |-> buffer_full);

  // 4. Occupancy never exceeds the buffer.
  p_occupancy_bounded: assert property (@(posedge clk) disable iff (!rst_n)
    occupancy_mb <= BUFFER_MBYTES);

  // 5. The interpretability verdict is monotone: once the apparatus
  //    has dropped, no later cycle may claim the result is clean.
  p_interpretable_is_sticky: assert property (@(posedge clk) disable iff (!rst_n)
    (c_self_dropped != 0) |-> !result_is_interpretable);

  // 6. Coverage unknown is also disqualifying.
  p_unknown_coverage_disqualifies: assert property (@(posedge clk) disable iff (!rst_n)
    coverage_unknown |-> !result_is_interpretable);

  // 7. Matches never exceed frames written at the near point.
  p_matches_bounded: assert property (@(posedge clk) disable iff (!rst_n)
    c_matched <= c_written);

  // 8. Ambiguous matches are a SUBSET of matches, not a separate class.
  p_ambiguous_subset: assert property (@(posedge clk) disable iff (!rst_n)
    c_ambiguous <= c_matched);

  // 9. Completion follows a trigger, never precedes it.
  p_complete_after_trigger: assert property (@(posedge clk) disable iff (!rst_n)
    capture_complete |-> $past(triggered));

  // 10. Arming clears the previous verdict.
  p_arm_clears: assert property (@(posedge clk) disable iff (!rst_n)
    armed && $rose(armed) |-> !capture_complete);
endmodule

Classification: ten properties whose subject is uniformly the instrument, because the instrument is the only thing here with a specification.

What it teaches: that property 2's missing converse is the chapter. record_written |-> frame_at_point says the apparatus invents nothing; the converse, frame_at_point |-> record_written, says it loses nothingand Section 10 proved it loses between 12% and 35%. Asserting the converse does not make the apparatus complete; it makes the property fail, or worse, pass vacuously because frame_at_point is itself generated by the apparatus. Section 20 is that argument at full length.

And it teaches that properties 5 and 6 are the ones with teeth. They are monotonicity constraints on a verdict: once the apparatus has dropped, nothing may later report the capture as clean. In a system where an operator reads a summary rather than a counter, that monotonicity is the whole protection, and it is the same shape as Chapter 21.6 §16's sticky evidence-quality flag.

Deliberately simplified: property 1 is a conservation law across counters that in a real apparatus are committed at different stages, which is exactly Chapter 19.7 §19's rejected class 87 — it holds here only because the model commits all three in one cycle. Property 2's $past(frame_at_point) || frame_at_point is a two-cycle window chosen to fit the model's timing rather than derived from a pipeline depth. And property 9 uses $past(triggered) where the ring's completion is several frames later, so it is weaker than it looks.

Production implication: the ten properties here are worth having in a capture appliance's own verification, and every one of them is checkable without any reference to the link at all — which is what makes them the right properties. An appliance vendor can prove all ten in a testbench with a synthetic frame source, and a customer who asks does your appliance drop, and does it say so is asking about properties 3, 5 and 6. The question nobody can answer with a property is the one everybody asks — did the capture contain every frame — and Section 20 explains why that is not a shortcoming of the properties.


17. What a Capture Point Cannot See

Six chapters of this module enumerated fifty seven faults. A capture reaches fourteen outright, nine partially, and thirty four not at all, and the thirty four have only four reasons. Fourteen belong to the link failures chapter, where the link is down, negotiating or flapping, so there are no frames to capture and an empty file is consistent with every one of the fourteen sites. Six have their evidence below the frame: symbol errors in the interframe gap, forward error correction symbol corrections per codeword, block lock and alignment marker state, the interframe gap measured in octets rather than as a timestamp difference, and lane skew. The correction count is the dangerous one, because a link correcting six of a possible seven symbols per Reed Solomon codeword delivers bit perfect frames, so a clean capture is exactly what a link about to fail produces. Eight have their evidence in host memory rather than on any wire: descriptor ownership bits, the order in which a descriptor and its payload became visible, the ring's producer and consumer indices, and whether a completion was posted before its data landed. And six have no event at all, which is the throughput chapter's class: a link running at sixty five and a half gigabits per second against a hundred gigabit line rate produces entirely correct frames, correctly spaced, and the capture is a perfect record of a system working as designed.57 faultssix chaptersenumerated14 reached24.6% outright9 partial15.8% — 4 need twopoints34 unreachable59.6%No frames14 — all of 21.4Below the frame6 — FEC hides themarginIn host memory8 — 21.7's orderingNo event at all6 — 21.8's ceilingsFour reasonsnot thirty-four12
Figure 4 — thirty-four of fifty-seven faults, and only four reasons.

Section 4 counted 34 of 57 faults out of reach. This section says why, and the reasons fall into four groups rather than thirty-four.

GroupFaultsReason
there are no frames14Chapter 21.4's entire chapter
the evidence is below the frame6symbols, codewords and PCS state are not frames
the evidence is in memory, not on the wire8Chapter 21.7's host side
there is no event at all6Chapter 21.8's ceilings

Group one is Chapter 21.4's dark class and it is the largest. Fourteen sites, and on every one of them the link is down, negotiating, or flapping — so the capture file is empty or nearly so, and an empty file is consistent with all fourteen. Chapter 21.4 §5 showed that MDIO separates some of those sites and that the rest need a cooperating partner; a capture separates none of them. It is the one chapter of Module 21 where the instrument contributes exactly zero.

Group two is the most interesting and the least appreciated.

Below the frameSeen bySeen by a capture?
symbol errors in the interframe gapthe PCSno
FEC symbol corrections per codewordthe FEC decoderno
block-lock and alignment-marker statethe PCSno
the actual interframe gap in octetsthe MACno — only a timestamp difference
lane skewthe PMAno

Row two is the one that changes how you should think about a clean capture. Chapter 3.7 §4's KR4 code corrects up to seven 10-bit symbol errors per 528-symbol codeword, and KP4 corrects fifteen. A link correcting six symbols per codeword delivers frames that are bit-for-bit perfect, so a capture of a million frames shows a million perfect frames — and the link is one symbol from the cliff.

FEC's job is to make the wire look perfect. A capture looks at the wire after FEC has done its job, and therefore cannot see how hard the job was.

Chapter 3.7 §15 named this exactly — read the slope, not the cliff — and the instrument that reads the slope is the FEC decoder's pre-correction counters, which Chapter 3.7 §10 built. A capture is the wrong instrument, and it is wrong in the most dangerous direction: it returns a clean result from a link about to fail.

Group three is Chapter 21.7's host side.

In memoryCapture?
the descriptor's ownership bitno
the order in which a descriptor and its payload became visibleno
the ring's producer and consumer indicesno
whether a completion was posted before its data landedno

Every one of those is a write to host DRAM and no wire carries it. Chapter 21.7 §8's three ordering failures are all in this table, which is why Section 4 scored that chapter zero-outright — and why the capture's role there is as the reference half of Section 5's two-point comparison rather than as the instrument itself.

Group four is Chapter 21.8's and it is the strangest of the four. A link running at 65.5 Gb/s against a 100 Gb/s line rate is producing entirely correct frames, correctly spaced, with no errorsso the capture is a perfect record of a system working as designed, and the fault is that the design's ceiling is lower than the line rate. There is nothing in the frames to find. Chapter 21.8 §14's link with no errors that is slow is a capture's null case: the instrument works, returns a complete and accurate answer, and the answer contains no information about the problem.

One row of group four is an exception, and it is worth having. The spacing relationship of Section 8 — visible only in a capture — does distinguish a source that is not offering load from a path that is limiting it. Chapter 21.8 §13's offered-load question is answerable from gaps, at rates where the gap exceeds the timestamp resolution, which Section 3 showed means 1 Gb/s and not 100 Gb/s. That is the one ceiling of the four a capture reaches, and it is the partial in Section 4's table.


18. What the Capture Assumes

Every instrument has premises. A capture's are unusually numerous and unusually unstated.

AssumptionIf it is false
the apparatus recorded every frame it sawSection 10 — between 12% and 35% did not
the point saw every frame on the linka mirror may drop before the point; a tap may be on one fibre
the timestamp is close to the arrivalqueueing inside the apparatus displaces it
timestamps from two points are comparableSection 6 — two free-running clocks drift past the signal
the frames are as the wire carried thema span port shows them after the switch's parser
the analysis tool's classification is the design'sChapter 21.2's classes are not the tool's
the capture is representative of the link's behaviourit is 84 ms of a week

Row seven is the assumption that does the most damage and it is almost never examined. Section 12's arithmetic: a one-gigabyte capture is 84 milliseconds of a 100 Gb/s link. A week of operation is 604 800 seconds. So the capture covers 1.4 × 10⁻⁷ of the interval the problem was reported overone part in seven million — and it is then used to argue that the link is fine.

"I took a capture and it looked clean" is a statement about 84 milliseconds. The complaint was about a week.

Row six is the one that produces confident wrong answers. Chapter 21.2 §3 established that the error classes are defined by the receiving MAC's rules, including a tie-break; an analysis tool recomputes its own classification from the octets, with its own rules, and the two need not agree. A frame the design counted as an alignment error and the tool counts as a check-sequence error is the same frame, classified twice, by two authorities — and only one of them is the one whose counters the operator is trying to explain.

Row three deserves its number. A capture timestamp is applied where the timestamp is applied, which for a software capture is after the kernel has queued the frame, and that queue is tens of microseconds deep under load. Chapter 16.3 §4's whole argument — that a timestamp must be taken at the MAC/PHY boundary or it measures the software stack — applies to a capture apparatus identically, and a software capture's timestamps are therefore useful for ordering and useless for intervals below about a hundred microseconds. Which makes Section 8's 88.9-microsecond microburst invisible to exactly the tool people use to look for microbursts.

And one assumption is so basic it is usually not even articulated.

The frames in the fileare the frames that were on the wire
truncated at a snap length?commonly 64 or 128 octets — the payload is gone
stripped of the check sequence?most capture paths discard it
stripped of the VLAN tag?some NICs do, and the file then shows an untagged frame

Row three is Chapter 13.2's tag going missing between the wire and the file, and it has produced more than one confident diagnosis of a misconfigured trunk where the trunk was correct and the capture NIC had stripped the tag. Row two removes the ability to recompute the check sequence at all — the single most valuable thing a capture's octets support — and it is the default on several common capture paths.


19. The Cost, Accounted

Every chapter in Modules 19 to 21 has priced its mechanism in flops against Chapter 19.7 §19's 14 166-flop datapath. This chapter's mechanism is different in kind and the accounting shows it.

BlockFlopsShare of 14 166
observable_set320.2%
two_point_frame_join6 37845.0%
frame_relationship_extractor3882.7%
capture_sink_model1130.8%
capture_self_drop_detector970.7%
capture_trigger_ring1010.7%
capdiag_telemetry7125.0%
capdiag_conformance_monitor2041.4%
total8 02556.6%

One block is 45% of the total and it is the join. Sixty-four entries of digest, timestamp and occupancy — 6 378 flops for a reorder tolerance of sixty-four frames, which at 100 Gb/s minimum size is 430 nanoseconds of tolerance. A path with real queueing needs thousands.

Join windowFlopsAgainst the datapathTolerance at 100 Gb/s
646 20843.8%430 ns
25624 8321.75×1.72 µs
1 02499 3287.01×6.88 µs
4 096397 31228.0×27.5 µs

By a tolerance of four microseconds the join alone is seven times the entire receive datapath. That is the quantitative reason two-point analysis lives on a host and not in a MAC, and it is the same shape as Section 7's per-flow argument — both conclusions are that relationship analysis has a storage cost proportional to how much history it must hold, and a MAC holds none.

And the trigger ring's real cost is not in the table at all, because it is not flops.

StorageBitsAs flip-flops, against 14 166
1 024 frames at 1 518 octets1.24 × 10⁷878×
8 GB6.4 × 10¹⁰4.5 million ×

Row two is the number that ends the argument. An eight-gigabyte capture buffer, expressed in the unit this track has priced everything else in, is four and a half million times the entire receive datapath — so no capture of any useful length is ever going to be a piece of the MAC. It is DRAM on a host, and the flop accounting above describes only the small control blocks that manage it.

Which gives the chapter's cost result in its true form.

InstrumentCostReaches
Chapter 19.7's 27 counters2 672 flops, 18.9% of the datapath, always onthe aggregate, over all time
a capture8 025 control flops plus gigabytes of DRAM, plus a maintenance window84 ms, and 40.4% of the faults

Both rows are true simultaneously and that is the whole of Module 21's instrumentation argument. The counters are cheap, permanent, and blind to order, spacing and pairing. The capture is expensive, brief, and the only instrument that has them. Chapter 21.1 §12 put it sixth in the procedure; this accounting is why sixth is right, and Section 8 is why it is on the list at all.


20. Properties Worth Asserting, and One Worth Refusing

Every property in this section has the apparatus as its subject. That is not a stylistic choice — it is the only subject available, because the link has no specification the capture can check against.

Group A — the apparatus's own bookkeeping.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A1. Seen equals written plus self-dropped. The instrument's
//     conservation law, and the only one it fully controls.
p_cap_conservation: assert property (@(posedge clk) disable iff (!rst_n)
  c_seen == c_written + c_self_dropped);

// A2. Records are never invented.
p_cap_no_invention: assert property (@(posedge clk) disable iff (!rst_n)
  record_written |-> frame_at_point || $past(frame_at_point));

// A3. A drop happens only when the buffer is full.
p_cap_drop_needs_full: assert property (@(posedge clk) disable iff (!rst_n)
  self_drop |-> buffer_full);

// A4. Occupancy never exceeds the buffer.
p_cap_occ_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  occupancy_mb <= BUFFER_MBYTES);

// A5. Occupancy falls only by at most the sink's per-tick capacity.
p_cap_drain_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  (occupancy_mb < $past(occupancy_mb)) |->
    (($past(occupancy_mb) - occupancy_mb) <= SINK_MB_PER_MS));

// A6. Once full and still arriving, the apparatus MUST report a drop.
//     This is the property that makes the loss visible. Note that it
//     constrains the REPORT, not the completeness.
p_cap_full_reports: assert property (@(posedge clk) disable iff (!rst_n)
  (buffer_full && frame_at_point && !record_written) |-> self_drop);

A6 is the constructive replacement for the refused property and it is worth naming now rather than at the end. It does not say the apparatus keeps every frame. It says that when the apparatus does not keep one, it says so — which is a property of the instrument's design, is checkable, and is exactly the guarantee an operator needs.

Group B — the interpretability verdict.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// B1. Any self-drop disqualifies the "the link lost frames" conclusion.
p_cap_selfdrop_disqualifies: assert property (@(posedge clk) disable iff (!rst_n)
  (c_self_dropped != 0) |-> !result_is_interpretable);

// B2. No independent completeness reference disqualifies it too.
p_cap_unknown_disqualifies: assert property (@(posedge clk) disable iff (!rst_n)
  coverage_unknown |-> !result_is_interpretable);

// B3. The verdict is sticky within a capture: it never recovers.
p_cap_verdict_sticky: assert property (@(posedge clk) disable iff (!rst_n)
  (!result_is_interpretable && !arm) |=> !result_is_interpretable);

// B4. Arming is the only thing that may clear it.
p_cap_verdict_cleared_only_by_arm: assert property (@(posedge clk) disable iff (!rst_n)
  ($rose(result_is_interpretable)) |-> $past(arm));

// B5. The caveat bits are monotone within a capture.
p_cap_caveats_monotone: assert property (@(posedge clk) disable iff (!rst_n)
  !arm |=> ((o_caveats & $past(o_caveats)) == $past(o_caveats)));

Group C — the two-point join.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// C1. Matches never exceed frames recorded at the near point.
p_join_matches_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  c_matched <= c_written);

// C2. Ambiguity is a subset of matching, not a separate outcome.
p_join_ambiguous_subset: assert property (@(posedge clk) disable iff (!rst_n)
  c_ambiguous <= c_matched);

// C3. A match yields a NON-NEGATIVE transit time. A negative one means
//     the two clocks are not comparable -- Section 6's premise, made
//     checkable.
p_join_transit_nonneg: assert property (@(posedge clk) disable iff (!rst_n)
  matched |-> (b_time_ns >= a_time_at_match));

// C4. A match consumes exactly one window entry.
p_join_consumes_one: assert property (@(posedge clk) disable iff (!rst_n)
  matched |=> (window_occupancy == $past(window_occupancy) - 1));

// C5. An entry evicted while still occupied is counted as lost, once.
p_join_evict_counts: assert property (@(posedge clk) disable iff (!rst_n)
  (a_valid && a_occ_at_wp) |=> (c_lost == $past(c_lost) + 1));

// C6. Lost plus matched never exceeds what point A saw.
p_join_accounting: assert property (@(posedge clk) disable iff (!rst_n)
  (c_lost + c_matched) <= c_a_seen);

C3 is the most useful property in the group and it is a premise check rather than a design check. A negative transit time is impossible if the clocks are related and commonplace if they are not, so the property fires exactly when Section 6's clock assumption has been violated — and it is far more likely to fire on a real deployment than any of the design properties around it.

Group D — the relationship extractor.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// D1. Ordering verdicts require the sequence field.
p_rel_order_needs_seq: assert property (@(posedge clk) disable iff (!rst_n)
  out_of_order |-> f_seq_valid);

// D2. So do duplicate verdicts.
p_rel_dup_needs_seq: assert property (@(posedge clk) disable iff (!rst_n)
  duplicate_seq |-> f_seq_valid);

// D3. Order and duplicate are mutually exclusive on one frame.
p_rel_order_dup_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
  !(out_of_order && duplicate_seq));

// D4. The burst peak is monotone within a capture.
p_rel_peak_monotone: assert property (@(posedge clk) disable iff (!rst_n)
  !arm |=> (burst_peak_bytes >= $past(burst_peak_bytes)));

// D5. Back-to-back implies a gap at or below the rate's floor.
p_rel_b2b_gap: assert property (@(posedge clk) disable iff (!rst_n)
  back_to_back |-> (gap_ns <= MIN_FRAME_PLUS_GAP_NS));

// D6. A gap is never negative -- the single-point clock is monotone.
p_rel_gap_nonneg: assert property (@(posedge clk) disable iff (!rst_n)
  f_valid && have_last |-> (f_time_ns >= last_time));

Group E — the trigger.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// E1. Completion never precedes a trigger.
p_trig_complete_after: assert property (@(posedge clk) disable iff (!rst_n)
  capture_complete |-> triggered);

// E2. A trigger fires only while armed.
p_trig_needs_armed: assert property (@(posedge clk) disable iff (!rst_n)
  $rose(triggered) |-> $past(armed));

// E3. Exactly one trigger per arming.
p_trig_once: assert property (@(posedge clk) disable iff (!rst_n)
  triggered && !arm |=> triggered);

// E4. A short pre-trigger window is reported, not hidden.
p_trig_context_reported: assert property (@(posedge clk) disable iff (!rst_n)
  ($rose(triggered) && (ring_fill < PRE_TARGET)) |-> trigger_lost_context);

// E5. Post-trigger frames are bounded by the post-trigger target.
p_trig_post_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  post_frames_taken <= POST_TARGET);

// E6. Arming empties the ring's accounting.
p_trig_arm_resets: assert property (@(posedge clk) disable iff (!rst_n)
  arm |=> (ring_fill == 0 && post_frames_taken == 0));

Group F — the observable set.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// F1. Nothing outside IN_A_CAPTURE is ever claimed available.
p_obs_no_extra: assert property (@(posedge clk) disable iff (!rst_n)
  (available & NOT_IN_CAPTURE) == 9'b0);

// F2. A missing trailing-bit count makes alignment undiagnosable, and
//     the block must say so.
p_obs_alignment_flag: assert property (@(posedge clk) disable iff (!rst_n)
  !available[OBS_TRAILING] |-> alignment_undiagnosable);

// F3. A passive tap does not perturb; anything else does.
p_obs_perturb: assert property (@(posedge clk) disable iff (!rst_n)
  perturbs_the_system == (POINT != PT_TAP));

// F4. The gap is never presented as an octet count.
p_obs_gap_is_time: assert property (@(posedge clk) disable iff (!rst_n)
  gap_is_a_timestamp_difference);

// F5. Frames captured only ever increases.
p_obs_count_monotone: assert property (@(posedge clk) disable iff (!rst_n)
  c_frames_captured >= $past(c_frames_captured));

Thirty-four properties, and every one has the instrument as its subject. Add the conformance monitor's ten and the chapter carries forty-fourand not one of them says anything about the link. That is the honest position: the capture has no specification for the link to violate, so a property about the link would be a property about the analysis tool's opinion, which is Chapter 21.2 §3's two-authority problem.


21. Verification Scenarios

Fifty-eight scenarios. The subject under test is the apparatus, and the hardest scenarios are the ones where the apparatus is wrong and looks right.

Group 1 — the sink and its buffer (12).

#ScenarioExpect
1sink at 12.5 GB/s, buffer never fillsself_drop never asserts; coverage 100%
2sink at 3 GB/s, 8 GB buffer, 500 ms captureno drop — buffer fills at 842 ms
3the same, 1 000 ms1.50 GB dropped, self_drop_pct_x10 = 120
4sink at 1 GB/s, 1 000 ms3.50 GB dropped, 28.0%
5sink at 0.125 GB/s, 1 000 ms4.37 GB dropped, 35.0%
6sink rate halves mid-capture (SLC exhaustion)drop begins partway; the rate is not constant
7offered load at 50% of linebuffer drains; no drop with a 6 GB/s sink
8offered load 50%, sink 1 GB/sstill drops — 6.25 exceeds 1.00
9buffer full, then link goes idleoccupancy falls at the sink rate, not instantly
10BUFFER_MB = 0every frame drops; p_cap_drop_needs_full still holds
11occupancy forced above BUFFER_MBYTESp_cap_occ_bounded fires
12a drop injected with the buffer not fullp_cap_drop_needs_full fires

Group 2 — the self-drop detector and interpretability (10).

#ScenarioExpect
13no port counter availablecoverage_unknown; result_is_interpretable low
14port counter available, counts agreecoverage 100%; interpretable high
15port counter read one minute after the capture stopsapparent self-drop that is entirely the minute
16port counter snapshotted before and afterthe difference is correct; coverage is real
17one frame self-dropped, then a clean hourverdict stays false — B3
18re-arm after a self-dropverdict may clear — B4
19a caveat bit set, then its input deassertsthe bit stays set — B5
20c_seen incremented without c_written or self-dropp_cap_conservation fires
21a record written with no frame at the pointp_cap_no_invention fires
22span port drops before the capture pointthe detector sees nothing — the known hole

Group 3 — the two-point join (12).

#ScenarioExpect
23every frame appears at both points, in orderc_matched = frames; c_lost = 0
24one frame lost between the pointsone eviction counted in c_lost
25frames reordered by 8 within a window of 64all match; transit times vary
26reordered by 80 within a window of 64evictions; false losses
27two identical frames inside the windowc_ambiguous increments
28a 32-bit digest collision, forceda wrong match; transit time meaningless
29B's clock 1 ms ahead of A'sp_join_transit_nonneg holds but the value is wrong
30B's clock 1 ms behind A'sp_join_transit_nonneg fires — the useful case
31a frame at B with no A recordb_without_a
32contents altered between A and Bno digest match — reported as content_differs
33c_matched forced above c_writtenp_join_matches_bounded fires
34c_ambiguous forced above c_matchedp_join_ambiguous_subset fires

Group 4 — relationships (10).

#ScenarioExpect
35frames with no sequence field, reorderedout_of_order never asserts — D1
36sequence present, one frame lateout_of_order once
37the same sequence twiceduplicate_seq; not out_of_order — D3
38sequence wraps at 2^32a false out_of_order — the known simplification
39back-to-back minimum frames at 100 Gb/sback_to_back every frame; gap 6.72 ns
40the same at 1 Gb/s with the 100 Gb/s constantback_to_back never asserts — miscalibrated
41an 88.9 µs burst of 1 518-octet framesburst_peak_bytes ≈ 1.11 MB
42the burst straddling the window boundarytwo half-peaks — the jumping-window artefact
43burst_peak_bytes forced to decreasep_rel_peak_monotone fires
44a timestamp that goes backwardsp_rel_gap_nonneg fires

Group 5 — the trigger (8).

#ScenarioExpect
45arm, fill the ring, trigger on a bad check sequence75% pre, 25% post; trigger_lost_context low
46trigger 10 frames after armingtrigger_lost_context high
47a burst of 100 bad framesone trigger; trigger_was_first_of_a_burst
48the link idles after the triggercompletion waits — the frames-not-time flaw
49completion forced before a triggerp_trig_complete_after fires
50a trigger while not armedp_trig_needs_armed fires
51post_frames_taken forced above the targetp_trig_post_bounded fires
52re-arm mid-capturering accounting resets — E6

Group 6 — the observable set and the perturbation (6).

#ScenarioExpect
53trailing bits not recordedalignment_undiagnosable high — F2
54POINT = PT_TAPperturbs_the_system low; sees_before_phy high
55POINT = PT_SPANperturbs_the_system high
56available forced to claim OBS_SYMBOLp_obs_no_extra fires
57a capture path that strips the VLAN tagthe frame reads untagged — Section 18's row three
58a snap length of 64 octetspayload absent; the check sequence unrecomputable

Scenario 15 is the one that catches real tooling and scenario 22 is the one that cannot be caught at all.

The directed test random stimulus will not produce

Random stimulus explores frame content, frame timing and apparatus state independently. This test requires all three to be arranged against each other, in a specific order, and the point of the test is that the apparatus's output is CORRECT-LOOKING and WRONG.

The arrangement:

StepAction
1a two-point setup, A a passive tap and B the host's receive queue, clocks disciplined
2the sink is set to 3 GB/s and the buffer to 8 GB — Section 10's row three
3the link runs at full 100 Gb/s line rate with every frame correct
4the capture runs for 1 000 ms, so the apparatus drops 12.0% after 842 ms
5during the last 158 ms, a real fault is injected: 200 frames are genuinely lost between A and B
6the port counter is NOT read — coverage_unknown is high throughout

Random stimulus produces steps 3 and 5 readily and will not produce step 4 aligned with step 5, because the apparatus's overflow is a function of elapsed capture time and the fault injection is a function of the stimulus generator's own ratetwo independent processes, and the test needs them overlapped in a specific 158-millisecond window. A generator that randomises both will hit the overlap with probability proportional to the window, and will never hit the part that makes the test diagnostic: that the genuine loss is a rounding error against the manufactured one.

The oracle is in four parts, and no single part is sufficient.

PartSignalRequired valueWhy this part alone is not enough
1 — the apparatus admits the dropc_self_dropped≈ 1.5 GB worth, non-zerosays nothing about the genuine 200
2 — the verdict is disqualifiedresult_is_interpretable0, from cycle 842 ms onwardcould be set by coverage_unknown alone
3 — the join's loss count is inflatedc_lostfar above 200without part 1 it looks like a link fault
4 — the genuine loss is NOT separablec_lost − apparatus dropnot computable from the capture alonethis is the finding, and it is a NEGATIVE result

Part 4 is the assertion that makes the test worth writing, and it is an assertion about what the apparatus cannot do. The 200 genuinely lost frames are real, they are in the window, and they are indistinguishable from the roughly 18 million frames the apparatus discarded in the same interval. A ratio of about 90 000 to one. The test passes when the apparatus refuses to report a link loss figure at all, and fails when it reports one — including when it reports one that happens to be close to 200, because a right answer produced by an instrument with no basis for it is the failure this chapter exists to prevent.

Run the test a second time with the port counter snapshotted at both ends. Part 2's verdict then flips on c_self_dropped alone, part 4 becomes computable — the port counter's delta minus c_written gives the apparatus's loss, and the residual is the link's — and the same 200 frames become a reportable finding. The difference between the two runs is one management command, and it is the difference between an uninterpretable capture and a diagnosis.


22. Debugging With a Capture

A procedure, in the order the arithmetic supports rather than the order the tools encourage.

StepDoBecause
1read the port counters first, and snapshot themChapter 19.7's 27, free, already running, and the only completeness reference
2decide what a capture could possibly showSection 4 — 59.6% of Module 21's faults it cannot reach
3if the answer is "nothing", stopChapter 21.4's fourteen sites are the clearest case
4choose the point, and write down what it perturbsSection 14 — a tap eats the link budget, a mirror drops bursts
5size the capture against Section 12's table1 GB is 84 ms; a 1-in-10⁹ fault needs 80 GB
6filter before the store if you have a hypothesisthe 1/p multiplier — 84 ms becomes 23.3 hours
7trigger if you do nota ring plus a stop condition beats a longer file
8capture, then read the port counters againthe delta is the only completeness evidence
9check the apparatus's own drop counter before anything elsea non-zero value disqualifies every loss conclusion
10analyse relationships before contentsSection 8 — relationships are what only this instrument has

Step 1 before step 4 is the whole procedure's point. Chapter 21.1 §12 put the capture sixth; this puts the counters before the capture even gets configured, because the counters are what will decide whether the capture means anything.

Three specific readings, each of which has produced a wrong diagnosis in practice.

ObservationTempting readingCheck first
frames missing from the capturethe link dropped themthe apparatus's drop counter, and the port counter delta
check-sequence errors in the capturethe channel is badwhether a tap was inserted without a budget check — Section 14
a clean capture on a failing linkthe fault is in softwareFEC pre-correction counts — Section 17's group two

Row three is the one that wastes the most time and the reason is in Chapter 3.7 §12. A link correcting six symbols per KR4 codeword delivers perfect frames and is one symbol from uncorrectable; the capture is clean, the frames are correct, and the link fails an hour later when the temperature rises. The instrument that would have shown it is the FEC decoder's own counter, not a bigger capture.

And one reading that is nearly always right.

ObservationReading
a burst peak far above the mean, with a drop counter movingChapter 21.6's tail drop, driven by a microburst

That is the capture earning its cost. No counter in Chapter 19.7's 27 can show it — a sum over a second represents an 88.9-microsecond burst as 0.0089% — and the relationship extractor of Section 7 shows it from the timestamps the apparatus already recorded. One question, out of a module's worth, where this is the right instrument and nothing else is.


23. Misconceptions

Six, each in the form: the wrong model, what believing it costs, and the corrected model.

Misconception 1 — "a capture shows you everything on the wire."

Wrong model: the apparatus is a transparent window; what was on the wire is in the file.

What it costs: every conclusion about loss. Section 10's arithmetic says that at 100 Gb/s an ordinary apparatus discards 12.0% to 35.0% of a one-second capture, and nothing in the file marks them. A missing frame in the file is read as a missing frame on the link, which is Chapter 21.6's hardest question answered by the instrument's own limitation.

Corrected model: the file is what the apparatus kept, which at 100 Gb/s is what its storage path could absorb. Completeness is not a property of the file — it is a comparison between the file and the port counters, and without that comparison there is no completeness evidence at all.

Misconception 2 — "a bigger buffer fixes it."

Wrong model: the apparatus drops because the buffer is small; buy a bigger one.

What it costs: money, and the wrong instrument. A buffer only postpones: 8 GB lasts 842 ms with a 3 GB/s sink, 64 GB lasts 6.74 s, and a terabyte of DRAM would last 105 seconds. The excess is 9.5 GB/s forever, so every buffer is a countdown. And Section 12 shows a terabyte is 84 seconds of wire time anyway, so the buffer was never the limit on what a capture could see.

Corrected model: the fix is on the arrival side, not the storage side. A pre-store filter divides the arrival rate by the selectivity, and at a 1-in-10⁶ fault rate it turns 84 milliseconds into 23.3 hours on the same gigabyte. Filtering is a factor of a million; buffering is a factor of eight.

Misconception 3 — "the capture is objective; the counters can lie."

Wrong model: counters are derived quantities computed by the design under suspicion; the capture is raw evidence.

What it costs: the completeness check, and it inverts the actual reliability. Chapter 19.7's counters run continuously, cost 2 672 flops, and have seen 9.00 × 10¹³ frames over a week. A 1 GB capture holds 1.25 × 10⁷7.2 million times fewer — and was taken by an apparatus with a known, unreported loss rate. The raw evidence is the smaller and less trustworthy sample.

Corrected model: the two instruments are complementary and neither dominates. Counters have the population; captures have the relationships. Section 19's table is the honest comparison, and the right procedure uses the counters to validate the capture rather than the other way round.

Misconception 4 — "put a tap in; it's passive, so it changes nothing."

Wrong model: passive means non-intrusive.

What it costs: the link's error budget and therefore the diagnosis. A 50/50 tap costs about 3 dB on the through path and the entire channel insertion-loss allowance for 100GBASE-SR4 over 100 m of OM4 is under 2 dB. So the tap takes the link out of spec, errors appear, and they appear in the capture — which is consistent with the channel having been marginal all along. Chapter 21.2's channel class, manufactured by the instrument.

Corrected model: passive describes the tap's electronics, not its effect on the optical budget. A tap is a component of the link and belongs in the link budget, exactly like a patch panel. Re-compute the budget before inserting one, and if there is no margin, use a span port and accept its bias instead.

Misconception 5 — "a capture at the receiver tells you what the sender sent."

Wrong model: one point is enough, because frames do not change in transit.

What it costs: Chapter 21.7's entire fault group, and any statement about delivery. Section 6's table: four of Module 21's nine partial faults become reachable only with a second point, and all of them are questions of the form did what arrived match what left. A single point answers what was here; it never answers what happened between here and there.

Corrected model: transit questions need two observations and a join, the join needs a key neither end can mintChapter 20.6 §4's problem — and the join's ambiguity rate must be published alongside its match rate, or a digest collision reports a delivery that did not happen.

Misconception 6 — "the capture was clean, so the link is healthy."

Wrong model: correct frames imply a correct physical layer.

What it costs: the warning. Section 17's group two: FEC exists to make the wire look perfect. Chapter 3.7 §4's KR4 code corrects up to seven symbol errors per codeword; a link correcting six delivers bit-perfect frames and is one symbol from the cliff. A capture of a million perfect frames is exactly what a link about to fail produces, and Chapter 3.7 §15 named the instrument that does show it.

Corrected model: a capture observes the wire after the physical layer has repaired it, so it reports the PHY's output, never its margin. The margin lives in the FEC decoder's pre-correction symbol counts, which Chapter 3.7 §10 built and which cost almost nothing to read. Read the slope, not the cliff — and a capture only ever shows the cliff.


24. Interview Questions

Six, with what a strong answer contains.

1. Why do the debugging chapters all put a packet capture last?

A strong answer gives the arithmetic rather than the folklore. A capture reaches 24.6% of Module 21's fault inventory outright and 40.4% including partials, costs a maintenance window, and covers 84 milliseconds per gigabyte at 100 Gb/s. The counters that would be consulted first are free, already running, and have a sample 7.2 million times larger. A stronger answer adds the exception: relationships between frames are the one thing no counter can ever supply, so the capture is last on the list and it is genuinely on the list.

2. A capture shows 3% of frames missing. What do you conclude?

The expected answer is the link is dropping. The correct answer is: nothing, until the apparatus's own drop counter and the port counter delta are known. At 100 Gb/s an ordinary apparatus loses 12% or more, so 3% is comfortably inside the instrument's noise floor — and a strong answer names the procedure that resolves it: snapshot the port counters immediately before and after, and compare against c_written plus c_self_dropped.

3. Why is a sum unable to detect reordering, however many sums you keep?

Because addition is commutative. Every permutation of a frame sequence produces identical totals, so the information is destroyed by the function rather than by the implementation — no counter width, resolution or count changes it. A strong answer names the alternative: a block that keeps the previous sequence value, which is state proportional to the number of flows, and prices it — 10 000 flows at about 130 flops each is 1.3 million flops, 92 times the entire receive datapath.

4. You insert an optical tap and check-sequence errors appear. What happened?

Most likely the tap. A 50/50 tap costs ≈3 dB and the 100GBASE-SR4 channel allowance over 100 m OM4 is under 2 dB, so the insertion took the link out of spec. A strong answer notes that the evidence is symmetric — the capture cannot distinguish a marginal channel from a channel the instrument made marginal — and that the resolution is to remove the tap and see whether the port's error counters return to zero, which is an experiment on the apparatus rather than on the link.

5. What single change most increases a capture's diagnostic power?

A second capture point — Section 6's table turns four unreachable questions into reachable ones. A strong answer names the cost honestly: twice the storage, a common time reference, and a join whose ambiguity must be measured — and notes the alternative answer, a pre-store filter, which multiplies covered wire time by 1/p and is cheaper, but requires a hypothesis you may not have.

6. The capture is clean and the link keeps failing. Where do you look?

Below the frame. FEC repairs the wire before anything a capture can see; a link correcting six of a possible seven symbols per KR4 codeword produces perfect frames. A strong answer names the instrument — pre-correction symbol counts from the FEC decoder — and adds the two other below-the-frame sources a capture misses: symbol errors in the interframe gap, and block-lock or alignment-marker state. The strongest answer observes that this is the failure mode where a capture is worse than no instrument, because it returns a confident clean result.


25. Questions and Answers


26. What's Next

Module 21 is complete. Nine chapters, 57 enumerated faults, and one instrument chapter that spends most of its length explaining what the instrument cannot do.

ChapterIts question
Chapter 21.1where in the receive path, and how do you narrow it
Chapter 21.2which class of error, and who decides the class
Chapter 21.3what a check sequence's one bit does and does not tell you
Chapter 21.4fourteen sites, no frames, and what MDIO separates
Chapter 21.5a mismatch priced against load, and which counters move at each end
Chapter 21.6the frames that arrived nowhere, without the optional counters
Chapter 21.7the descriptor ownership space, and the consumer stall
Chapter 21.8three ceilings below the line rate, and the lowest one
Chapter 21.9what the instrument of last resort is actually for

The module's closing result is a division of labour. Chapter 19.7's counters hold the population — every frame, over all time, for 2 672 flops. A capture holds the relationships — order, spacing and pairing — for 84 milliseconds and a maintenance window. Neither substitutes for the other, and the commonest debugging error in the module is reaching for the second when the first has not been read.

Module 22 turns to a setting where almost every assumption in Modules 19 to 21 changes. Automotive Ethernet runs over a single twisted pair, at rates chosen for cost rather than throughput, in a system where a frame's deadline is a safety property rather than a performance target, and where the link is expected to survive fifteen years and 125 °C rather than a data centre's three-year refresh. The counters, the taxonomy, and the instruments all follow — and the first question is why a whole separate physical layer was necessary at all.

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.