Ethernet · Module 21
Link Failures
A down link has no frames, so every instrument in Module 21 is unavailable at once; the replacement space has fourteen sites and MDIO separates nine classes of them.
Every instrument Module 21 has built is a statistic over frames. A link that will not come up produces none of them, so all of it is unavailable at once.
| Instrument | From | Available on a link that will not come up? |
|---|---|---|
| twelve fault sites | Chapter 21.1 §1 | no — all twelve are receive-path stages frames pass through |
| the conservation identity | Chapter 21.1 §7 | no — frames_in is zero |
| thirteen error classes | Chapter 21.2 §12 | no — every one is a predicate on a frame |
| the length slope | Chapter 21.3 §4 | no — it is a ratio over size buckets |
| the residue comb | Chapter 21.3 §8 | no |
| the clustering test | Chapter 21.3 §9 | no |
Six for six. And it is worse than a table of unavailable tools: 332 of the 557 properties Modules 19, 20 and 21 have published — 59.6% — have an antecedent that requires a frame, so on exactly the fault this chapter is about three of every five checks go quiet rather than failing.
What replaces them is a different fault space with a different shape.
| Count | |
|---|---|
| sites a link that will not come up can be broken at | 14 |
| observables, all of them registers or actions | 12 |
| classes MDIO alone separates them into | 9 |
| sites in the largest of those classes | 5 |
| classes with a cooperating partner as well | 11 |
| sites no observation in the space ever separates | 3 |
And the structural contrast with Chapter 21.1 is exact and opposite. There, twelve sites were separable and the search was inefficient — seven observations against a floor of four. Here the search is optimal — four observations, which is the floor — and the space is not separable. Different problem, different fix: Chapter 21.1 wanted better questions; this chapter wants a second observer.
1. Scope, and a Fault Space With No Frames In It
Scope: a link that never reaches a usable state. The sites it can be broken at, the registers that observe them, and what is left unseparated when every register has been read.
Not in scope: a link that comes up and then misbehaves. Chapter 21.1, Chapter 21.2 and Chapter 21.3 own that entirely, and the boundary between the two chapters is sharp: frames exist, or they do not. Nor negotiation's outcome — Chapter 21.5 takes a link that comes up at the wrong speed or duplex, which is a different failure from one that does not come up at all.
Start from why the previous three chapters stop.
Chapter 21.1 §1's twelve sites are the receive path — the channel, the PHY's lane alignment, the xMII, preamble detection, the parser, the check engine, the validity classifier, the filter, VLAN membership, the FIFO, the DMA, the driver. Every one of them is a stage a frame passes through, and its fault is detected by observing what happened to frames that passed.
A link that will not come up has passed nothing. So:
| Reads | |
|---|---|
frames_in | 0 |
frames_out | 0 |
| every RMON error counter | 0 |
| Chapter 21.1 §3's candidate mask | all twelve bits still set, and none of them right |
Row four is the specific failure and it is worth stating plainly. Chapter 21.1's method intersects a mask with the sites each observation is consistent with. With every counter at zero, every observation is EV_ZERO — and a zero eliminates the sites its counter moves for, so the mask converges quickly and confidently on the sites that no counter watches. Which is class G: the filter, VLAN, the FIFO, the DMA and the driver, none of which is the fault, and all of which are downstream of a link that never came up.
Chapter 21.1's method does not fail to diagnose a down link. It diagnoses one, wrongly, with five candidates and full confidence — because every one of its observations reads as evidence.
That is the strongest argument for treating this as a separate chapter rather than an extension. A method whose failure mode on an out-of-scope input is a confident wrong answer needs an explicit guard, and Section 16's monitor is that guard: frames_in at zero is not an observation, it is a different problem.
Three things this chapter is and is not:
| Choice | |
|---|---|
| the space is three-party | local, medium, remote — and that is the whole difficulty |
| the instruments are registers and actions | not statistics — there is no population to be statistical about |
| it stops at the partner's boundary | what the partner will tell us is a protocol question, not ours |
Row one is the structural difference from every previous chapter in the module. Chapter 21.1's twelve sites are all ours; a frame that reaches our parser was already ours to account for. Here a failing receive gate implicates three parties at once — our receiver, the medium, and their transmitter — and MDIO reads exactly one of the three.
2. The Ladder as a Fault Space
Chapter 3.8 §2 built the bring-up ladder as a sequence of gates. This section reads it backwards: as a partition of the ways a link can fail.
The six gates, and what each one's failure means:
| Gate | Chapter 3.8 §2's dependency | If it never passes |
|---|---|---|
| signal detect | physical | nothing is arriving at all |
| clock lock | needs signal | something arrives and has no recoverable timing |
| block lock | needs clock | bits arrive and do not parse as blocks |
| marker lock | needs block lock | blocks parse and no marker is recognised |
| lane deskew | needs marker lock on every lane | markers found and the lanes will not align |
| codeword lock | needs deskew | lanes aligned and the FEC structure will not lock |
Plus two gates that are not on the receive ladder and fail differently:
| Gate | Owner | If it never passes |
|---|---|---|
| auto-negotiation complete | both ends, jointly | Chapter 11.1's exchange never resolves |
| the link stays up | Chapter 4.4 | it comes up and drops — a different symptom entirely |
Now the fourteen sites, arranged by party, because the party is what decides who can observe them.
| # | Site | Party |
|---|---|---|
| 1 | our transmit driver or optic | local — and unobservable locally |
| 2 | our receive front end | local |
| 3 | our clock recovery — Chapter 3.4 §10's PMA | local |
| 4 | our block lock — Chapter 3.4 §11's aligner | local |
| 5 | our lane deskew — Chapter 3.8 §7 | local |
| 6 | our FEC codeword lock — Chapter 3.7 | local |
| 7 | our auto-negotiation state machine — Chapter 3.8 §8 | local |
| 8 | our elastic buffer — Chapter 4.4 §9 | local |
| 9 | the medium: attenuation, a break, a bend | medium |
| 10 | the medium: polarity inverted or lanes swapped | medium |
| 11 | the medium: the wrong optic or cable category | medium |
| 12 | their transmit driver or optic | remote |
| 13 | their configuration — forced where we auto-negotiate | remote |
| 14 | their port administratively down | remote |
Eight local, three medium, three remote — and the count is the point. Chapter 21.1's twelve sites were twelve of ours. Six of these fourteen are not ours at all, and nothing in our chip can read them.
Two of the fourteen deserve a note because they are the ones people leave out.
Site 1 — our own transmitter — is invisible from here. Whatever our driver is doing, we do not receive it. Every register in our PHY describes the receive direction; our transmit direction is observable only at the far end. A dead local transmitter therefore produces a perfect local status vector, which is the single most misleading reading in the chapter and is Section 8's subject.
Site 8 — the elastic buffer — is not a bring-up fault at all. Chapter 4.4 §9 separates overrun from underrun and both happen after the link is up: the ladder completes, traffic flows, and the buffer drifts to a limit. The symptom is a link that comes up and drops, repeatedly, and it is in this chapter's space because that is what the ticket says — not because the ladder failed.
| The ticket says | Which space is it in? | |
|---|---|---|
| "the link is down" | the ladder never completed | this chapter |
| "the link keeps bouncing" | it completes and then does not hold | this chapter, site 8 |
| "the link is up and lossy" | frames exist | Chapter 21.1 |
Row two is the reason Section 13 exists, and distinguishing it from row one costs one timer.
3. RTL 1 — The Bring-Up Package and the Gate Vector
// ---------------------------------------------------------------------
// linkdiag_pkg -- fourteen sites, twelve observables, and the one
// structural fact that shapes everything: the receive ladder is a
// MONOTONE CHAIN, so five registers have six readings and not
// thirty-two.
//
// Chapter 3.4 Section 13 built the ordered status vector and said the
// lowest deasserted bit names the sublayer. This package says what
// that is worth in bits, and it is 2.585.
// ---------------------------------------------------------------------
package linkdiag_pkg;
// Section 2's fourteen, grouped by PARTY because the party decides
// who is able to observe the site at all.
typedef enum logic [3:0] {
S_L_TX = 4'd0, // our transmitter -- unobservable locally
S_L_RX_FE = 4'd1,
S_L_CDR = 4'd2,
S_L_BLOCK = 4'd3,
S_L_DESKEW = 4'd4,
S_L_FEC = 4'd5,
S_L_AN = 4'd6,
S_L_ELASTIC = 4'd7, // comes up and drops -- Section 13
S_M_ATTEN = 4'd8,
S_M_POLARITY = 4'd9,
S_M_WRONGOPT = 4'd10,
S_R_TX = 4'd11,
S_R_CONFIG = 4'd12,
S_R_ADMIN = 4'd13
} site_e;
typedef logic [13:0] site_mask_t;
localparam site_mask_t ALL_SITES = 14'h3FFF;
// Party masks. Section 6: what MDIO reads is entirely the LOCAL
// receive direction, and six of the fourteen sites are not local.
localparam site_mask_t LOCAL_SITES = 14'b00_0000_1111_1111;
localparam site_mask_t MEDIUM_SITES = 14'b00_0111_0000_0000;
localparam site_mask_t REMOTE_SITES = 14'b11_1000_0000_0000;
// Chapter 3.8 Section 2's ladder, lowest rung first. The ORDER is
// the diagnostic and the MONOTONICITY is the cost -- Section 4.
typedef enum logic [2:0] {
G_SIGNAL = 3'd0,
G_CLOCK = 3'd1,
G_BLOCK = 3'd2,
G_MARKER = 3'd3,
G_FEC = 3'd4,
G_NONE = 3'd7 // every rung passed
} gate_e;
localparam int N_GATES = 5;
// Section 8: the class MDIO cannot break apart. Five sites, one
// reading -- "nothing is arriving", which is true of a dead receiver,
// a broken cable, a wrong optic, a dead far-end transmitter and a
// port somebody turned off.
localparam site_mask_t CLASS_DARK = 14'b10_1101_0000_0010;
// Section 17: the three that survive every observation in the space.
localparam site_mask_t CLASS_UNSEPARABLE = 14'b10_0101_0000_0000;
function automatic int popcount14(site_mask_t m);
int n = 0;
for (int i = 0; i < 14; i++) if (m[i]) n++;
return n;
endfunction
// Chapter 3.4 Section 13's rule, as a function: the lowest deasserted
// bit names the rung that owns the problem.
function automatic gate_e lowest_failed(logic [N_GATES-1:0] gates);
for (int i = 0; i < N_GATES; i++) if (!gates[i]) return gate_e'(i);
return G_NONE;
endfunction
endpackageClassification: a package whose central content is a monotone chain and three party masks.
What it teaches: that the party masks are the chapter's real type distinction. Chapter 21.1's twelve sites needed no such thing — they were all ours. Here LOCAL_SITES has eight members and the other six are behind an interface nothing in our chip reads, so a diagnosis that does not track which party a candidate belongs to cannot say whether the next step is a register read or a phone call.
And it teaches that lowest_failed is Chapter 3.4 §13's rule written as code, and that it is a projection. The five ladder bits collapse to one gate number; Section 4 shows that the collapse loses nothing, because the chain has only six reachable states anyway.
Deliberately simplified: CLASS_DARK and CLASS_UNSEPARABLE are literals where they should fall out of Section 6's incidence table by construction — Section 16's monitor recomputes them and compares. The ladder has five rungs where Chapter 3.8 §2 notes a single-lane link has three, so G_MARKER and G_FEC are trivially passed on such a link and the package does not say so. And site_e has no representation of two simultaneous faults, which Section 18's row seven is about.
Production implication: LOCAL_SITES is the mask worth printing at the top of any automated link diagnosis. Eight of fourteen candidates can be investigated by the machine holding the port; six cannot, at any price, without a second party. A tool that reports fourteen candidates undifferentiated has told an operator to investigate things they have no access to — and the split is free, because it is a property of the site rather than of the run.
// ---------------------------------------------------------------------
// gate_vector_reader -- Chapter 3.4 Section 13's status vector, read as
// a diagnosis rather than as a status.
//
// One output matters: the lowest rung that has not passed. Everything
// above it is UNDEFINED rather than failed -- a gate that cannot be
// attempted has not failed, and reporting it as failed is Section 14's
// second prohibition.
// ---------------------------------------------------------------------
module gate_vector_reader
import linkdiag_pkg::*;
(
input logic clk,
input logic rst_n,
// Chapter 3.4 Section 13's evidence, from the medium upward.
input logic signal_detect,
input logic clock_locked,
input logic block_locked,
input logic lanes_aligned,
input logic fec_locked,
input logic single_lane, // Chapter 3.8 Section 2's note
output logic [N_GATES-1:0] gates,
output gate_e failed_gate,
output logic [2:0] rungs_passed,
output logic ladder_complete,
output logic vector_not_monotone
);
always_comb begin
gates[G_SIGNAL] = signal_detect;
gates[G_CLOCK] = clock_locked;
gates[G_BLOCK] = block_locked;
// Chapter 3.8 Section 2: marker lock and deskew exist only because
// there are multiple lanes. On a single-lane family they are
// trivially true rather than absent.
gates[G_MARKER] = single_lane ? 1'b1 : lanes_aligned;
gates[G_FEC] = fec_locked;
failed_gate = lowest_failed(gates);
ladder_complete = (failed_gate == G_NONE);
rungs_passed = 3'd0;
for (int i = 0; i < N_GATES; i++)
if (gates[i] && (3'(i) == rungs_passed)) rungs_passed = rungs_passed + 3'd1;
// A gate asserted above a deasserted one. Section 4: only six of
// the thirty-two combinations are reachable, so this is a broken
// PHY or a stale read rather than a link fault.
vector_not_monotone = 1'b0;
for (int i = 1; i < N_GATES; i++)
if (gates[i] && !gates[i-1]) vector_not_monotone = 1'b1;
end
endmoduleClassification: a priority encoder over a chain, and one consistency check the chain makes possible.
What it teaches: that vector_not_monotone is checkable only because the ladder is a chain, and that it catches a class of fault nothing else does. Chapter 3.8 §2 establishes five dependencies of five different kinds — physical, definitional, structural, measurement, structural — so block_locked asserted while clock_locked is low is not a link condition at all. It is a PHY reporting nonsense, a stale MDIO read, or Chapter 4.5 §7's sample-age problem arriving as a contradiction.
And it teaches that rungs_passed and failed_gate are the same information twice, deliberately. A number is what a monitoring system charts over time; a named gate is what an engineer reads. The chapter uses the number in Section 13's stability test and the name everywhere else.
Deliberately simplified: the five inputs are assumed sampled at one instant, which MDIO cannot do — Chapter 4.5 §7's sample age is milliseconds per register and five registers are five separate transactions. A link flapping faster than the read sequence produces a non-monotone vector from a healthy PHY. rungs_passed's loop is a sequential comb over a combinational variable and will not synthesise as written. And single_lane silences two rungs rather than marking them inapplicable, so a single-lane link and a multi-lane link both report rungs_passed of 5 when complete.
Production implication: the sample-age problem is the one to design around rather than to document. Five MDIO reads at a millisecond each is five milliseconds of skew, and Chapter 3.8 §11 showed bring-up is a distribution — a link can pass three rungs inside that window. The correct instrument is a latched snapshot in the PHY, read as one transaction; where that does not exist, vector_not_monotone is the flag that says the reading cannot be trusted, and it should suppress the diagnosis rather than appear beside it.
4. Five Registers, Six Readings, 2.58 Bits
The ladder's five gates are five bits, so thirty-two readings. Six of them occur.
Chapter 3.8 §2's five dependencies make the chain monotone: a gate can pass only if every gate below it has passed. So the reachable vectors are exactly the prefixes:
| Reading | Lowest failed gate | Sites consistent with it |
|---|---|---|
00000 | signal detect | 5 — Section 8's dark class |
10000 | clock lock | 1 — our CDR |
11000 | block lock | 2 — our PCS, or polarity/lane swap |
11100 | marker lock and deskew | 1 — our deskew |
11110 | codeword lock | 1 — our FEC |
11111 | none — the ladder completed | 4 — the rest |
Six readings of thirty-two: 18.75% of the encoding is reachable, and the other 81.25% would be a PHY fault rather than a link fault.
| Value | |
|---|---|
| bits in the vector | 5 |
| reachable readings | 6 |
| information the five registers carry | log₂ 6 = 2.585 bits |
| information five independent bits would carry | 5.000 bits |
| the loss | 2.415 bits — 48.3% |
Nearly half the apparent information in the status vector is not there, and the reason is the thing that makes the vector useful: the ordering. Chapter 3.4 §13 argued that the ordered vector turns "the link is down" into "the link is down here", and that is exactly right — it is worth 2.585 bits and a single link_up bit is worth 1.000. The gain is real and it is smaller than five registers suggests.
Which is the structural opposite of Chapter 21.1's problem, and worth putting side by side.
| Chapter 21.1 | this chapter | |
|---|---|---|
| sites | 12 | 14 |
| information floor | 4 observations | 4 observations |
| optimal achieved | 7 | 4 |
| why | four counters are one-site indicators | the observations are well balanced |
| classes reachable | 12 with full instrumentation | 9 with MDIO, 12 with everything |
| the binding constraint | efficiency | separability |
Rows three and six are the contrast. Chapter 21.1's search was 75% worse than its floor and eventually reached every site; this chapter's search is at its floor and cannot reach every site at all. More questions do not help here — there are no more questions, only a second observer.
And the ladder has one more property that a diagnosis has to respect.
A gate above the lowest failed one has not failed. It has not been attempted.
Reading 11000 | |
|---|---|
| block lock | failed |
| marker lock | not attempted |
| lane deskew | not attempted |
| codeword lock | not attempted |
Reporting four failed gates where one failed and three were never tried is Section 14's second prohibition, and it is the commonest way a bring-up status page misleads: a wall of red that names one fault.
5. RTL 2 — The MDIO Evidence Reader
// ---------------------------------------------------------------------
// mdio_evidence_map -- the ten readings a local PHY can supply, and the
// mask of sites each one is consistent with.
//
// Chapter 21.2 Section 9 and Chapter 21.3 Section 15 both reached for
// MDIO and both noted it goes unread. This block is what reading it is
// worth: nine classes of fourteen sites, and the largest holds five.
// ---------------------------------------------------------------------
module mdio_evidence_map
import linkdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic sample_valid,
input logic [15:0] sample_age_us, // Chapter 4.5 Section 7
// The ladder, from Section 3.
input logic [N_GATES-1:0] gates,
// Link-level evidence, also in the local PHY.
input logic an_complete,
input logic lp_ability_valid, // their advertisement, parsed
input logic remote_fault_rx, // they told us they cannot hear
input logic flp_bursts_seen, // Chapter 11.1's detector
input logic link_stable_1s,
output site_mask_t consistent,
output logic consistent_valid,
output logic [3:0] readings_used,
output logic evidence_stale
);
// Section 6's table, one mask per reading: the sites at which this
// reading is ABNORMAL. A reading that is normal intersects with the
// complement, which is the half people forget -- Chapter 21.1 §10.
localparam site_mask_t M_SIGNAL = 14'b10_1101_0000_0010;
localparam site_mask_t M_CLOCK = 14'b10_1101_0000_0110;
localparam site_mask_t M_BLOCK = 14'b10_1111_0000_1110;
localparam site_mask_t M_MARKER = 14'b10_1111_0001_1110;
localparam site_mask_t M_FEC = 14'b10_1111_0011_1110;
localparam site_mask_t M_AN = 14'b11_1111_0111_1111;
localparam site_mask_t M_LP = 14'b11_1111_0111_1110;
localparam site_mask_t M_RF = 14'b00_0000_0000_0001;
localparam site_mask_t M_FLP = 14'b11_1101_0000_0010;
localparam site_mask_t M_STABLE = 14'h3FFF;
function automatic site_mask_t apply(site_mask_t cur, logic reading_ok,
site_mask_t abnormal_at);
return reading_ok ? (cur & ~abnormal_at) : (cur & abnormal_at);
endfunction
always_comb begin
consistent = ALL_SITES;
consistent = apply(consistent, gates[G_SIGNAL], M_SIGNAL);
consistent = apply(consistent, gates[G_CLOCK], M_CLOCK);
consistent = apply(consistent, gates[G_BLOCK], M_BLOCK);
consistent = apply(consistent, gates[G_MARKER], M_MARKER);
consistent = apply(consistent, gates[G_FEC], M_FEC);
consistent = apply(consistent, an_complete, M_AN);
consistent = apply(consistent, lp_ability_valid, M_LP);
consistent = apply(consistent, !remote_fault_rx, M_RF);
consistent = apply(consistent, flp_bursts_seen, M_FLP);
// link_stable_1s is abnormal at EVERY site -- it is the trigger,
// not a measurement. Section 6 computes what it is worth: nothing.
consistent = apply(consistent, link_stable_1s, M_STABLE);
readings_used = 4'd10;
// Chapter 4.5 Section 7: a reading older than the flap it is meant
// to describe is not evidence.
evidence_stale = (sample_age_us > 16'd1000);
consistent_valid = sample_valid && !evidence_stale;
end
endmoduleClassification: a ten-way intersection, and the only block in the chapter that touches the management bus.
What it teaches: that the ten masks are nested for five of their members and independent for the rest. M_CLOCK contains M_SIGNAL, M_BLOCK contains M_CLOCK, and so on — which is Section 4's monotonicity expressed as set containment, and it is why five of the ten readings carry 2.585 bits between them rather than five. M_AN, M_LP, M_RF and M_FLP are not nested in the chain, and that is exactly why they are worth having.
And it teaches that M_RF has one member. A remote fault indication is the only reading in the entire local instrument set that is abnormal at exactly one site — our own transmitter — because it is the only reading that is not ours: the partner computed it and sent it to us. It is the smallest mask in the table and the most valuable, and Section 8 is about why.
Deliberately simplified: evidence_stale's 1 ms threshold is a judgement against Chapter 4.5 §7's per-register sample age, and ten registers is ten transactions, so the set is stale even when each member is fresh. The masks are literals rather than derived from a machine-readable model. readings_used is hardcoded to ten where a PHY missing the FEC or FLP registers supplies fewer. And link_stable_1s is applied at all, which Section 6 shows changes nothing — it is in the block to make its worthlessness visible rather than assumed.
Production implication: the ten transactions are the real cost and they are not free. At Chapter 4.5's Clause 22 timings a register read is tens of microseconds and a Clause 45 indirect read is two transactions, so a full evidence sweep is a millisecond or more — during which Chapter 3.8 §11's bring-up distribution says a link can pass three rungs. A diagnosis built from ten separately-timed reads of a moving target needs vector_not_monotone from Section 3 as a gate on the whole result, and a PHY with a latched snapshot register is worth more than any additional status bit.
6. What MDIO Separates: Nine Classes of Fourteen
Run Section 5's ten readings over Section 2's fourteen sites and count the distinct signatures.
| Reading | Abnormal at | Bits |
|---|---|---|
partner_link_up | 7 of 14 | 1.000 — and it is not local |
clock_locked | 6 | 0.985 |
block_locked | 8 | 0.985 |
flp_bursts_seen | 6 | 0.985 |
signal_detect | 5 | 0.940 |
lanes_aligned | 9 | 0.940 |
local_loopback_passes | 5 | 0.940 |
fec_locked | 10 | 0.863 |
lp_ability_valid | 12 | 0.592 |
an_complete | 13 | 0.371 |
remote_fault_rx | 1 | 0.371 |
link_stable_1s | 14 | 0.000 |
The last row is Chapter 21.1 §6's frames_out arriving in a different chapter. A link that will not come up is unstable at every one of the fourteen sites — that is what the ticket means — so the reading carries no information at all. It is the trigger.
The first row is the finding, and it is the only observable in the space that splits the fourteen almost evenly. partner_link_up is abnormal at seven of fourteen: exactly half, 1.000 bits, a perfectly balanced question — and it is the one reading no local register supplies.
Now the classes.
| Instrument set | Classes | Largest class |
|---|---|---|
| MDIO alone — ten local readings | 9 | 5 |
| MDIO + a cooperating partner | 11 | 3 |
| MDIO + local loopback | 11 | 4 |
| all three | 12 | 3 |
Nine of fourteen with MDIO alone, and the fused groups are these:
| Fused under MDIO alone | Why |
|---|---|
| our receiver, attenuation, a wrong optic, their transmitter, their admin state | nothing arrives — five sites, one reading |
| our block lock, polarity or lane swap | bits arrive and do not parse — two sites, one reading |
The second pair is the easier one and it is worth naming because the fix differs completely. Chapter 3.4 §11's aligner failing to find block boundaries is what an inverted pair or a swapped lane looks like from inside the PCS — the bits are there, the timing is recovered, and the content is scrambled. One is a chip; the other is a connector installed backwards, and the reading is identical.
The first group is five sites and Section 8 is about it.
And the search's efficiency, for the contrast with Chapter 21.1:
| Value | |
|---|---|
| fourteen sites | 3.807 bits |
| information floor | 4 observations |
| optimal decision tree, MDIO alone | 4 |
| optimal decision tree, all twelve observables | 4 |
The search is at its floor with the local instruments alone, which is a much better position than Chapter 21.1's seven-against-four — and it does not help, because the ceiling is nine classes and no ordering of questions raises a ceiling.
Chapter 21.1 needed better questions. This chapter needs a second observer — and no arrangement of the first one's questions substitutes for it.
7. RTL 3 — The Three-Party Attribution
// ---------------------------------------------------------------------
// three_party_attributor -- which of the three parties owns the
// surviving candidates, and therefore what the next action is.
//
// This block has no analogue in Chapter 21.1, because there every site
// was ours. Here a diagnosis that has narrowed to three candidates
// spread across three parties has NOT told an operator what to do.
// ---------------------------------------------------------------------
module three_party_attributor
import linkdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic consistent_valid,
input site_mask_t consistent,
output logic [3:0] n_local,
output logic [3:0] n_medium,
output logic [3:0] n_remote,
output logic single_party,
output logic needs_partner,
output logic needs_physical_access,
output logic [1:0] next_action // 0 register, 1 hands, 2 phone, 3 mixed
);
site_mask_t loc, med, rem;
always_comb begin
loc = consistent & LOCAL_SITES;
med = consistent & MEDIUM_SITES;
rem = consistent & REMOTE_SITES;
n_local = 4'(popcount14(loc));
n_medium = 4'(popcount14(med));
n_remote = 4'(popcount14(rem));
single_party = ((n_local != 4'd0) + (n_medium != 4'd0) +
(n_remote != 4'd0)) == 1;
// A remote candidate cannot be investigated from here at ANY cost.
needs_partner = (n_remote != 4'd0);
needs_physical_access = (n_medium != 4'd0);
// The cheapest action that could still be the right one.
if (n_local != 4'd0 && n_medium == 4'd0 && n_remote == 4'd0) next_action = 2'd0;
else if (n_medium != 4'd0 && n_local == 4'd0 && n_remote == 4'd0) next_action = 2'd1;
else if (n_remote != 4'd0 && n_local == 4'd0 && n_medium == 4'd0) next_action = 2'd2;
else next_action = 2'd3;
end
endmoduleClassification: a partition of a candidate set by who can act on it.
What it teaches: that a candidate count is not a diagnosis until it is attributed. Three surviving candidates all local is one engineer with a register map; three candidates spread one per party is a register read, a site visit and a phone call — same number, three times the cost, and a report that gives only the number has concealed the difference.
And it teaches that needs_partner is an absolute rather than an economic statement. A remote site cannot be investigated from here at any cost, with any instrument, by anybody with access only to this end. Chapter 21.1 §15's best_possible_answer was about a platform's instrumentation and could be bought; this is about the topology and cannot.
Deliberately simplified: the action encoding collapses "mixed" into one value where an operator needs to know which mixture. single_party sums three comparisons as integers, which is legal SystemVerilog and reads badly. And the block assumes exactly one fault — Section 18's row seven — so a mask spanning two parties is reported as ambiguity where it may be two simultaneous faults, which is Chapter 21.1 §17's case arriving where masking does not apply, because no frame passes through both.
Production implication: next_action is the field that belongs in a ticket rather than in a log. A link-down ticket that says "3 candidates" gets triaged by whoever is free; one that says "3 candidates, all local, all readable from this switch" gets closed in five minutes, and one that says "1 candidate, remote" gets routed to the other team immediately instead of after an hour. The attribution is free — it is a mask AND — and it is the single highest-value output in the chapter for an operations organisation.
8. The Class of Five, and Why It Is the Darkest One
Section 6's largest fused group is the reading every link-down investigation starts from: nothing is arriving.
| Site | Party | Why it produces no signal |
|---|---|---|
| our receive front end | local | our detector is dead |
| attenuation or a break | medium | the signal does not survive the path |
| the wrong optic or cable | medium | wavelength or category mismatch |
| their transmitter | remote | they are not sending |
| their port administratively down | remote | they are deliberately not sending |
Five sites, three parties, one reading — and the reading is the most common one in the whole space.
Every local register agrees. signal_detect low forces clock_locked, block_locked, lanes_aligned and fec_locked low by Section 4's monotonicity — so five of the ten MDIO readings are determined by the first one and add nothing. an_complete and lp_ability_valid are low; flp_bursts_seen is low; remote_fault_rx is low because a remote fault indication has to arrive, and nothing is arriving.
| Readings that distinguish anything within this class | |
|---|---|
| of the ten MDIO readings | 0 |
| of the twelve observables in the space | 2 — loopback and the partner's status |
Zero of ten. A full MDIO sweep on this fault is ten transactions that cannot change the answer, and doing it is Section 14's fifth prohibition.
What does split the class:
| Observation | Splits off | Cost |
|---|---|---|
| local loopback passes | our receive front end | an action, and it breaks the link further |
| the partner reports signal | our transmitter's health, and their admin state | a second party |
| optical power at our receiver | attenuation from everything else | a meter, or DDM over I2C — not MDIO |
| optical power at our transmitter | our transmitter | the same |
Row three and four are the ones that are not in the Ethernet management path at all. Chapter 4.5's MDIO addresses the PHY; the optic's digital diagnostics live on a separate two-wire bus, and a switch that reads them is doing so through a different driver, a different address space and often a different team's code. That is why the darkest class in the space is resolved by an instrument the protocol does not define.
And one member of the class deserves separating out, because it is the only one that is not a fault.
"Their port is administratively down" is indistinguishable from a broken cable by every instrument this chapter has, and it is the most common cause of the reading.
| A broken cable | A port somebody turned off | |
|---|---|---|
| our signal detect | low | low |
| our every other register | low | low |
| their link status | low | low |
| their admin state | up | down |
| the distinguishing evidence | — | one register, at the other end |
Row four is a single register and it is on the other machine. Which is the chapter's recurring shape: the cheapest fact is the one you do not own, and the whole of Section 10 is about what it is worth to be able to ask for it.
9. RTL 4 — The FLP Detector as a Diagnostic
// ---------------------------------------------------------------------
// flp_presence_probe -- Chapter 11.1's parallel detection, used to
// answer a question it was not built for.
//
// That chapter asks "what kind of partner is this". This block asks a
// narrower one: are link pulses arriving AT ALL? The answer separates
// "our negotiation state machine is broken" from "their end does not
// negotiate", which Section 6 found fused under every other reading.
// ---------------------------------------------------------------------
module flp_presence_probe
import linkdiag_pkg::*;
#(
parameter int PULSE_WINDOW_MS = 100 // Chapter 11.1 Section 13
)(
input logic clk,
input logic rst_n,
input logic pulse_edge, // any link pulse on the pair
input logic burst_decoded, // a full 33-pulse FLP burst
input logic tick_ms,
output logic pulses_present,
output logic bursts_present,
output logic partner_is_forced, // pulses, no bursts
output logic we_cannot_parse, // bursts arriving, AN stuck
output logic [15:0] c_pulses,
output logic [15:0] c_bursts,
output logic window_elapsed
);
logic [15:0] ms_count;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
c_pulses <= '0; c_bursts <= '0; ms_count <= '0; window_elapsed <= 1'b0;
end else begin
if (pulse_edge) c_pulses <= c_pulses + 16'd1;
if (burst_decoded) c_bursts <= c_bursts + 16'd1;
if (tick_ms) begin
ms_count <= ms_count + 16'd1;
if (ms_count == 16'(PULSE_WINDOW_MS) - 16'd1) window_elapsed <= 1'b1;
end
end
end
assign pulses_present = (c_pulses != 16'd0);
assign bursts_present = (c_bursts != 16'd0);
// Chapter 11.1 Section 6: a one and a zero are not symmetric, and a
// legacy 10BASE-T partner emits a SINGLE normal link pulse rather
// than a burst. Pulses without bursts is that partner -- or a
// modern one configured for a forced speed.
assign partner_is_forced = window_elapsed && pulses_present && !bursts_present;
// Bursts arriving and negotiation not completing is OURS.
assign we_cannot_parse = window_elapsed && bursts_present;
endmoduleClassification: a presence detector, and the only block in the chapter that observes the wire rather than a register.
What it teaches: that Chapter 11.1's parallel detection is a diagnostic instrument and is almost never read as one. That chapter built it to decide what kind of partner is present so that a link can be brought up; here the same evidence separates two sites Section 6 found fused — our auto-negotiation state machine, and a partner configured for a forced speed. Both produce "every ladder gate passes and negotiation never completes"; only the presence or absence of bursts tells them apart.
And it teaches why the two outputs are not complements. partner_is_forced requires pulses and no bursts; we_cannot_parse requires bursts. Neither asserts when nothing at all arrives — which is Section 8's dark class, where this block correctly says nothing. A detector that reported "not forced" on silence would have converted an absence into a conclusion.
Deliberately simplified: the window is 100 ms against Chapter 11.1 §13's timers, which is a judgement rather than a derivation. burst_decoded is an input, so the block trusts somebody else's burst decoder — Chapter 11.1 §5's receiver is the one that matters and it is the harder half. window_elapsed never clears. And the block is copper-only: an optical link has no link pulses at all, so both outputs are permanently low there and the AN pair stays fused.
Production implication: the copper-only limitation is the important one and it changes which chapters apply. On a fibre link Chapter 11.1's FLP mechanism does not exist, so Section 6's flp_bursts_seen reading is unavailable and the class count falls from nine to eight. The equivalent evidence on fibre is the Clause 73 auto-negotiation page exchange, which is visible in MDIO rather than on the wire — the same distinction, a different register, and a diagnosis written for one medium silently loses a reading on the other.
10. What a Cooperating Partner Is Worth
Section 6 measured it: nine classes become eleven, and the largest class falls from five to three. This section is about why that is a larger improvement than the numbers suggest.
partner_link_up is abnormal at seven of fourteen sites — 1.000 bits, the only perfectly balanced observable in the space. No local register comes close: the best is clock_locked at 0.985, and the ladder's monotonicity means the five gates share 2.585 bits between them.
| MDIO alone | + partner | |
|---|---|---|
| classes | 9 | 11 |
| largest class | 5 | 3 |
| sites uniquely named | 7 | 9 |
And the two splits it makes are the two that matter most.
First, it splits the dark class. The five sites that all read "nothing arrives" divide by what the partner sees:
| Site | Partner's link status |
|---|---|
| our receive front end | UP — they hear us fine |
| their transmitter | UP — they think they are fine |
| attenuation or a break | down |
| the wrong optic | down |
| their admin state | down |
Two of five split off immediately, and both are the asymmetric cases — the ones where the link is dark in one direction only. That asymmetry is invisible from either end alone and obvious from both, which is the general principle the whole section rests on.
Second, it makes site 1 — our own transmitter — diagnosable at all. Section 2 noted that our transmit direction is unobservable locally: every register in our PHY describes the receive path. With the partner's status:
| Our transmitter is dead | |
|---|---|
| our ladder | complete — we hear them perfectly |
our an_complete | low |
our remote_fault_rx | asserted — they told us |
| their link status | down |
| the diagnosis | unambiguous |
remote_fault_rx is doing the partner's job through the protocol, which is why Section 5 called it the smallest and most valuable mask in the table: one site, 0.371 bits, and it is the only local reading that is not about us. A partner that sets it has cooperated without being asked.
Which gives the ranking of what to want from a partner, cheapest first:
| Want | Costs | Worth |
|---|---|---|
| a remote fault bit, in the protocol | nothing — it is standard | names site 1 alone |
| their link status, on request | a management path between two machines | 1.000 bits |
| their full gate vector | the same path, more fields | splits the remaining three-site class no further |
Row three is the honest limit and it is worth stating because it is counter-intuitive. Asking the partner for its whole status vector rather than one bit adds nothing — the three sites that remain fused after partner_link_up are attenuation, a wrong optic and their admin state, and all three look identical in the partner's registers too. Cooperation is worth exactly one bit, and the second bit is free and useless.
And the mechanism by which a partner's bit resolves anything is worth naming, because it generalises past this chapter.
A second observation resolves a fault exactly when the fault is asymmetric — when the link is dark in one direction and not the other. Symmetric faults look the same from both ends and defeat any number of observers.
| Fault | Our view | Their view | Asymmetric? |
|---|---|---|---|
| our transmitter dead | we hear them | they hear nothing | yes — named by one bit |
| our receiver dead | we hear nothing | they hear us | yes |
| their transmitter dead | we hear nothing | they hear us | yes |
| a broken cable | nothing | nothing | no |
| a wrong optic | nothing | nothing | no |
| their port switched off | nothing | nothing | no |
The three asymmetric rows are exactly the three that partner_link_up splits off, and the three symmetric rows are exactly Section 17's unseparable set. That is not a coincidence and it is not an artefact of this particular instrument list: a second observer adds information only about the direction it observes differently, so a fault that is identical in both directions is invisible to any number of them.
| What a second observer can decide | |
|---|---|
| which direction is dark | yes |
| whether both directions are dark | yes |
| why both directions are dark | no — and that is three of the fourteen |
11. RTL 5 — The Loopback Sequencer
// ---------------------------------------------------------------------
// loopback_diagnostic -- an ACTION rather than an observation, and the
// distinction matters: it changes the system it measures.
//
// A local loopback connects our transmitter to our receiver inside the
// PHY. If the ladder then completes, our receive path works and our
// transmitter produces something -- which eliminates two sites at once
// and eliminates them WITHOUT a partner.
// ---------------------------------------------------------------------
module loopback_diagnostic
import linkdiag_pkg::*;
#(
parameter int SETTLE_MS = 50
)(
input logic clk,
input logic rst_n,
input logic tick_ms,
input logic request,
input logic link_was_up, // before we broke it
input logic [N_GATES-1:0] gates,
output logic loopback_enable,
output logic busy,
output logic result_valid,
output logic loopback_passed,
output site_mask_t eliminated,
output logic refused
);
logic [15:0] settle;
// Section 14's first prohibition: a loopback breaks a link that is
// carrying traffic. Refusing is the block's most important behaviour.
assign refused = request && link_was_up;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
loopback_enable <= 1'b0; busy <= 1'b0; settle <= '0;
result_valid <= 1'b0; loopback_passed <= 1'b0;
end else if (request && !link_was_up && !busy) begin
loopback_enable <= 1'b1; busy <= 1'b1; settle <= '0;
result_valid <= 1'b0;
end else if (busy && tick_ms) begin
settle <= settle + 16'd1;
if (settle == 16'(SETTLE_MS) - 16'd1) begin
// Chapter 3.8 Section 11: bring-up is a DISTRIBUTION, so a
// fixed settle time is a probability rather than a proof.
loopback_passed <= (gates == {N_GATES{1'b1}});
result_valid <= 1'b1;
busy <= 1'b0;
loopback_enable <= 1'b0;
end
end
end
// A pass eliminates our receive chain AND says our transmitter emits
// something. It says nothing about whether what it emits is at the
// right power, wavelength or polarity to cross a real medium.
always_comb
eliminated = (result_valid && loopback_passed)
? 14'b00_0000_0011_1110 // sites 1..5
: 14'd0;
endmoduleClassification: an intervention, and the only block in the chapter that writes a register rather than reading one.
What it teaches: that refused is the block's most important output. A loopback breaks the link — it disconnects the receiver from the medium — so requesting one on a link that is currently up converts a working port into a down port, and the request usually arrives because somebody is investigating a neighbouring port. An instrument that changes what it measures needs a guard, and the guard belongs in the instrument.
And it teaches the precise limit of what a pass proves. A passing local loopback eliminates sites 1 through 5 — our receive front end, CDR, block lock, deskew and FEC — and does not eliminate site 1 in the sense that matters. Our transmitter emitted something the internal path could parse; whether it emits enough optical power, at the right wavelength, with the right polarity, to survive a real medium and be parsed by a different chip is not tested at all.
Deliberately simplified: SETTLE_MS is 50 against Chapter 3.8 §11's distribution — block lock on a 64b/66b link is variable and data-dependent, so a fixed settle time turns a bring-up into a coin flip at the tail. The block tests only the deepest loopback; Chapter 3.4's three sublayers each support one and they eliminate different sets. And eliminated is a literal rather than derived from which loopback was taken.
Production implication: the three loopback depths are the feature this block is missing and they are the reason loopback is worth having at all. A PMD-level loopback tests the optic; a PMA-level one tests the serialiser; a PCS-level one tests the coding — Chapter 3.4 §2's stack, walked from the top down. Each one that passes eliminates one more sublayer, so three loopbacks separate five local sites where one loopback separates them as a block. It is the one instrument in the chapter that can be made finer without a second party, and it costs a mux per sublayer, which Chapter 3.4 already has for other reasons.
12. Loopback Against Cooperation: 4 Against 3
Two ways to improve on MDIO alone, and they cost completely different things.
| MDIO + loopback | MDIO + partner | |
|---|---|---|
| classes | 11 | 11 |
| largest class | 4 | 3 |
| needs a second party | no | yes |
| breaks the link | yes | no |
| available at 3 a.m. with nobody awake | yes | rarely |
Same class count, different largest class, and the difference is which fusion each one breaks.
Loopback splits our receive chain off from everything else, so the dark five become our-receiver plus a group of four — attenuation, a wrong optic, their transmitter, their admin state. The partner's status splits the dark five into two and three, taking our receiver and their transmitter out together, because both are cases where the link is dark in one direction only.
| Fused after | Sites |
|---|---|
| loopback | attenuation, wrong optic, their TX, their admin — 4 |
| partner | attenuation, wrong optic, their admin — 3 |
| both | attenuation, wrong optic, their admin — 3 |
Row three is the finding: adding loopback to a diagnosis that already has the partner's status changes nothing at all. The partner's bit already separates what loopback separates, and it separates one more. So:
If you can ask the partner, do not run a loopback. If you cannot, run one — and know that it leaves four candidates where the partner would have left three.
And the ordering matters in the other direction too, because loopback is destructive. A loopback taken first on a link that somebody is still trying to bring up destroys the evidence of what it was doing — Chapter 3.8 §11's bring-up is a search with state, and interrupting it restarts the search. The MDIO sweep is free and non-destructive and must come first, which is Section 14's fourth prohibition.
| Step | Instrument | Destructive? |
|---|---|---|
| 1 | the ten MDIO readings | no |
| 2 | Chapter 11.1's pulse presence | no |
| 3 | ask the partner | no |
| 4 | local loopback | YES |
| 5 | swap the optic, measure power | yes, and physical |
Four of the five steps are free and the destructive one is fourth, which is the same shape as Chapter 21.1 §12's procedure — register reads before equipment — arriving in a chapter where the equipment is a screwdriver rather than a capture tool.
13. RTL 6 — The Intermittent-Link Detector
// ---------------------------------------------------------------------
// intermittent_link_detector -- site 8, and it is the only site in the
// space whose symptom is a TIME SERIES rather than a state.
//
// Chapter 3.8 Section 10: one lane cycling in and out of lock makes the
// whole link flap, and from the combined bit that is indistinguishable
// from every lane being marginal. This block keeps the per-lane history
// because that is the distinction, and it is a completely different fix.
// ---------------------------------------------------------------------
module intermittent_link_detector
import linkdiag_pkg::*;
#(
parameter int LANES = 4,
parameter int WINDOW_S = 60
)(
input logic clk,
input logic rst_n,
input logic tick_s,
input logic link_up,
input logic [LANES-1:0] lane_locked,
// Chapter 4.4 Section 9: two faults, not one.
input logic elastic_overrun,
input logic elastic_underrun,
output logic [15:0] c_link_transitions,
output logic [15:0] c_lane_transitions [LANES],
output logic [15:0] uptime_s,
output logic is_flapping,
output logic one_lane_dominates,
output logic [3:0] worst_lane,
output logic elastic_suspected,
output logic lossy_flap // overrun -- data was lost
);
logic link_prev;
logic [LANES-1:0] lane_prev;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
c_link_transitions <= '0; uptime_s <= '0;
link_prev <= 1'b0; lane_prev <= '0;
for (int i = 0; i < LANES; i++) c_lane_transitions[i] <= '0;
end else begin
link_prev <= link_up;
lane_prev <= lane_locked;
if (link_up != link_prev) c_link_transitions <= c_link_transitions + 16'd1;
for (int i = 0; i < LANES; i++)
if (lane_locked[i] != lane_prev[i])
c_lane_transitions[i] <= c_lane_transitions[i] + 16'd1;
if (tick_s && link_up) uptime_s <= uptime_s + 16'd1;
end
end
logic [15:0] lane_total, lane_max;
always_comb begin
lane_total = 16'd0; lane_max = 16'd0; worst_lane = 4'd0;
for (int i = 0; i < LANES; i++) begin
lane_total += c_lane_transitions[i];
if (c_lane_transitions[i] > lane_max) begin
lane_max = c_lane_transitions[i]; worst_lane = 4'(i);
end
end
is_flapping = (c_link_transitions > 16'd4) && (uptime_s < 16'(WINDOW_S));
// Chapter 3.8 Section 10's asymmetry: one lane owning most of the
// transitions is a LANE fault; transitions spread evenly across
// lanes is a MARGIN fault, and the fixes are a fibre and a budget.
one_lane_dominates = (lane_total > 16'd8) &&
(lane_max > ((lane_total * 16'd3) / 16'd4));
// Chapter 4.4 Section 9: overrun destroys data, underrun does not.
elastic_suspected = elastic_overrun || elastic_underrun;
lossy_flap = elastic_overrun;
end
endmoduleClassification: a history keeper, and the only block in the chapter that has to run before the fault is reported.
What it teaches: that one_lane_dominates is Chapter 3.8 §10's argument made into a comparison. A link is up only when every lane is locked, so the combined bit is dominated by the worst lane — and one lane cycling produces exactly the same link_up trace as four lanes all marginal. Three quarters of the transitions on one lane is a fibre, a connector or a lane's receiver; transitions spread evenly is a link budget, and no aggregate bit tells them apart.
And it teaches that lossy_flap is not a severity refinement but a different fault. Chapter 4.4 §9 is explicit: an overrun destroys data and an underrun does not, because the read side must emit something and what it emits is idle. A shared "elastic buffer error" counter makes a lossy link and a merely mistimed one read identically, which is the same shape as Chapter 21.2 §11's discard-versus-damage distinction, in the physical layer.
Deliberately simplified: is_flapping's thresholds — four transitions, sixty seconds — are judgements. The per-lane counters are LANES × 16 bits with no saturation. uptime_s accumulates rather than measuring the current up interval, so a link that was up for an hour and then flapped four times reads as healthy. And the block cannot run retroactively: a flap that happened before anybody looked leaves no evidence at all, which is the whole reason it must be always-on.
Production implication: always-on is the requirement and it is the one usually missed. A flap is a transition and a transition is only observable while it happens — so a diagnosis that begins after the ticket arrives starts with c_link_transitions at zero and no way to distinguish "it is not flapping" from "it has not flapped since I started watching". The counters are about 100 flops for four lanes, they must be free-running from reset, and the alternative is asking an operator to leave a terminal open overnight, which is what actually happens.
14. What a Link Diagnosis Must Never Do
Six prohibitions. Two are about the ladder's structure, two about the three parties, and two about the instruments — and every one produces a confident wrong answer rather than a visible failure.
| Never | Because | |
|---|---|---|
| 1 | run Chapter 21.1's frame method on a down link | Section 1 — it converges on class G with full confidence |
| 2 | report gates above the lowest failed one as failed | Section 4 — they were not attempted |
| 3 | conclude a local site from a receive-side gate | a failing gate implicates three parties |
| 4 | take a loopback before the MDIO sweep | Section 12 — it destroys a search in progress |
| 5 | sweep all ten registers on a dark link | Section 8 — zero of ten can change the answer |
| 6 | treat a flap as a down link | Section 13 — it is a different site and a time series |
Row one is the prohibition with the highest cost and it needs a mechanical guard. Chapter 21.1's method is correct, well-specified and completely wrong on this input — every counter reads zero, every zero eliminates the sites its counter watches, and the mask converges on the five sites past the MAC. The guard is one comparison: frames_in at zero is not an observation. Section 16's monitor is where it lives.
Row three is the one that is violated hourly and it is structural rather than careless. Every register in our PHY describes our receive direction, so block_locked low is a fact about what arrived here — and what arrived here is a function of our receiver, the medium and their transmitter. Concluding "our PCS is broken" from it is right about one time in two at best, and Section 6 measured it: the block-lock reading alone leaves two candidates and one of them is a connector installed backwards.
And the two that look like impatience and are not:
| Why it is a prohibition | |
|---|---|
| row four | Chapter 3.8 §11 — bring-up is a search with state, and a loopback restarts it |
| row five | the ladder's monotonicity makes nine of the ten readings determined by the first |
Both spend an instrument on a question whose answer is already fixed, which is what all six share: each one produces activity whose outcome was decided before it began.
15. RTL 7 — Bring-Up Telemetry
// ---------------------------------------------------------------------
// linkdiag_telemetry -- three groups, and the first thing it reports is
// which of Module 21's two problems this is.
//
// Chapter 21.1's method and this chapter's are both correct and they
// are correct on disjoint inputs. Choosing between them is a one-bit
// decision and getting it wrong costs an investigation.
// ---------------------------------------------------------------------
module linkdiag_telemetry
import linkdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [31:0] frames_in,
input site_mask_t consistent,
input logic consistent_valid,
input gate_e failed_gate,
input logic [3:0] n_local,
input logic [3:0] n_medium,
input logic [3:0] n_remote,
input logic partner_status_known,
input logic loopback_done,
input logic is_flapping,
input logic evidence_stale,
// Which problem is this?
output logic is_link_down,
output logic is_frame_problem,
output logic wrong_chapter,
// Where are we?
output logic [3:0] n_candidates,
output gate_e rung,
output logic [1:0] next_action,
// What is the ceiling?
output logic [3:0] best_possible,
output logic needs_second_party,
output logic at_ceiling
);
assign is_link_down = (frames_in == 32'd0);
assign is_frame_problem = (frames_in != 32'd0);
// Section 14's row one, as a bit.
assign wrong_chapter = is_frame_problem;
assign n_candidates = 4'(popcount14(consistent));
assign rung = failed_gate;
assign next_action = (n_remote != 4'd0) ? 2'd2
: (n_medium != 4'd0) ? 2'd1
: 2'd0;
// Section 6's table, as arithmetic rather than as prose. Nine classes
// with MDIO alone; the partner's bit buys two more.
always_comb begin
best_possible = partner_status_known ? 4'd3
: loopback_done ? 4'd4
: 4'd5;
needs_second_party = (best_possible > 4'd3);
at_ceiling = consistent_valid && !evidence_stale &&
(n_candidates <= best_possible);
end
endmoduleClassification: a reporting block whose first output decides which chapter applies.
What it teaches: that wrong_chapter is the most valuable bit in the block and it is one comparison. Module 21 now contains two complete and correct diagnostic methods that are correct on disjoint inputs — frames exist, or they do not — and each one produces a confident wrong answer on the other's input. A single frames_in == 0 test routes between them, and without it an operator reaches for whichever method they used last.
And it teaches that best_possible is knowable before any reading, exactly as Chapter 21.1 §15's was. It depends only on which instruments are available — can we ask the partner, can we take a loopback — and not on anything about this fault. Five candidates is the ceiling with local registers alone; telling an operator that at the start changes the plan, and telling them after an hour changes only the mood.
Deliberately simplified: best_possible returns the largest class size rather than the achievable candidate count for this reading, so a fault that lands in a singleton class reports a ceiling of five and reaches one. next_action prioritises the most expensive party, which is right for planning and wrong for ordering. And is_link_down uses frames_in alone where a link that is up and idle also reads zero — Section 18's row two.
Production implication: the frames_in-alone test is the simplification to fix before shipping, and the fix is one more input. An idle link and a down link both have frames_in at zero; what separates them is link_up, which the telemetry already has in is_flapping's neighbourhood. The correct test is !link_up, and the reason it is worth being pedantic is that an idle port on a lightly loaded switch is the normal case — a monitoring system that routes every idle port into this chapter's method will generate a link-down diagnosis for every unused port in a building.
16. RTL 8 — The Bring-Up Conformance Monitor
// ---------------------------------------------------------------------
// linkdiag_conformance_monitor -- six verdicts, and the first one is
// about which method is being run rather than about the link.
// ---------------------------------------------------------------------
module linkdiag_conformance_monitor
import linkdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [31:0] frames_in,
input logic link_up,
input logic frame_method_running,
input logic [N_GATES-1:0] gates,
input logic vector_not_monotone,
input logic evidence_stale,
input site_mask_t consistent,
input logic loopback_requested,
input logic mdio_sweep_done,
input logic partner_status_known,
output logic wrong_method_running,
output logic vector_impossible,
output logic evidence_too_old,
output logic loopback_out_of_order,
output logic unseparable_remains,
output logic candidates_span_parties,
output logic diagnosis_sound
);
// Section 14's row one. Chapter 21.1's method on a link with no
// frames converges, confidently, on the wrong five sites.
assign wrong_method_running = frame_method_running && !link_up &&
(frames_in == 32'd0);
// Section 4: six of thirty-two readings are reachable. Anything else
// is a PHY fault or a stale read, not a link fault.
assign vector_impossible = vector_not_monotone;
assign evidence_too_old = evidence_stale;
// Section 12: the MDIO sweep is free and non-destructive; a loopback
// restarts a search in progress.
assign loopback_out_of_order = loopback_requested && !mdio_sweep_done;
// Section 17: three sites that no observation in this space splits.
assign unseparable_remains = ((consistent & CLASS_UNSEPARABLE) == CLASS_UNSEPARABLE)
&& (popcount14(consistent) == 3);
// Section 7: a candidate set spread across parties is a triage
// problem as well as a diagnostic one.
assign candidates_span_parties =
(((consistent & LOCAL_SITES) != 14'd0) +
((consistent & MEDIUM_SITES) != 14'd0) +
((consistent & REMOTE_SITES) != 14'd0)) > 1;
assign diagnosis_sound = !wrong_method_running && !vector_impossible &&
!evidence_too_old && !loopback_out_of_order;
always_ff @(posedge clk) begin
if (rst_n && unseparable_remains)
$display("[linkdiag] attenuation, wrong optic and remote admin-down are not separable from here");
end
endmoduleClassification: an auditor whose loudest verdict names a limit rather than a fault.
What it teaches: that unseparable_remains is deliberately not a term of diagnosis_sound. Reaching three fused candidates is the correct terminal state of a correctly run diagnosis — the instruments genuinely cannot separate them — and calling that unsound would blame an engineer for a property of the observation set. It is the same split Chapter 21.1 §16 made for instrumentation_limited and Chapter 21.2 §16 for order_unexpected: a loud report, never a failure.
And it teaches that wrong_method_running is the only verdict in Module 21 that audits a different chapter's method. It fires when Chapter 21.1's frame-based procedure is running against a link with no frames, which is a correct method applied to an input it was never specified for — and the reason it belongs here rather than there is that only this chapter knows the input is out of scope.
Deliberately simplified: candidates_span_parties sums three comparisons as integers. unseparable_remains tests for exactly that three-site set where a superset containing it should also report. evidence_too_old is a pass-through. And diagnosis_sound omits candidates_span_parties, which is right — a candidate set spanning parties is a triage fact and not an error.
Production implication: wrong_method_running is the verdict to wire into tooling rather than into a report, and it is worth one sentence about why. The two methods in Module 21 look interchangeable from outside — both take a port, both read counters, both produce a candidate list — and they are correct on disjoint inputs with no overlap at all. One comparison routes between them. Without it the common failure is not that the wrong method fails; it is that the wrong method succeeds, producing five plausible candidates that an operator will spend a morning eliminating.
17. The Three Sites Nothing Separates
Section 6's table has one row that does not improve: attenuation, a wrong optic and a remote port that is administratively down stay fused under MDIO, under loopback, under a cooperating partner, and under all three together.
| Observable | Attenuation | Wrong optic | Their admin state |
|---|---|---|---|
| our signal detect | low | low | low |
| our whole ladder | low, by monotonicity | low | low |
an_complete, lp_ability_valid | low | low | low |
remote_fault_rx | absent | absent | absent |
flp_bursts_seen | low | low | low |
| local loopback | passes | passes | passes |
| their link status | down | down | down |
Seven observations, three sites, one signature. All three are dark in both directions, and that is the whole reason: every instrument in the space is an Ethernet instrument, and Ethernet instruments observe a signal. When there is no signal in either direction, the protocol has nothing to say.
What does split them is not Ethernet.
| Instrument | Splits off | Where it lives |
|---|---|---|
| received optical power at our end | attenuation — a number, not a bit | DDM, over a two-wire bus |
| the optic's part number and wavelength | a wrong optic | the same bus, a different page |
| the far end's administrative configuration | their admin state | a management system, or a person |
| swap the cable | attenuation, by substitution | hands |
Three of the four are outside the Ethernet management path entirely — a different bus, a different address space, often a different team's driver. The fourth is a person walking to a rack.
Which is a stronger statement than "the instruments are incomplete", and it is worth making precisely.
The three sites are not unobserved because nobody built a register. They are unobserved because a protocol that communicates over a link cannot report on a link that carries nothing.
And that explains why the resolution is by substitution rather than by measurement. The standard field procedure for the dark class is swap the optic, swap the cable, ask the far end — three actions in the order of increasing cost — and it is not a failure of rigour. It is the only method available, because every instrument that could have measured instead of substituting is on a different bus.
| Step | Cost | Resolves |
|---|---|---|
| read optical power, if the platform exposes it | a register on another bus | attenuation, quantitatively |
| swap the optic | minutes, and a spare | a wrong or dead optic |
| swap the cable | minutes | attenuation and breaks |
| ask the far end | a person, or a management system | admin state |
Row one is worth insisting on because it is the one step that is a measurement. Chapter 3.3's channel has a loss budget, and a received power reading against that budget answers "attenuation or not" in one number — where the other three steps answer it by elimination, over minutes, destructively. A platform that exposes DDM turns the darkest class in the chapter from a substitution exercise into a reading, and the cost is a driver rather than a gate.
Two sites that people expect to be in this class and are not:
| Why it is separable | |
|---|---|
| their transmitter | the partner's own link status is up — they hear us |
| our receiver | a loopback passes, or the partner hears us |
Both are asymmetric faults — dark in one direction and not the other — and asymmetry is exactly what a second observation detects. The three that remain are symmetric, and no amount of observing from either end distinguishes them, because both ends see the same nothing.
18. What the Diagnosis Assumes
Nine assumptions. Three are about the ladder, three about the instruments and three about the fault — and the one that is false most often is the second.
| Assumption | From | If false | |
|---|---|---|---|
| 1 | the ladder is monotone | Chapter 3.8 §2's five dependencies | Section 4's six readings become thirty-two and the vector is meaningless |
| 2 | frames_in at zero means the link is down | convenience | every idle port is diagnosed as broken |
| 3 | the ten MDIO reads describe one instant | nothing | Chapter 4.5 §7 — five rungs can pass inside the sweep |
| 4 | the PHY implements the registers we read | the datasheet | absent registers read as zero — Chapter 21.1 §10's trap |
| 5 | this link has all five rungs | Chapter 3.8 §2's note | a single-lane family has three, and two read as passed |
| 6 | exactly one site is faulty | convenience | Section 7 — a mask spanning parties may be two faults |
| 7 | the medium is symmetric | convention | fibre is two strands and one can break alone |
| 8 | the partner will answer honestly | cooperation | a partner reporting stale status is worse than none |
| 9 | link pulses exist | Chapter 11.1 | fibre has none; the class count falls from nine to eight |
Row two is false on most ports of most switches and the consequence is operational rather than technical. An idle port and a down port both have frames_in at zero. A monitoring system that routes on frames_in alone will run this chapter's method against every unused port in a building and report a link-down diagnosis for each. The correct test is !link_up and Section 15 says so; the reason it is worth a row of its own is that the wrong test is the one that is natural when you have just finished writing Chapter 21.1's method.
Row seven is the assumption that turns one fault into two and is invisible until it does. A fibre pair is two strands, and one can break while the other does not:
| One strand broken | |
|---|---|
| our receive ladder | complete — their strand is fine |
| their receive ladder | dark |
our remote_fault_rx | asserted, if they can tell us — and they can, on our good strand |
| which site is this? | site 1's signature exactly |
A single broken strand is indistinguishable from our own transmitter being dead, because both are "we hear them, they do not hear us." Site 1 and a unidirectional medium fault are fused and Section 2's site list does not contain the second one — which is an omission in the fault space rather than in the instruments, and the fix is a fifteenth site rather than another register.
Row three is the assumption whose failure is hardest to see. Ten MDIO transactions at tens of microseconds each is hundreds of microseconds to a millisecond, and Chapter 3.8 §11 showed bring-up is a distribution: block lock is evidence-driven and data-dependent. A link actively searching can pass three rungs inside one sweep, producing a vector that is internally inconsistent — which vector_not_monotone catches and which a reader who trusts the sweep does not.
And three things deliberately not assumed:
| Not assumed | Why not |
|---|---|
| that a failing gate names a local site | Section 6 — three parties per reading |
| that reading more registers helps | Section 8 — zero of ten on the dark class |
| that the diagnosis can reach one site | Section 17 — three of fourteen never separate |
Row three is the chapter's honest terminal position, and it is the one that distinguishes it from the three chapters before it: Chapter 21.1, Chapter 21.2 and Chapter 21.3 all reach a single site with enough instrumentation. This one does not, at any price, from one end.
19. The Cost, Accounted
A link diagnosis is masks and counters, and the counters are small because there is no population to count.
| Block | Flops | Nature |
|---|---|---|
linkdiag_pkg | 0 | fourteen sites and ten masks |
gate_vector_reader | 0 | combinational |
mdio_evidence_map | 0 | combinational |
three_party_attributor | 0 | combinational |
flp_presence_probe | ~50 | two counters and a window |
loopback_diagnostic | ~21 | a settle timer and three flags |
intermittent_link_detector — 4 lanes | ~97 | the history, and it must be always-on |
linkdiag_telemetry | 0 | combinational |
linkdiag_conformance_monitor | 0 | combinational |
| total | ~168 flops |
Six of the nine blocks are pure combinational logic, and the total is the smallest in Module 21 by a factor of two. Chapter 21.1 §19's method was 299 flops; this one is 168, and the reason is structural: a decision procedure over fourteen candidates has almost no state, and there is no traffic to accumulate statistics about.
| Flops | What it stores | |
|---|---|---|
| Chapter 21.3 §19's diagnosis | ~4 856 | a histogram over 64 residues |
| Chapter 21.2 §19's taxonomy | ~785 | thirteen class counters |
| Chapter 21.1 §19's method | ~299 | a mask and an audit log |
| this chapter | ~168 | a flap history |
The trend is the module's shape stated as a number: the more traffic a diagnosis reasons about, the more it has to remember. This chapter reasons about none, and 97 of its 168 flops are the one block that has to observe something over time.
And the instrumentation this chapter would add to a design is almost nothing, because the registers already exist.
| Addition | Flops | Buys |
|---|---|---|
| a latched snapshot of the gate vector | ~5 | Section 18's row three — one read instead of ten |
| per-lane transition counters, 4 lanes | ~64 | Chapter 3.8 §10's lane-versus-margin split |
| an FLP presence bit | ~2 | the AN pair, on copper |
| all three | ~71 | 0.5% of Chapter 19.7 §19's 14 166-flop datapath |
Half of one per cent, and the first row is the one that matters most: a latched snapshot turns ten skewed reads into one atomic one and makes vector_not_monotone a real fault indicator rather than a reason to distrust the sweep.
Module 21's whole instrumentation bill, now four chapters in:
| Chapter | Flops to add | Share of the datapath |
|---|---|---|
| Chapter 21.1 §19 | ~297 | 2.1% |
| Chapter 21.2 §19 | ~426 | 3.0% |
| Chapter 21.3 §19, logged rather than binned | ~792 | 5.6% |
| this chapter | ~71 | 0.5% |
| total | ~1 586 | 11.2% |
Eleven point two per cent of a datapath for every diagnosis Module 21 has built — and this chapter's contribution is the cheapest and, on the fault that takes a network down completely, the one that gets used first.
20. Properties Worth Asserting, and One Worth Refusing
Thirty-three properties and eight covers, in four groups: the ladder, the three parties, the instruments, and the two methods.
Group one — the ladder, and its monotonicity.
// Chapter 3.8 Section 2's five dependencies, as five properties. They
// are the only properties in this chapter that hold with no traffic on
// the wire, which is the whole point -- Section 20's refused property.
p_clock_needs_sig: assert property (@(posedge clk) disable iff (!rst_n)
gates[G_CLOCK] |-> gates[G_SIGNAL]);
p_block_needs_clk: assert property (@(posedge clk) disable iff (!rst_n)
gates[G_BLOCK] |-> gates[G_CLOCK]);
p_marker_needs_blk: assert property (@(posedge clk) disable iff (!rst_n)
gates[G_MARKER] |-> gates[G_BLOCK]);
p_fec_needs_marker: assert property (@(posedge clk) disable iff (!rst_n)
gates[G_FEC] |-> gates[G_MARKER]);
p_six_readings: assert property (@(posedge clk) disable iff (!rst_n)
!vector_not_monotone);
p_lowest_is_first: assert property (@(posedge clk) disable iff (!rst_n)
(failed_gate != G_NONE) |-> !gates[failed_gate]);
p_below_all_passed: assert property (@(posedge clk) disable iff (!rst_n)
(failed_gate != G_NONE) |->
((gates & ((1 << failed_gate) - 1)) ==
((1 << failed_gate) - 1)));
p_complete_is_all: assert property (@(posedge clk) disable iff (!rst_n)
ladder_complete |-> (gates == {N_GATES{1'b1}}));Group two — the three parties.
p_mask_only_shrinks: assert property (@(posedge clk) disable iff (!rst_n)
consistent_valid |=>
((consistent & $past(consistent)) == consistent));
p_parties_partition: assert property (@(posedge clk) disable iff (!rst_n)
((LOCAL_SITES | MEDIUM_SITES | REMOTE_SITES) == ALL_SITES) &&
((LOCAL_SITES & MEDIUM_SITES) == 14'd0) &&
((LOCAL_SITES & REMOTE_SITES) == 14'd0) &&
((MEDIUM_SITES & REMOTE_SITES) == 14'd0));
p_counts_sum: assert property (@(posedge clk) disable iff (!rst_n)
consistent_valid |->
((n_local + n_medium + n_remote) == n_candidates));
p_remote_needs_ask: assert property (@(posedge clk) disable iff (!rst_n)
(n_remote != 4'd0) |-> needs_partner);
p_medium_needs_hand: assert property (@(posedge clk) disable iff (!rst_n)
(n_medium != 4'd0) |-> needs_physical_access);
p_single_party_act: assert property (@(posedge clk) disable iff (!rst_n)
single_party |-> (next_action != 2'd3));
p_tx_needs_partner: assert property (@(posedge clk) disable iff (!rst_n)
(consistent == (14'd1 << S_L_TX)) |->
(partner_status_known || remote_fault_rx));
p_dark_is_five: assert property (@(posedge clk) disable iff (!rst_n)
(!gates[G_SIGNAL] && consistent_valid && !loopback_done &&
!partner_status_known) |-> (n_candidates == 4'd5));Group three — the instruments, and what each may and may not conclude.
p_stale_no_verdict: assert property (@(posedge clk) disable iff (!rst_n)
evidence_stale |-> !consistent_valid);
p_loopback_refuses: assert property (@(posedge clk) disable iff (!rst_n)
(loopback_requested && link_was_up) |-> refused);
p_loopback_order: assert property (@(posedge clk) disable iff (!rst_n)
loopback_enable |-> mdio_sweep_done);
p_loopback_scope: assert property (@(posedge clk) disable iff (!rst_n)
(result_valid && loopback_passed) |->
(eliminated == 14'b00_0000_0011_1110));
p_flp_needs_window: assert property (@(posedge clk) disable iff (!rst_n)
(partner_is_forced || we_cannot_parse) |-> window_elapsed);
p_flp_silent_on_dark:assert property (@(posedge clk) disable iff (!rst_n)
(!pulses_present && !bursts_present) |->
(!partner_is_forced && !we_cannot_parse));
p_flap_needs_history:assert property (@(posedge clk) disable iff (!rst_n)
is_flapping |-> (c_link_transitions > 16'd4));
p_lane_dominance: assert property (@(posedge clk) disable iff (!rst_n)
one_lane_dominates |->
(c_lane_transitions[worst_lane] ==
$past(c_lane_transitions[worst_lane]) ||
c_lane_transitions[worst_lane] >=
$past(c_lane_transitions[worst_lane])));
p_overrun_is_lossy: assert property (@(posedge clk) disable iff (!rst_n)
lossy_flap |-> elastic_overrun);Group four — the two methods, and the boundary between them.
p_methods_disjoint: assert property (@(posedge clk) disable iff (!rst_n)
!(is_link_down && is_frame_problem));
p_wrong_method: assert property (@(posedge clk) disable iff (!rst_n)
(frame_method_running && !link_up && (frames_in == 32'd0))
|-> wrong_method_running);
p_sound_excludes: assert property (@(posedge clk) disable iff (!rst_n)
diagnosis_sound |->
(!wrong_method_running && !vector_impossible));
p_ceiling_static: assert property (@(posedge clk) disable iff (!rst_n)
($stable(partner_status_known) && $stable(loopback_done))
|=> $stable(best_possible));
p_ceiling_five: assert property (@(posedge clk) disable iff (!rst_n)
(!partner_status_known && !loopback_done) |->
(best_possible == 4'd5));
p_second_party: assert property (@(posedge clk) disable iff (!rst_n)
(best_possible > 4'd3) |-> needs_second_party);
p_unseparable_flag: assert property (@(posedge clk) disable iff (!rst_n)
((consistent == CLASS_UNSEPARABLE)) |-> unseparable_remains);
p_never_below_three: assert property (@(posedge clk) disable iff (!rst_n)
((consistent & CLASS_UNSEPARABLE) == CLASS_UNSEPARABLE)
|-> (n_candidates >= 4'd3));
p_at_ceiling_needs: assert property (@(posedge clk) disable iff (!rst_n)
at_ceiling |-> (consistent_valid && !evidence_stale));And eight covers, because six of these are states a reviewer needs to have seen reached.
c_dark_class: cover property (@(posedge clk) n_candidates == 4'd5 && !gates[G_SIGNAL]);
c_unseparable: cover property (@(posedge clk) unseparable_remains);
c_our_tx_named: cover property (@(posedge clk) consistent == (14'd1 << S_L_TX));
c_spans_parties: cover property (@(posedge clk) candidates_span_parties);
c_flap_one_lane: cover property (@(posedge clk) one_lane_dominates);
c_flap_all_lanes: cover property (@(posedge clk) is_flapping && !one_lane_dominates);
c_partner_used: cover property (@(posedge clk) partner_status_known);
c_every_rung: cover property (@(posedge clk) rungs_seen == 6'b111111);21. Verification Scenarios
Fifty-eight scenarios, plus a five-run directed test whose variable is which party is broken — which no frame-level generator can vary, because the fault is that there are no frames.
The ladder — 11 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 1 | all five gates high | ladder_complete; failed_gate = G_NONE |
| 2 | signal low, everything else low | failed_gate = G_SIGNAL; reading 00000 |
| 3 | signal high, clock low | G_CLOCK; reading 10000 |
| 4 | signal, clock high, block low | G_BLOCK; reading 11000 |
| 5 | three rungs high, marker low | G_MARKER |
| 6 | four rungs high, FEC low | G_FEC |
| 7 | block high with clock low | vector_not_monotone — a PHY fault |
| 8 | all 32 vectors driven | 26 of them assert vector_not_monotone |
| 9 | single_lane set, marker low | marker reads passed; G_FEC or none |
| 10 | rungs_passed on reading 11000 | 2 |
| 11 | gates above failed_gate | reported as not-attempted, never as failed |
MDIO evidence — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 12 | every reading normal except an_complete | 2 candidates — our AN, or their config |
| 13 | the same plus flp_bursts_seen low | 1 — their config |
| 14 | the same plus bursts present | 1 — our AN state machine |
| 15 | on a fibre link, no pulses exist | the pair stays fused — Section 9 |
| 16 | remote_fault_rx asserted, ladder complete | 1 candidate — our transmitter |
| 17 | signal low | 5 candidates — the dark class |
| 18 | the nine other readings on that fault | none narrows it |
| 19 | sample_age_us = 2 000 | evidence_stale; no verdict |
| 20 | an absent register reading zero | indistinguishable from a real zero |
| 21 | all ten readings normal, link down | 1 candidate — the elastic buffer |
Party attribution — 9 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 22 | candidates all in LOCAL_SITES | single_party; next_action = register |
| 23 | candidates all medium | needs_physical_access |
| 24 | candidates all remote | needs_partner; next_action = phone |
| 25 | the dark class | spans all three parties |
| 26 | candidates_span_parties on it | asserts |
| 27 | n_local + n_medium + n_remote | equals n_candidates, always |
| 28 | a remote candidate and full local instrumentation | still needs_partner |
| 29 | the party masks | partition ALL_SITES exactly |
| 30 | a mask spanning two parties | may be two faults — Section 18's row six |
The instruments — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 31 | loopback requested while link is up | refused |
| 32 | loopback before the MDIO sweep | loopback_out_of_order |
| 33 | loopback passes | sites 1 to 5 eliminated |
| 34 | loopback passes, transmitter still dead on the wire | not detected — Section 11's limit |
| 35 | SETTLE_MS shorter than block lock's tail | a false fail — Chapter 3.8 §11 |
| 36 | pulses, no bursts, window elapsed | partner_is_forced |
| 37 | bursts arriving, AN incomplete | we_cannot_parse |
| 38 | silence | both outputs low — no conclusion from nothing |
| 39 | 99 ms elapsed | window_elapsed low; no verdict yet |
| 40 | DDM power reading available | attenuation separates — and it is not MDIO |
Flapping — 9 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 41 | five link transitions in 60 s | is_flapping |
| 42 | lane 2 owns 9 of 10 transitions | one_lane_dominates; worst_lane = 2 |
| 43 | transitions spread evenly | is_flapping, not one_lane_dominates |
| 44 | the combined bit alone on both | identical — Chapter 3.8 §10 |
| 45 | elastic overrun | lossy_flap; data was lost |
| 46 | elastic underrun | elastic_suspected, not lossy_flap |
| 47 | a shared elastic error counter | the two read identically — Chapter 4.4 §9 |
| 48 | counters started after the ticket | zero transitions; no evidence |
| 49 | uptime_s after an hour up then four flaps | reads healthy — the block's known defect |
Method selection and the ceiling — 9 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 50 | frames_in = 0, link_up low | is_link_down; this chapter |
| 51 | frames_in = 0, link_up high — an idle port | NOT link-down; Section 18's row two |
| 52 | Chapter 21.1's method run on a dead link | converges on class G — five wrong sites |
| 53 | wrong_method_running on that | asserts |
| 54 | best_possible with no partner, no loopback | 5, before any reading |
| 55 | with loopback | 4 |
| 56 | with the partner | 3 |
| 57 | with both | 3 — the loopback adds nothing |
| 58 | the three unseparable sites | unseparable_remains, and a $display |
And the directed test, because random stimulus will not produce it.
The case: one symptom, five parties broken in turn, and the diagnosis must attribute each one correctly using only what a local machine can read.
A constrained-random frame generator reaches none of this. The variable is which party is faulty, and three of the five settings are not in our design at all — a cable, a far-end transmitter, a far-end configuration. Chapter 20.5 §10 split injections into what a wire can produce and what needs a fault inside the design; this test needs a third category: faults inside somebody else's design.
| Run | What is broken | Party |
|---|---|---|
| A | our block-lock aligner | local |
| B | the cable, unplugged | medium |
| C | one strand of the fibre pair | medium, asymmetric |
| D | the far end's transmitter | remote |
| E | the far end configured for a forced speed | remote |
The oracle is four-part and every part is a register or a bit.
| Part | A | B | C | D | E |
|---|---|---|---|---|---|
our failed_gate | G_BLOCK | G_SIGNAL | none — ladder complete | G_SIGNAL | none |
our remote_fault_rx | absent | absent | ASSERTED | absent | absent |
| candidates, MDIO alone | 2 | 5 | 1 | 5 | 2 |
| candidates, with the partner | 2 | 3 | 1 | 2 | 2 |
Row two is the test's most instructive line and run C is why. A single broken strand of a fibre pair leaves our receive ladder complete — we hear them perfectly — and the only evidence anything is wrong is that they told us. remote_fault_rx is the one local reading that is not about us, and on run C it is the entire diagnosis.
Run C also exposes the fault space's own gap. Section 18's row seven: a single broken strand and a dead local transmitter have identical signatures, so run C's "1 candidate" is site 1 — our transmitter — and the answer is the cable. The diagnosis is confidently wrong, and it is wrong because the site list has fourteen members and should have fifteen.
Runs B and D are the dark class from both sides, and the contrast with run C is the point: B and D are symmetric faults and stay fused at five candidates until the partner speaks; C is asymmetric and is named by one bit. Asymmetry is what a second observation detects, and a fault that is symmetric in both directions is the one that defeats every observer.
Run E needs no fault at all. A far end configured for a forced speed is a correctly working device, and every gate on our ladder passes. It is in this chapter because the ticket says the link is down, and it is the case Chapter 11.1's parallel detection was built for — which is why Section 9 exists and why it stops working on fibre.
22. Debugging a Link That Will Not Come Up
Six complaints.
Complaint 1 — "every counter is zero so we have no information."
| Check | If yes | Meaning |
|---|---|---|
is link_up low? | yes | this chapter, not Chapter 21.1 |
| was Chapter 21.1's method run? | yes | it produced five wrong candidates |
| have the MDIO gates been read? | no | there is the information |
what is failed_gate? | the answer's first half | Section 4 |
The counters being zero is not an absence of information; it is a statement that the wrong instrument was consulted. A link with no frames has a status vector, and Chapter 3.4 §13's ordered vector names the rung. The information is in a different register file, on a different bus, behind a driver most monitoring systems do not call.
Complaint 2 — "we replaced the cable and the optic and it is still down."
| Check | If yes | Meaning |
|---|---|---|
is signal_detect still low? | yes | still the dark class |
| did anybody ask the far end? | no | two of five candidates are theirs |
| is their port administratively down? | often | Section 17 — one register, at the other end |
| how many substitutions so far? | two | and neither could have resolved it |
Substitution resolves the medium's two members and cannot resolve the remote's. Attenuation and a wrong optic are 2 of the 5; their transmitter and their admin state are another 2, and no amount of swapping at this end touches them. The phone call is cheaper than the second optic and it is made third.
Complaint 3 — "the status vector says four gates failed."
| Check | If yes | Meaning |
|---|---|---|
| is the lowest failed gate block lock? | yes | one gate failed |
| are marker, deskew and FEC also low? | yes | they were never attempted |
| does the display distinguish them? | no | that is the defect |
| is the vector monotone? | yes | so the reading is fine |
Four red indicators naming one fault is Section 14's second prohibition and it is a display problem rather than a link problem. Chapter 3.8 §2's dependencies mean a gate above the lowest failed one cannot have been tried, and rendering "not attempted" the same as "failed" turns a precise diagnosis into a wall of alarm.
Complaint 4 — "the ladder reads 10100 and that is impossible."
| Check | If yes | Meaning |
|---|---|---|
vector_not_monotone? | yes | correctly flagged |
| how many MDIO transactions? | ten, sequential | Chapter 4.5 §7's sample age |
| how long did the sweep take? | about a millisecond | and bring-up is a distribution |
| is the link actively searching? | yes | it passed rungs during the read |
It is not impossible; it is not simultaneous. Chapter 3.8 §11 showed block lock is evidence-driven and data-dependent — a link can pass three rungs inside a millisecond — so ten sequentially-timed reads of a moving target produce an inconsistent vector from a perfectly healthy PHY. The fix is a latched snapshot, five flops; the workaround is to re-read and require two consistent sweeps.
Complaint 5 — "the link comes up and drops every few minutes."
| Check | If yes | Meaning |
|---|---|---|
| is this a bring-up failure at all? | no | Section 13 — site 8, a different symptom |
one_lane_dominates? | yes | one lane, not a budget — Chapter 3.8 §10 |
| which lane? | worst_lane | and that names a fibre in a ribbon |
elastic_overrun? | if set, data was lost | Chapter 4.4 §9 |
Row two is the split that decides the fix. One lane owning three quarters of the transitions is one fibre, one connector or one lane's receiver — a component; transitions spread evenly across lanes is a link budget — a distance, a patch count, a dirty connector everywhere. The combined link_up trace is identical for both, and the per-lane counters are 64 flops.
Complaint 6 — "the diagnosis says our transmitter and we replaced the optic."
| Check | If yes | Meaning |
|---|---|---|
| is our receive ladder complete? | yes | we hear them |
is remote_fault_rx asserted? | yes | they do not hear us |
| is this fibre? | yes | two strands, and one can break alone |
| is site 15 in the list? | no | Section 18's row seven — the space is short one member |
A unidirectional medium fault and a dead local transmitter are the same signature and only one of them is in the fault space. Swapping the optic is the right first action and it is right for the wrong reason; the second action is swapping the strands, which costs nothing and resolves it.
Complaint 7 — "the monitoring system opened a link-down ticket for two hundred ports."
| Check | If yes | Meaning |
|---|---|---|
| what triggered it? | frames_in = 0 | Section 18's row two |
is link_up high on those ports? | yes | they are idle, not down |
| how many are genuinely down? | usually none | the test was wrong, not the ports |
| what is the correct trigger? | !link_up | one signal, and it already exists |
An idle port and a down port both have frames_in at zero, and on a lightly loaded switch most ports are idle most of the time. The wrong trigger is the natural one for anybody who has just finished writing Chapter 21.1's method, where frames_in is the first thing read — and it turns a diagnostic into a pager storm.
And the three symptoms this chapter is systematically blamed for:
| Symptom | Blamed on | Usually is |
|---|---|---|
| "we have no information" | the instrumentation | the wrong register file was read |
| "four gates failed" | the link | three were never attempted |
| "the vector is impossible" | the PHY | ten reads of a moving target |
23. Misconceptions
Misconception 1 — "a link that is down gives you nothing to work with."
The wrong model: all the counters read zero, so there is no evidence.
What it costs: it sends an investigation straight to substitution — swap the cable, swap the optic — skipping ten readings that would have named a rung. Chapter 3.4 §13's ordered status vector exists precisely for this, and it is worth 2.585 bits: enough to reduce fourteen candidates to a class, before anybody walks to a rack.
The corrected model: the counters are the wrong instrument, not an absent one. A down link has a status vector, an FLP presence bit and a remote-fault bit, and together they separate the fourteen sites into nine classes. Section 12's procedure spends four free readings before the first destructive one.
Misconception 2 — "block lock is low, so our PCS is broken."
The wrong model: the register is in our chip, so it describes our chip.
What it costs: every register in our PHY describes what arrived at our receiver, and what arrived is a function of three parties — our receiver, the medium, and their transmitter. A failing block lock has two candidates in Section 6's table and one of them is a connector installed backwards. Concluding "our PCS" is right about half the time and wrong in a way that costs a chip investigation.
The corrected model: a receive-side gate names a rung, not a site. Section 7's attribution splits the survivors by party, and the party decides whether the next step is a register read, a screwdriver or a phone call — three costs that differ by orders of magnitude.
Misconception 3 — "five status registers is five bits of diagnosis."
The wrong model: the vector has five independent bits, so thirty-two readings.
What it costs: Chapter 3.8 §2's five dependencies make the chain monotone, so six of the thirty-two readings are reachable and the other twenty-six are PHY faults. The vector carries 2.585 bits, not 5.000 — and a diagnosis budgeted on five bits expects the vector to do twice the work it can.
The corrected model: the ordering is what makes the vector useful and it is also what caps it. A single link_up bit is 1.000 bits; the ordered vector is 2.585; the gain is real, it is 1.585 bits, and it is not four.
Misconception 4 — "more registers would resolve the dark class."
The wrong model: five fused sites means the PHY needs more status bits.
What it costs: on the dark class zero of the ten existing readings narrows anything, because signal_detect low forces four of the others low by monotonicity and the rest describe a signal that is not there. An eleventh local register would be the same nothing. The class is symmetric — dark in both directions — and a protocol that communicates over a link cannot report on a link that carries nothing.
The corrected model: the dark class is broken by a second observer or a different bus. The partner's link status splits it five into two and three; optical power over DDM splits attenuation off quantitatively; the rest is substitution. Section 17 is the honest limit and it is not an instrumentation gap.
Misconception 5 — "run the standard diagnostic procedure."
The wrong model: Module 21 has a method; apply it.
What it costs: Module 21 has two methods, correct on disjoint inputs, and each produces a confident wrong answer on the other's. Chapter 21.1's procedure on a link with no frames reads every counter as zero, eliminates every site each counter watches, and converges on the five sites past the MAC — none of which is the fault and all of which are downstream of a link that never came up.
The corrected model: one comparison routes between them. link_up low is this chapter; frames exist is Chapter 21.1. Section 16's wrong_method_running is the guard, and the failure it prevents is not that the wrong method fails — it is that the wrong method succeeds.
Misconception 6 — "the link is flapping, so the link is marginal."
The wrong model: link_up toggling means the whole link is near its budget.
What it costs: Chapter 3.8 §10's asymmetry — a link is up only when every lane is locked, so the combined bit is dominated by the worst lane. One lane cycling produces exactly the same link_up trace as four lanes all marginal, and the fixes are a single fibre and a whole link budget. A team that reads "marginal" shortens the run or adds a repeater when one connector needed cleaning.
The corrected model: keep the per-lane transition counts. Three quarters on one lane is a component; evenly spread is a budget. Sixty-four flops for four lanes, and they must be free-running from reset, because a flap leaves no evidence after it stops.
24. Interview Questions
Question 1 — "A link will not come up and every counter reads zero. Where do you start?"
What the answer should establish: that the counters are the wrong instrument and the status vector is the right one. A strong answer names it: Chapter 3.4 §13's ordered vector, read over MDIO, whose lowest deasserted bit names the rung. The strongest answer adds the guard: do not run the frame-based procedure — it will converge, confidently, on five sites that are all downstream of a link that never came up.
Question 2 — "Block lock is low. Is your PCS broken?"
What the answer should establish: possibly, and there are two candidates. Every register in our PHY describes the receive direction, so a failing gate implicates our receiver, the medium and their transmitter at once. A strong answer names the second candidate: an inverted pair or a swapped lane looks exactly like an aligner that cannot find block boundaries — the bits are there, the timing is recovered, the content is scrambled.
Question 3 — "How much diagnosis is in a five-bit status vector?"
What the answer should establish: 2.585 bits, not 5. The ladder is monotone — a gate can pass only if every gate below it has — so six of the thirty-two readings occur and the other twenty-six would be a PHY fault. A strong answer says why that is still worth having: a single link_up bit is 1.000 bits, so the ordering buys 1.585 and names a rung. The strongest adds that gates above the lowest failed one were not attempted, not failed.
Question 4 — "What is a cooperating partner worth?"
What the answer should establish: one bit, and it is the only balanced question in the space. partner_link_up is abnormal at seven of fourteen sites — exactly half — where the best local reading is 0.985 and the five ladder bits share 2.585 between them. A strong answer says what it buys concretely: nine classes become eleven and the largest falls from five to three, because the two splits it makes are the asymmetric faults that are invisible from either end alone.
Question 5 — "Your local loopback passes. What have you proved?"
What the answer should establish: that our receive chain works and our transmitter emits something the internal path can parse. A strong answer states the limit immediately: it says nothing about power, wavelength or polarity, so an optic that cannot drive a real medium passes. The strongest answer notes the cost and the ordering: a loopback breaks the link and restarts a bring-up search, so it comes fourth, after the MDIO sweep, the pulse check and the phone call.
Question 6 — "Which link faults can you never separate from one end?"
What the answer should establish: three — attenuation, a wrong optic, and a far-end port that is administratively down. All three are dark in both directions, so every instrument in the Ethernet management path sees the same nothing, and they stay fused under MDIO, under loopback, under a cooperating partner and under all three. A strong answer names what does split them: received optical power over DDM — a different bus — the optic's part number, and a question asked of a person.
25. Questions and Answers
26. What's Next
Module 21 has five chapters left, and this one has drawn the boundary the rest sit inside.
| Chapter | Takes | Frames exist? |
|---|---|---|
| this chapter | the link never comes up | no |
| Chapter 21.5 | it comes up wrong | yes, and some of them collide |
| Chapter 21.6 | Chapter 21.1 §4's class G | yes |
| Chapter 21.7 | Chapter 19.6's descriptors | yes |
| Chapter 21.8 | a healthy link that is slow | yes, and none of them is wrong |
Chapter 21.5 is the near neighbour and the boundary between them is worth stating. A link that comes up at the wrong duplex has completed every rung of Section 2's ladder — the fault is in what the two ends agreed, not in whether they can hear each other — and the symptom is frames, collisions and a throughput figure rather than a status vector. Chapter 11.4 already built the many-to-many symptom matrix and class 48; the next chapter prices it, because a duplex mismatch's cost is a function of load and the load is what decides whether anybody notices.
Chapter 21.6 is the batch's hardest and it is the one this module has been deferring since Chapter 21.1 §4. Five sites, byte-identical on every RMON-required counter, and Chapter 21.3 §17 priced the four counters that would separate them at about 297 flops. The question that chapter will answer is what can be done without them — and the only instrument in a MAC that is conditioned on size is Chapter 19.7 §2's histogram, which is exactly what an MTU mismatch is visible in.
And the series is now ninety-seven classes long. Chapter 20.2 §8's six-group taxonomy took an eighth group in Chapter 21.2 §26 — a property bound to the wrong side of a function, holding 93 through 96. Class 97 is not a member of it. It is about a property that is bound to exactly the right thing and stops being evaluated, which is a property of the stimulus rather than of the binding — and 332 of the track's 557 published properties share it. Whether that is a ninth group or a sharpening of group 4's "unevaluable" is the argument Chapter 21.6 will have to have, because a chapter about five indistinguishable sites is where a taxonomy of indistinguishability belongs.
Continue learning
Related tutorials
- Related topic
A Method for Debugging Ethernet
The receive path has twelve fault sites and the RMON-required counters separate them into seven classes, one holding five — so the method's ceiling is knowable from a datasheet.
- Related topic
The Ethernet Error Taxonomy
Seven of the twelve reachable frame shapes belong to more than one error class, so exclusivity comes from a priority rule — and the two obvious rules disagree on exactly half the space.
- Related topic
CRC Errors
A channel fault's error rate is proportional to frame length and every logic fault's is flat, so the ratio is 23.72 or 1.00 — measurable on counters a MAC already computes.
- Related topic
Negotiation Failures
A duplex mismatch loses 95.78% of the collisions on a maximum-size frame and exactly none on a minimum-size one, which is why ping works and the transfer does not.
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.
