Ethernet · Module 21
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.
Chapter 11.4 built the symptom matrix and measured what each symptom is worth in bits. This chapter prices the most expensive of its seven causes, and the price turns out to depend on the frame size in a way that explains the commonest report in networking.
| Offered load at the full-duplex end | 64-octet frames | 512 | 1 518 | 9 000 |
|---|---|---|---|---|
| 1% | 0.00% | 0.87% | 0.95% | 0.99% |
| 10% | 0.00% | 8.33% | 9.12% | 9.45% |
| 25% | 0.00% | 19.35% | 21.19% | 21.96% |
| 50% | 0.00% | 34.43% | 37.69% | 39.07% |
The first column is exactly zero at every load and it is a structural zero rather than a small number. A 64-octet frame is 512 bits — precisely the slot time — so a collision on one can never be a late collision, and a collision that is not late is retried by the MAC and succeeds. Minimum-size traffic on a duplex-mismatched link is delayed and not lost.
A 1 518-octet frame is 12 144 bits, of which 11 632 lie beyond the slot time: 95.78%. A collision on one is almost certainly late, a late collision is not retried, and the frame is gone.
Ping works and the file transfer does not, and the ratio between them is 95.78 to 0.
And the two ends of the same cable report two different faults.
| The half-duplex end | The full-duplex end | |
|---|---|---|
| what it sees | collisions, late collisions, deferrals | FCS errors and fragments |
| which counters move | transmit-side | receive-side |
| Chapter 21.1's method says | not its space — no receive fault | the channel, the PHY or the xMII |
| where the fault is | neither end's receive path | neither end's receive path |
Row three is the finding. Run Chapter 21.1's procedure at the full-duplex end and it points outward at the cable, correctly following its own table — and the cable is fine. The frames really were cut; they were cut by the far end's MAC aborting them.
1. Scope, and Pricing a Matrix Somebody Else Built
Scope: what a duplex mismatch costs, as a function of offered load and frame size; which counters move at each end; and why the same link produces two incompatible diagnoses.
Not in scope: the taxonomy. Chapter 11.4 §2 enumerated seven ways a link comes up wrong, §3 built the symptom matrix and measured each row's information content, and §15 rejected the property that a symptom implies a cause. All of that stands and none of it is repeated here. This chapter takes cause 1 — the duplex mismatch — and asks what it costs.
Nor a link that will not come up. Chapter 21.4 owns that, and the boundary is sharp: here every rung of Chapter 3.8 §2's ladder passed. The link is up, frames are flowing, and what is wrong is what the two ends agreed about how to use it.
Start from what Chapter 11.4 §3 already established, because the pricing question comes directly out of it.
| Symptom | Causes it is consistent with | Bits |
|---|---|---|
| collisions at one end only | 2 of 7 | 1.81 |
| late collisions | 2 | 1.81 |
| FCS errors under load | 3 | 1.22 |
| speed below expectation | 4 | 0.81 |
| zero errors, low throughput | 5 | 0.49 |
The two most decisive symptoms are the two this chapter is about, and Chapter 11.4 §3 noted that both require the far end: "collisions at one end only" is by construction a statement about two ends, and a late collision is decisive only when paired with the other end's collision count being zero.
What that chapter did not ask is how often those symptoms occur. A symptom worth 1.81 bits that never appears is worth nothing, and this chapter's whole content is that the occurrence rate is a function of two things the network chooses rather than of the fault.
| Decides whether the symptom appears | |
|---|---|
| the offered load at the full-duplex end | collision probability — Section 4 |
| the frame size | whether a collision is late — Section 6 |
| the fault | present or not, and that is all |
Three inputs, and the fault is the one that contributes least to whether anybody notices. A mismatched link carrying minimum-size traffic at one per cent load is indistinguishable from a healthy one by every counter in the space, forever.
2. What a Duplex Mismatch Physically Is
One end runs Chapter 9.1's CSMA/CD and the other does not, and every consequence follows from that single asymmetry.
| The half-duplex end | The full-duplex end | |
|---|---|---|
| before transmitting | senses carrier and defers | transmits immediately |
| during transmission | watches for a collision | does not look |
| on a collision | jams, backs off, retries | no such event exists |
| while receiving | will not start a transmission | transmits anyway |
Row four is the mechanism. The full-duplex end transmits while the half-duplex end is transmitting, because it has no reason not to. The half-duplex end detects that as a collision — carrier sense and transmission at once is the definition — and does exactly what the standard tells it to: jam, abandon, back off, retry.
So the two ends experience the same physical event completely differently.
| What happens | |
|---|---|
| the full-duplex end transmits mid-frame | a physical overlap on the pair |
| the half-duplex end calls it | a collision |
| the full-duplex end calls it | nothing — it is not looking |
| the half-duplex end's frame | truncated, jammed, abandoned |
| the full-duplex end receives | a short frame with a bad check value |
Row five is what makes this a Module 21 problem rather than a Module 11 one. Chapter 11.4 diagnoses the negotiation; this chapter is about what the counters do afterwards — and the full-duplex end's receive counters fill up with exactly the pattern Chapter 21.2 §17 called archaeology.
Two properties of the mismatch that decide everything after this.
First, it is unidirectional in its effect. The half-duplex end receives perfectly well: nothing about CSMA/CD interferes with reception, and the full-duplex end's frames arrive intact. Only the half-duplex end's transmissions are damaged.
| Direction | Affected? |
|---|---|
| full-duplex → half-duplex | no |
| half-duplex → full-duplex | yes, and severely |
A one-directional throughput test therefore reports a healthy link half the time, depending on which direction it measured — and Section 14's third prohibition is about that.
Second, the damage is a function of when the overlap starts, not whether it happens. Chapter 9.1 §5 established that a collision detected within the slot time — 512 bit times — is a normal collision: the frame is abandoned early, backed off and retried, and no data is lost. A collision detected after the slot time is a late collision, and 802.3 does not require the MAC to retry it. The frame is discarded.
A normal collision costs time. A late collision costs the frame. The boundary is 512 bit times, and it is a fixed number of bits rather than a fraction of a frame.
That fixed boundary against a variable frame length is the whole of Sections 6, 7 and 8.
3. RTL 1 — The Mismatch Package and the Collision Classifier
// ---------------------------------------------------------------------
// dupmis_pkg -- the slot time, the two collision kinds, and the two
// ends' counter sets.
//
// The package's one structural claim: the slot time is a fixed number
// of BITS and a frame is a variable number of bits, so the fraction of
// a frame that is "late" is a function of its length. Everything in
// Sections 6 to 8 is that ratio.
// ---------------------------------------------------------------------
package dupmis_pkg;
// Chapter 9.1 Section 5's slot time. 512 bit times at 10 and 100
// Mb/s; it is not a fraction of anything.
localparam int SLOT_BITS = 512;
typedef enum logic [1:0] {
COLL_NONE = 2'd0,
COLL_EARLY = 2'd1, // within the slot time -- retried, costs time
COLL_LATE = 2'd2 // beyond it -- not retried, costs the frame
} coll_kind_e;
// Which counters a duplex mismatch moves, and at WHICH end. The
// split is the chapter's second subject: transmit-side at one end,
// receive-side at the other, and no overlap at all.
typedef enum logic [2:0] {
CNT_COLLISIONS = 3'd0, // half-duplex end, transmit
CNT_LATE_COLL = 3'd1, // half-duplex end, transmit
CNT_EXCESS_COLL = 3'd2, // half-duplex end, transmit
CNT_DEFERRED = 3'd3, // half-duplex end, transmit
CNT_FCS_ERRORS = 3'd4, // full-duplex end, receive
CNT_FRAGMENTS = 3'd5, // full-duplex end, receive
CNT_UNDERSIZE = 3'd6 // full-duplex end, receive
} counter_e;
localparam logic [6:0] HALF_DUPLEX_END = 7'b000_1111;
localparam logic [6:0] FULL_DUPLEX_END = 7'b111_0000;
// Section 6: the fraction of a frame that lies beyond the slot time.
// Returned in parts per thousand because there is no float here.
function automatic int late_fraction_ppt(int wire_len_octets);
int bits = wire_len_octets * 8;
if (bits <= SLOT_BITS) return 0;
return ((bits - SLOT_BITS) * 1000) / bits;
endfunction
// Chapter 5.1's minimum frame is 64 octets = 512 bits = the slot
// time exactly. That is not a coincidence: Chapter 9.1 sized one
// from the other, and it is why minimum-size traffic loses nothing.
localparam int MIN_FRAME_OCTETS = 64;
endpackageClassification: a package whose entire content is one constant, one enum and one ratio.
What it teaches: that the minimum frame size and the slot time are the same 512 bits, and that the coincidence is a design decision rather than an accident. Chapter 9.1 sized the minimum frame so that a transmitter is still transmitting when the furthest collision could reach it — and the consequence, three decades later, is that a minimum-size frame can never suffer a late collision, because there is no part of it beyond the slot time.
And it teaches that the two counter sets do not overlap at all. HALF_DUPLEX_END is four transmit counters; FULL_DUPLEX_END is three receive counters; HALF_DUPLEX_END & FULL_DUPLEX_END is zero. Two ends of one cable, one fault, and not a single counter in common — which is why Section 10's correlation needs both ends' data rather than a comparison of like with like.
Deliberately simplified: SLOT_BITS is 512, which is correct at 10 and 100 Mb/s and wrong at 1 Gb/s, where carrier extension makes the slot time 4 096 bit times — and a gigabit duplex mismatch is rare because gigabit half-duplex is rare. late_fraction_ppt uses integer arithmetic in parts per thousand, so 1 518 octets returns 957 rather than 95.78%. And counter_e has seven members where a real MAC's RMON set distinguishes single from multiple collisions, which matters for Section 4's retry arithmetic and not for the loss.
Production implication: the gigabit exception is worth carrying in a real implementation because it inverts the chapter's headline. At a 4 096-bit slot time a 512-octet frame is 4 096 bits — entirely within the slot — so the late fraction is zero up to 512 octets rather than up to 64. The frame sizes that lose data move by a factor of eight, and a diagnosis calibrated at 100 Mb/s will report a gigabit mismatch as healthy for everything below 512 octets.
// ---------------------------------------------------------------------
// collision_kind_split -- classify a collision by WHEN it happened,
// because that decides whether the frame survives.
//
// Chapter 9.1 Section 5: a collision inside the slot time is normal
// contention and the MAC retries. Outside it, the standard does not
// require a retry, and a conforming half-duplex segment cannot produce
// one -- so a late collision is evidence of a configuration fault
// rather than of traffic.
// ---------------------------------------------------------------------
module collision_kind_split
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
input logic tx_active,
input logic collision_detected,
input logic [15:0] bits_transmitted, // into the current frame
output coll_kind_e kind,
output logic kind_valid,
output logic [31:0] c_early,
output logic [31:0] c_late,
output logic [31:0] c_frames_lost,
output logic [31:0] c_frames_delayed,
output logic late_seen
);
always_comb begin
if (!collision_detected || !tx_active) kind = COLL_NONE;
else if (bits_transmitted <= 16'(SLOT_BITS)) kind = COLL_EARLY;
else kind = COLL_LATE;
kind_valid = collision_detected && tx_active;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
c_early <= '0; c_late <= '0;
c_frames_lost <= '0; c_frames_delayed <= '0; late_seen <= 1'b0;
end else if (kind_valid) begin
case (kind)
COLL_EARLY: begin
c_early <= c_early + 32'd1;
// The frame is not lost. It is backed off and retried, and
// the cost is latency rather than data.
c_frames_delayed <= c_frames_delayed + 32'd1;
end
COLL_LATE: begin
c_late <= c_late + 32'd1;
c_frames_lost <= c_frames_lost + 32'd1;
late_seen <= 1'b1;
end
default: ;
endcase
end
end
endmoduleClassification: a one-comparison classifier whose two outcomes have completely different costs.
What it teaches: that c_frames_delayed and c_frames_lost are the two numbers, and an RMON collisions counter reports their sum. A link with ten thousand early collisions and zero late ones has lost nothing — it is a busy shared segment behaving exactly as designed. A link with ten thousand late collisions has lost ten thousand frames. The standard's own counter set distinguishes them; most dashboards add them together and call the result "collisions", which is Chapter 21.2 §11's discard-versus-damage conflation in the transmit path.
And it teaches that late_seen is decisive in a way almost nothing in Module 21 is. Chapter 9.1 §5 established that a conforming half-duplex segment cannot produce a late collision — the slot time was sized so that it cannot. So a single late collision is proof of a configuration fault, not evidence of one, and it is the only sticky bit in the chapter.
Deliberately simplified: bits_transmitted is assumed to count from the first bit of the preamble, where the standard's slot time is measured from the start of transmission including the preamble — an eight-octet difference that moves the boundary by 64 bits. The block does not model the jam pattern or the 96-bit interframe gap after an abort. And c_frames_lost assumes the MAC abandons a late collision, which 802.3 permits rather than requires: an implementation that retries them turns this chapter's loss into latency, and the two are not distinguishable from the far end.
Production implication: that last simplification is a real implementation divergence and it is worth knowing which one you have. A MAC that retries late collisions converts a 37.7% loss at half load into a 37.7% retransmission rate, which is far better for the application and far worse for the link's utilisation — and from the full-duplex end the two are identical, because both produce the same stream of aborted fragments. The distinguishing evidence is at the half-duplex end: c_frames_lost against c_frames_delayed, and only the local MAC knows which it did.
4. The Collision Probability Is 1 − e^(−u)
The half-duplex end transmits a frame. It collides if the full-duplex end starts transmitting at any point during it.
Model the full-duplex end's frame starts as a Poisson process — which is the standard assumption and is close enough for a ratio — at a rate set by its utilisation u and its mean frame time T. Over one half-duplex frame of the same duration:
P(no full-duplex start during our frame) = exp(−u)
P(collision) = 1 − exp(−u)| Full-duplex utilisation | P(collision) per half-duplex frame |
|---|---|
| 1% | 1.00% |
| 5% | 4.88% |
| 10% | 9.52% |
| 25% | 22.12% |
| 50% | 39.35% |
| 90% | 59.34% |
At low load the collision probability is very nearly the utilisation itself, which is why a mismatched link on a quiet network behaves almost normally: at one per cent load, one frame in a hundred collides, and if the frames are small it is retried and nobody sees anything at all.
Now the retry arithmetic, for the collisions that are not late.
A backed-off frame is retried up to sixteen times. With an independent collision probability p per attempt:
u = 10% | u = 50% | |
|---|---|---|
| P(collision) per attempt | 9.52% | 39.35% |
| expected attempts per frame | 1.105 | 1.649 |
| P(sixteen consecutive collisions) | 4.5 × 10⁻¹⁷ | 3.3 × 10⁻⁷ |
Row three is why excessive collisions is almost never the symptom. Even at fifty per cent load from the other end, a minimum-size frame is abandoned after sixteen attempts about three times in ten million — so the half-duplex end's frame loss from early collisions is negligible. What it loses is time: 1.649 attempts per frame, plus binary exponential backoff between them.
Which gives the first half of the pricing, and it is a latency price rather than a loss one.
| Cost of a mismatch on minimum-size traffic | |
|---|---|
| frames lost | essentially zero |
| attempts per frame at 50% load | 1.649 |
| the visible symptom | latency and jitter |
| the counter that moves | collisions, and it looks like a busy segment |
Row four is the trap. A half-duplex end reporting collisions looks exactly like a half-duplex end on a genuinely shared segment, which is what half-duplex is for. The counter does not distinguish "somebody else is talking" from "the other end does not know it should stop talking" — and Chapter 11.4 §3 already said so: collisions at one end only needs both ends to be decisive, and one end alone sees an ordinary collision count.
One refinement of the collision model, because it changes the shape at high load rather than the value at low load.
The derivation above treats each transmission attempt as independent, which is right for the first attempt and wrong for the retries: after a collision the half-duplex end backs off and retries, and the far end is still transmitting. So the retries are correlated with exactly the condition that caused the first collision.
| Independent model | What actually happens | |
|---|---|---|
| first attempt | P(collision) = 1 − e^(−u) | the same |
| retry after backoff | the same probability | higher while the far end's burst lasts |
expected attempts at u = 50% | 1.649 | more |
| the loss figure | unchanged | unchanged |
Row four is why the refinement does not matter for this chapter's headline. The retries affect early collisions, which cost latency; a late collision is not retried at all, so the loss figure is decided by the first attempt alone and the independence assumption is exactly right for it. The correlation makes the latency worse and the loss identical, which is a convenient split: the number this chapter projects is the robust one.
5. RTL 2 — The Late-Collision Discriminator
// ---------------------------------------------------------------------
// late_collision_discriminator -- Section 3's split, applied across a
// whole traffic mix rather than to one frame.
//
// The output that matters is the late RATIO, because Section 6 shows it
// is decided by the frame size distribution and not by the fault. A
// mismatched link carrying minimum-size traffic has a late ratio of
// exactly zero.
// ---------------------------------------------------------------------
module late_collision_discriminator
import dupmis_pkg::*;
#(
parameter int N_BUCKETS = 7 // Chapter 19.7 Section 2's histogram
)(
input logic clk,
input logic rst_n,
input logic coll_event,
input coll_kind_e kind,
input logic [2:0] size_bucket,
input logic [13:0] wire_len,
output logic [31:0] c_coll_by_bucket [N_BUCKETS],
output logic [31:0] c_late_by_bucket [N_BUCKETS],
output logic [15:0] late_ratio_ppt,
output logic [15:0] predicted_ppt,
output logic model_disagrees,
output logic loss_is_structural_zero
);
logic [31:0] total_coll, total_late;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < N_BUCKETS; i++) begin
c_coll_by_bucket[i] <= '0;
c_late_by_bucket[i] <= '0;
end
end else if (coll_event) begin
c_coll_by_bucket[size_bucket] <= c_coll_by_bucket[size_bucket] + 32'd1;
if (kind == COLL_LATE)
c_late_by_bucket[size_bucket] <= c_late_by_bucket[size_bucket] + 32'd1;
end
end
always_comb begin
total_coll = 32'd0; total_late = 32'd0;
for (int i = 0; i < N_BUCKETS; i++) begin
total_coll += c_coll_by_bucket[i];
total_late += c_late_by_bucket[i];
end
late_ratio_ppt = (total_coll == 32'd0) ? 16'd0
: 16'((total_late * 32'd1000) / total_coll);
// Section 6: the predicted ratio is a property of the frame's
// length alone. Measuring it and predicting it and comparing is
// what turns a count into a confirmation.
predicted_ppt = 16'(late_fraction_ppt(int'(wire_len)));
// A measured ratio far from the predicted one means the model is
// wrong for this link -- a different slot time, or a MAC that
// retries late collisions after all.
model_disagrees = (total_coll > 32'd64) &&
((late_ratio_ppt > (predicted_ppt + 16'd200)) ||
(predicted_ppt > (late_ratio_ppt + 16'd200)));
// The headline: at the minimum frame size there is no part of the
// frame beyond the slot time, so no collision on one can be late.
loss_is_structural_zero = (wire_len <= 14'(MIN_FRAME_OCTETS));
end
endmoduleClassification: a two-dimensional collision counter, crossed with the one dimension that decides the cost.
What it teaches: that a collision count is not a loss figure until it is crossed with the frame size. Ten thousand collisions on minimum-size traffic is zero frames lost; ten thousand on maximum-size traffic is 9 578 frames lost — and the RMON collisions counter reports ten thousand in both cases. Chapter 19.7 §2's size histogram is already in the MAC, and crossing it with the collision counter costs six more counters.
And it teaches that predicted_ppt makes the measurement falsifiable. The late ratio is not a number to be observed and interpreted; it has a predicted value derived from the frame length and the slot time, and a measurement far from it says the model is wrong for this link rather than that the link is unusual. That is the same discipline Chapter 21.3 §4 used for the length slope, and it is what separates a diagnostic from a dashboard.
Deliberately simplified: predicted_ppt uses the current frame's length where the measured ratio is over the whole population, so the comparison is only meaningful on fixed-size traffic — a real implementation predicts per bucket. The tolerance of 200 parts per thousand is a judgement. total_coll and total_late sum seven 32-bit counters combinationally. And loss_is_structural_zero is per-frame where the useful version is a property of the traffic mix.
Production implication: model_disagrees is the output that catches the implementation divergence Section 3 flagged. A MAC that retries late collisions rather than abandoning them produces the same late ratio and a completely different loss, so the ratio alone cannot distinguish them — but a link whose measured late ratio is near zero while carrying large frames has either no mismatch or a slot time this model does not know about, and at 1 Gb/s it is the second. One comparison, and it prevents a gigabit link being declared healthy because it was measured with a 100 Mb/s constant.
6. Why 95.8% of a Maximum-Size Frame Is Late
The slot time is a fixed number of bits and a frame is a variable number of bits. Everything follows.
late fraction = (8L − 512) / 8L, for L ≥ 64 octets| Frame, octets | Bits | Beyond the slot time | Late fraction |
|---|---|---|---|
| 64 | 512 | 0 | 0.00% |
| 128 | 1 024 | 512 | 50.00% |
| 256 | 2 048 | 1 536 | 75.00% |
| 512 | 4 096 | 3 584 | 87.50% |
| 1 000 | 8 000 | 7 488 | 93.60% |
| 1 518 | 12 144 | 11 632 | 95.78% |
| 9 000 | 72 000 | 71 488 | 99.29% |
The curve is the mirror image of Chapter 8.3 §2's efficiency curve and for the same reason: a fixed overhead against a variable payload. There, 38 octets of framing against the payload gave 54.8% efficiency at 46 octets and 97.5% at 1 500. Here, 512 bits of slot time against the frame gives 0% late at 64 octets and 95.78% at 1 518 — the same shape, the same arithmetic, and an outcome that is beneficial in one chapter and catastrophic in this one.
| Chapter 8.3 §2 | this chapter | |
|---|---|---|
| the fixed quantity | 38 octets of framing | 512 bits of slot time |
| big frames are | efficient — 97.5% | doomed — 95.78% late |
| small frames are | wasteful — 54.8% | safe — 0% late |
| the advice that follows | use large frames | large frames are what you lose |
Row four is the collision between two pieces of standard advice and it is worth being explicit about, because both are right in their own chapter. Everything about Ethernet performance says use larger frames; on a duplex-mismatched link the larger frames are precisely the ones that do not arrive.
Now combine with Section 4's collision probability.
P(frame lost) = P(collision) × P(late | collision)
= (1 − e^(−u)) × (8L − 512) / 8Lu | 64 octets | 128 | 512 | 1 518 | 9 000 |
|---|---|---|---|---|---|
| 1% | 0.00% | 0.50% | 0.87% | 0.95% | 0.99% |
| 5% | 0.00% | 2.44% | 4.27% | 4.67% | 4.84% |
| 10% | 0.00% | 4.76% | 8.33% | 9.12% | 9.45% |
| 25% | 0.00% | 11.06% | 19.35% | 21.19% | 21.96% |
| 50% | 0.00% | 19.67% | 34.43% | 37.69% | 39.07% |
Three readings of that table.
First, the left column is exactly zero everywhere. Not small — zero, and structurally so: there is no part of a 512-bit frame beyond a 512-bit slot time. No amount of load, no amount of time and no amount of simulation produces a late collision on minimum-size traffic.
Second, the right four columns are nearly identical. Once the frame is a few times the slot time, the late fraction is already above 90% and further growth buys little. The transition is between 64 and about 256 octets — the same region where Chapter 8.3 §2's efficiency curve is steep, and for the same reason.
Third, the loss is very nearly the utilisation, for any frame that is not minimum size. At 10% load a 1 518-octet frame loses 9.12%; the utilisation is 10%. So a rule of thumb that is accurate to within a tenth: on a duplex-mismatched link, large-frame loss equals the other end's load.
Frame loss ≈ the far end's utilisation, for every frame size except the smallest one, where it is exactly zero.
7. RTL 3 — The Size-Conditioned Loss Model
// ---------------------------------------------------------------------
// size_loss_model -- predict the loss from the load and the size mix,
// and compare it against what was measured.
//
// The block exists because the two inputs are things the NETWORK chose
// and the output is what the FAULT costs. Separating them is what lets
// a report say "this mismatch will cost 9% when the backup job starts"
// rather than "there are collisions".
// ---------------------------------------------------------------------
module size_loss_model
import dupmis_pkg::*;
#(
parameter int N_BUCKETS = 7
)(
input logic clk,
input logic rst_n,
input logic [15:0] far_end_util_ppt, // the other end's load
input logic [31:0] c_tx_by_bucket [N_BUCKETS],
input logic [15:0] bucket_mid [N_BUCKETS],
input logic [31:0] c_frames_lost,
input logic [31:0] c_frames_tx,
output logic [15:0] predicted_loss_ppt,
output logic [15:0] measured_loss_ppt,
output logic [15:0] loss_at_full_load_ppt,
output logic prediction_holds,
output logic min_size_traffic_only
);
logic [31:0] weighted, total;
always_comb begin
weighted = 32'd0; total = 32'd0;
for (int i = 0; i < N_BUCKETS; i++) begin
total += c_tx_by_bucket[i];
weighted += c_tx_by_bucket[i] *
32'(late_fraction_ppt(int'(bucket_mid[i])));
end
// Section 6: loss = P(collision) x the traffic's mean late
// fraction. The first term is approximated by the utilisation,
// which Section 6 showed is accurate to about a tenth.
predicted_loss_ppt = (total == 32'd0) ? 16'd0
: 16'((32'(far_end_util_ppt) * (weighted / total)) / 32'd1000);
measured_loss_ppt = (c_frames_tx == 32'd0) ? 16'd0
: 16'((c_frames_lost * 32'd1000) / c_frames_tx);
// What this link will cost when the far end gets busy. It is the
// number a capacity planner needs and no counter reports.
loss_at_full_load_ppt = (total == 32'd0) ? 16'd0
: 16'((32'd632 * (weighted / total)) / 32'd1000);
prediction_holds = (c_frames_tx > 32'd1000) &&
((measured_loss_ppt > predicted_loss_ppt)
? ((measured_loss_ppt - predicted_loss_ppt) < 16'd50)
: ((predicted_loss_ppt - measured_loss_ppt) < 16'd50));
// Section 6's structural zero: a link carrying only minimum-size
// frames shows nothing, at any load, forever.
min_size_traffic_only = (total != 32'd0) && ((weighted / total) < 32'd10);
end
endmoduleClassification: a predictor, and the only block in Module 21 whose most useful output describes a load that has not happened yet.
What it teaches: that loss_at_full_load_ppt is the number that gets a mismatch fixed. A link losing 0.95% today, at one per cent load, will lose 60.5% when the far end saturates — 63.2% collision probability times a 95.78% late fraction — and the difference between those two numbers is entirely the load. A report that states today's loss describes the network's current quietness; a report that states the loss at full load describes the fault.
And it teaches that min_size_traffic_only is a statement about the evidence, not about the link. A link carrying only minimum-size frames has a mean late fraction of essentially zero, so it will show no loss whatever is wrong with it — and the correct response is not to conclude the link is healthy. It is to note that the instrument is blind on this traffic and to look at the collision count instead.
Deliberately simplified: the collision probability is approximated by the utilisation, which Section 6 showed is accurate to about a tenth and diverges above 50%: at full load the true figure is 63.2% and the linear approximation gives 100%. loss_at_full_load_ppt uses the correct 632 rather than 1 000, which makes it inconsistent with predicted_loss_ppt's approximation. bucket_mid is an input so the block trusts somebody else's histogram boundaries — Chapter 21.2 §18 showed those move with the VLAN configuration.
Production implication: the inconsistency between the two predictions is the block's own bug and it is instructive rather than shameful. A linear approximation is right to a tenth below half load and wrong by 58% at full load, so a model that uses it for today's figure and an exact form for the projection reports two numbers computed differently. The fix is one exponential or a small lookup table; the reason it matters is that the projection is the number somebody acts on, and a projection that is wrong by 58% in the pessimistic direction gets ignored the first time it is checked.
8. Ping Works and the File Transfer Does Not
Section 6's table has a left column of exact zeros, and that column is what every diagnostic tool in ordinary use sends.
| Tool | Typical frame size | Late fraction | Loss at 25% far-end load |
|---|---|---|---|
| a 56-byte ping | 98 octets on the wire | 34.7% | 7.67% |
| a 0-byte ping | 64 octets — padded | 0.00% | 0.00% |
| ARP | 64 octets — padded | 0.00% | 0.00% |
| a TCP acknowledgement | 64 octets — padded | 0.00% | 0.00% |
| a bulk transfer segment | 1 518 | 95.78% | 21.19% |
Rows two, three and four are exactly 64 octets because Chapter 5.6's padding rule pads everything shorter up to the minimum — and the minimum is the slot time. So the three most common small-frame exchanges on any network cannot suffer a late collision, are retried transparently by the MAC, and arrive.
Row five is the application, and it loses a fifth of its frames.
Every tool an operator reaches for first sends frames that are structurally immune to the fault. The application sends the frames that are not.
And the effect is amplified far beyond the raw loss figure by what sits above.
| At 21.19% frame loss | |
|---|---|
| raw goodput, if loss were independent | 78.8% of offered |
| what TCP does | treats loss as congestion and collapses the window |
| what the user sees | a transfer that runs at a few per cent of the link |
| what the link's counters say | the link is 25% utilised |
Row four is Chapter 8.3 §4's throughput-against-goodput distinction arriving as an operational trap. The link carries plenty of octets — most of them retransmissions — so a utilisation graph looks reasonable while the application gets almost nothing. Chapter 8.3 §13 called this "a link that is full and looks idle"; this is the mirror image: a link that is nearly idle and looks busy.
The asymmetry makes it worse, and it is the reason a bidirectional test is mandatory.
| Test | Direction measured | Result on a mismatched link |
|---|---|---|
| download from the server | full-duplex → half-duplex | full rate, no errors |
| upload to the server | half-duplex → full-duplex | collapses |
| a one-directional benchmark | whichever it chose | healthy, half the time |
Section 2 established that the mismatch damages only the half-duplex end's transmissions. So a test that measures the other direction reports a perfectly healthy link, and reports it correctly — it measured a direction that is perfectly healthy.
And there is a second size effect on top of the first, which makes the asymmetry worse than the loss figures alone suggest.
The frames flowing in the two directions are not the same size. A download is large data frames one way and small acknowledgements the other; an upload is the reverse.
| Large frames travel | Small frames travel | Which direction is damaged? | |
|---|---|---|---|
| download | FD → HD — safe direction | HD → FD | the small ones, which are immune |
| upload | HD → FD — damaged direction | FD → HD | the large ones, at 95.78% late |
A download therefore escapes twice over: its large frames travel in the direction the mismatch does not touch, and its acknowledgements travel in the damaged direction at exactly the size that cannot suffer a late collision. The two effects compound, and a download across a badly mismatched link can run at full rate with no measurable loss at all.
An upload is hit twice for the same reason. Its data frames are large and travel in the damaged direction.
| Observed throughput on a mismatched link | |
|---|---|
| download | line rate |
| upload | collapses |
| a bidirectional test | shows the asymmetry immediately |
| a one-directional test | a coin flip |
Which makes "test both directions" not merely good practice but the single cheapest discriminator in the chapter — it needs no counters, no far-end access and no knowledge of the frame sizes involved, and the asymmetry it exposes is produced by no other fault in Module 21.
9. RTL 4 — The Two-Ended Counter Correlator
// ---------------------------------------------------------------------
// two_end_counter_join -- put both ends' counters in one row.
//
// Section 3 showed the two counter sets do not intersect: four
// transmit counters at one end, three receive counters at the other,
// and nothing in common. So this is not a comparison -- it is a JOIN
// on a link, and the pattern it looks for is a correspondence between
// two different quantities.
// ---------------------------------------------------------------------
module two_end_counter_join
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
// Our end, whichever it is.
input logic we_are_half_duplex,
input logic [31:0] our_collisions,
input logic [31:0] our_late_collisions,
input logic [31:0] our_fcs_errors,
input logic [31:0] our_fragments,
input logic [31:0] our_frames_tx,
// The far end, over a management path.
input logic far_valid,
input logic [31:0] far_collisions,
input logic [31:0] far_late_collisions,
input logic [31:0] far_fcs_errors,
input logic [31:0] far_fragments,
output logic one_sided_collisions,
output logic cross_correspondence,
output logic [15:0] correspondence_ppt,
output logic mismatch_confirmed,
output logic need_far_end
);
logic [31:0] hd_late, fd_frag;
always_comb begin
// Chapter 11.4 Section 3: "collisions at one end only" is by
// construction a statement about two ends. On a genuinely shared
// segment BOTH ends collide.
one_sided_collisions = far_valid &&
(((our_collisions > 32'd0) && (far_collisions == 32'd0)) ||
((far_collisions > 32'd0) && (our_collisions == 32'd0)));
// The correspondence: every frame the half-duplex end abandoned
// arrives at the full-duplex end as a fragment. The two counts
// should track, and they are different counters at different ends.
hd_late = we_are_half_duplex ? our_late_collisions : far_late_collisions;
fd_frag = we_are_half_duplex ? far_fragments : our_fragments;
correspondence_ppt = (hd_late == 32'd0) ? 16'd0
: 16'((fd_frag * 32'd1000) / hd_late);
// Within 20% of one-to-one is a correspondence. Exactly one-to-one
// it will not be: the far end also discards some fragments below
// its own detection threshold.
cross_correspondence = (hd_late > 32'd64) &&
(correspondence_ppt > 16'd800) &&
(correspondence_ppt < 16'd1200);
mismatch_confirmed = one_sided_collisions && cross_correspondence &&
(hd_late > 32'd0);
need_far_end = !far_valid;
end
endmoduleClassification: a join across a link, and the only block in Module 21 whose two inputs come from two different machines.
What it teaches: that the confirmation is a correspondence between two different counters rather than an agreement between two copies of one. The half-duplex end's late_collisions and the full-duplex end's fragments are the same physical events counted from two sides — one end abandoned a frame, the other received its truncated remains — and they should track one-to-one. Neither counter alone says anything; the ratio between them is the diagnosis.
And it teaches why one_sided_collisions needs the far end by construction. Chapter 11.4 §3 made exactly this point: on a genuinely shared segment both ends collide, so a collision count at one end is ordinary and a collision count at one end while the other end has none is impossible under contention. The symptom is worth 1.81 bits and none of them is available unilaterally.
Deliberately simplified: the 20% tolerance is a judgement, and the true correspondence is below one-to-one because the far end discards some fragments before counting them — a runt shorter than its detection threshold is not counted anywhere. far_valid is an input so the block has no opinion about how the far end's counters were obtained or how stale they are. And the two ends' counters are assumed to cover the same window, which across a management path they do not — Chapter 4.5 §7's sample-age problem across two machines.
Production implication: the window-alignment problem is the practical obstacle to this block existing at all. Two switches' counters, read by a monitoring system at two different moments, over two different paths, are not a matched pair — and the correspondence test needs deltas over a common interval. The workable version reads both ends twice, a minute apart, and correlates the deltas; the ratio is robust enough that a few seconds of skew does not matter, which is the one respect in which this diagnosis is forgiving.
10. Two Ends, Two Different Faults
The same physical event, counted at two ends, produces two counter sets with nothing in common.
| Half-duplex end | Full-duplex end | |
|---|---|---|
collisions | climbing | zero — the counter does not apply |
late_collisions | climbing | zero |
deferred | climbing | zero |
excessive_collisions | rarely | zero |
fcs_errors | zero | climbing |
fragments | zero | climbing |
frames_in | healthy | healthy |
Seven counters, and no row has both columns non-zero. So the two operators looking at the two ends see disjoint evidence and describe different faults:
| Says | |
|---|---|
| the half-duplex end's operator | "we are on a busy shared segment" |
| the full-duplex end's operator | "something is corrupting frames on the cable" |
| the truth | neither, and it is one configuration line |
And the second one is the dangerous reading, because it is the one that sends a technician to a rack. fcs_errors and fragments climbing together with a healthy frames_in is the classic signature of a damaged medium — and Chapter 21.2 §17 spent a section on why fragments no longer means what its name says.
This chapter is the exception to that section.
Chapter 21.2 §17 said the fragment counter's original cause — a collision remnant — no longer exists on a switched full-duplex link. A duplex mismatch resurrects it: the fragments really are collision remnants, produced by collisions on a link where collisions should be impossible.
fragments, three eras | |
|---|---|
| 1985, shared medium | a collision remnant |
| Chapter 21.2 §17, switched full duplex | a cut frame — cable, partner, or our own truncation |
| this chapter | a collision remnant again, and it should be impossible |
Which gives the fragment counter its fourth cause and the only one that is diagnosable from its companion counter rather than from itself. fragments alone is three causes; fragments with the far end reporting late collisions is one.
11. RTL 5 — The Fragment Provenance Detector
// ---------------------------------------------------------------------
// fragment_provenance -- which of Chapter 21.2 Section 17's causes
// produced these fragments?
//
// Three were known: a cut cable, a stopped partner, our own truncation.
// This chapter adds a fourth -- a far-end collision abort -- and it is
// the only one with a positive local signature.
// ---------------------------------------------------------------------
module fragment_provenance
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
input logic frag_event,
input logic [13:0] frag_len,
input logic [31:0] c_self_inflicted, // Chapter 21.2 Section 11
input logic [31:0] c_fragments,
input logic [31:0] c_frames_in,
input logic far_late_collisions_seen,
output logic [31:0] c_frag_at_slot,
output logic [15:0] at_slot_ppt,
output logic [2:0] provenance, // 0 unknown 1 cut 2 ours 3 collision
output logic collision_abort_likely,
output logic len_evidence_only
);
// A frame abandoned on a collision stops at the moment of the
// collision plus the jam. A frame cut by a cable stops anywhere.
// So a CLUSTER of fragment lengths just past the slot time is the
// signature of an abort rather than of a cut.
localparam int SLOT_OCTETS = SLOT_BITS / 8; // 64
localparam int JAM_OCTETS = 4;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_frag_at_slot <= '0;
else if (frag_event &&
(frag_len >= 14'(SLOT_OCTETS)) &&
(frag_len <= 14'(SLOT_OCTETS + JAM_OCTETS + 8)))
c_frag_at_slot <= c_frag_at_slot + 32'd1;
end
always_comb begin
at_slot_ppt = (c_fragments == 32'd0) ? 16'd0
: 16'((c_frag_at_slot * 32'd1000) / c_fragments);
// A cut cable produces fragment lengths spread over the whole
// range. An early collision abort produces them at the slot time.
// A LATE collision abort produces them anywhere too -- which is
// why the length evidence alone is not decisive.
collision_abort_likely = (c_fragments > 32'd64) && (at_slot_ppt > 16'd300);
len_evidence_only = !far_late_collisions_seen;
if (far_late_collisions_seen) provenance = 3'd3;
else if (c_self_inflicted > (c_fragments >> 1)) provenance = 3'd2;
else if (collision_abort_likely) provenance = 3'd3;
else if (c_fragments > 32'd0) provenance = 3'd1;
else provenance = 3'd0;
end
endmoduleClassification: a provenance estimator, and the first block in Module 21 to attribute a counter to a cause outside the local design.
What it teaches: that fragment lengths carry evidence that fragment counts do not. A frame abandoned on an early collision stops at the slot time plus the jam — about 68 octets, every time — so a cluster of fragments at that length is an abort rather than a cut. A cable that parts mid-frame produces fragment lengths spread over the whole range, and Chapter 19.5 §14's truncation produces them at the buffer's boundary.
And it teaches that the length evidence is only half the story, which len_evidence_only says out loud. A late collision abort stops wherever the collision happened, which is uniformly distributed over the frame — so exactly the collisions that cost frames are the ones whose lengths look like a cut cable. The clustered signature comes from the early collisions, which cost nothing; the decisive evidence is the far end's late_collisions counter, and provenance prefers it whenever it is available.
Deliberately simplified: the at-slot window is eight octets wide around 68, which is a judgement about jam timing and MAC abort latency. The block cannot see Chapter 21.2 §11's c_self_inflicted unless that optional counter exists — and it is one of the four Chapter 21.1 §19 priced at about 297 flops. And provenance returns a single value where the honest output is a set, which is Chapter 11.4 §15's class 48 in miniature.
Production implication: the fragment-length histogram is the instrument this block wants and no MAC has it. Chapter 19.7 §2's size histogram bins all frames, not fragments specifically, so a cluster of 68-octet fragments is invisible inside bucket 0's ordinary traffic. The fix is the same one Chapter 21.3 §19 argued for: log the length of each fragment as a field rather than binning it — a 14-bit value in a record the MAC already keeps — and the clustering test is free in software.
12. What 21.1's Method Says at Each End, and Why It Is Wrong at One
Run Chapter 21.1's candidate-mask procedure at the full-duplex end and watch it work exactly as specified.
The readings at that end:
| Reading | Value | Why |
|---|---|---|
c_crc_errors | moving | the aborted frames fail their check |
c_alignment_errors | zero | the jam is a whole number of octets |
c_undersize / fragments | moving | the aborted frames are short |
c_oversize | zero | an abort makes frames shorter, never longer |
frames_in | healthy | the port counted them at the delimiter |
Now intersect, in Chapter 21.1 §5's order:
| Step | Surviving candidates |
|---|---|
| start | all twelve |
c_crc_errors moved | channel, PHY lanes, xMII, parser, CRC logic — 5 |
c_alignment_errors zero | parser, CRC logic — 2 |
c_undersize moved | parser — 1 |
c_oversize zero | none — 0 |
The mask goes empty at the fourth step, and Chapter 21.1 §13's contradiction fires.
That is the correct behaviour and it is the best outcome available, because the fault is not at any of the twelve sites: it is at the far end's MAC, which is outside Chapter 21.1's space entirely. An empty mask is that method saying so.
But it depends on a register almost nobody reads.
| Readings used | Result | Verdict |
|---|---|---|
crc, alignment, undersize, frames_in | one candidate: the parser | confidently wrong |
the same plus c_oversize | empty | honestly refused |
c_oversize is the least informative counter in Chapter 21.1 §6's table — 0.650 bits, joint last — and here it is the difference between a diagnosis that names an innocent block and one that admits the fault is elsewhere. A counter's average information content is not its worst-case value, and this is the case that pays for it.
And Chapter 21.3's slope test, run at the same end, refuses in a different way.
The FCS error rate at the full-duplex end is (1 − e^(−u)) × late_fraction(L) — and the late fraction is a function of the frame's length, so the rate rises with size just as a channel fault's does.
| Bucket | Midpoint | Late fraction | Error rate at 25% load |
|---|---|---|---|
| 0 | 64 | 0.00% | 0.000% |
| 1 | 96 | 33.33% | 7.373% |
| 3 | 383 | 83.29% | 18.424% |
| 5 | 1 271 | 94.96% | 21.006% |
Bucket 0 is exactly zero, so Chapter 21.3 §5's estimator picks bucket 1 as its low bucket and computes:
| Value | |
|---|---|
| slope, bucket 5 over bucket 1 | 2.849 |
| Chapter 21.3 §5's channel threshold | ≥ 5.00 |
| its logic threshold | ≤ 2.00 |
| verdict | slope_ambiguous — no decision |
A duplex mismatch lands squarely in Chapter 21.3's undecided band, and the ratio is load-independent — it is a pure ratio of late fractions, so it reads 2.849 at every utilisation. That chapter's method refuses to answer and is right to.
And the reading that would have named it is one neither chapter tests for.
A structural zero in bucket 0. A channel fault has errors in bucket 0 at a lower rate; a logic fault has them at the same rate. Only a duplex mismatch has none at all, and it is none rather than few.
| Fault | Bucket 0 error rate |
|---|---|
| a channel fault | non-zero, and 19.86× below bucket 5 |
| any logic fault | non-zero, and equal to bucket 5 |
| a duplex mismatch | exactly zero |
One bucket, one comparison against zero, and it separates this chapter's fault from both of Chapter 21.3's categories — using a cross that chapter already built.
And the reason the zero is trustworthy is worth one more paragraph, because a zero is usually the weakest kind of evidence.
Chapter 21.1 §10 spent a section on why a zero reading is fragile: an unimplemented counter reads zero, a window too short reads zero, and an undersized addend can read zero. None of those applies here.
| Ordinary reason a zero is untrustworthy | Does it apply to bucket 0? |
|---|---|
| the counter is not implemented | no — it is the same counter as bucket 5 |
| the window was too short | no — bucket 5 is full over the same window |
| the traffic never entered that bucket | check it; c_frames_in_bucket[0] says |
| the fault is rare | no — the other buckets are at 21% |
Row three is the only one that has to be checked and it is one more counter, already present. The reading that matters is bucket 0 carrying frames and reporting no errors while bucket 5 carries frames and reports 21% — a zero measured alongside a large number, over the same window, on the same counter. That is the strongest form a zero takes, and it is the form Chapter 21.1 §10's cautions were written to distinguish from the weak ones.
13. RTL 6 — The Load-Conditioned Alarm
// ---------------------------------------------------------------------
// load_conditioned_alarm -- report the fault's COST, not its symptom
// count, and report it at a load that has not happened yet.
//
// Section 7 derived the projection. This block is what turns it into
// an alarm that fires on a quiet link, which is the only time a
// mismatch is cheap to fix.
// ---------------------------------------------------------------------
module load_conditioned_alarm
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] far_end_util_ppt,
input logic [15:0] mean_late_fraction_ppt,
input logic [31:0] c_late_collisions,
input logic [31:0] c_frames_tx,
input logic mismatch_confirmed,
output logic [15:0] loss_now_ppt,
output logic [15:0] loss_at_full_load_ppt,
output logic [15:0] amplification,
output logic alarm,
output logic quiet_link_warning,
output logic evidence_below_floor
);
// Section 4: the collision probability at full load is 1 - 1/e,
// which is 632 parts per thousand. Not 1000.
localparam int P_COLL_FULL_PPT = 632;
always_comb begin
loss_now_ppt = (c_frames_tx == 32'd0) ? 16'd0
: 16'((c_late_collisions * 32'd1000) / c_frames_tx);
loss_at_full_load_ppt =
16'((32'(P_COLL_FULL_PPT) * 32'(mean_late_fraction_ppt)) / 32'd1000);
// How much worse this gets when the network gets busy. It is the
// number that decides whether a mismatch is fixed this week.
amplification = (loss_now_ppt == 16'd0) ? 16'd0
: 16'((32'(loss_at_full_load_ppt) * 32'd100) /
32'(loss_now_ppt));
// Fire on the PROJECTION, not on today's loss.
alarm = mismatch_confirmed && (loss_at_full_load_ppt > 16'd50);
// The trap: a quiet link with a confirmed mismatch shows almost
// nothing today and will show 60% when the backup job runs.
quiet_link_warning = mismatch_confirmed &&
(far_end_util_ppt < 16'd50) &&
(loss_at_full_load_ppt > 16'd300);
// Section 6's structural zero: minimum-size traffic produces no
// evidence at all, so the absence of late collisions says nothing.
evidence_below_floor = (mean_late_fraction_ppt < 16'd10);
end
endmoduleClassification: a projector, and the only alarm in Module 21 that fires on a link that currently works.
What it teaches: that amplification is the number that changes a decision. A link losing 0.95% today has a 60.5% loss waiting at full load — an amplification of 6 400% — and the two numbers describe the same fault. Reporting today's figure gets the ticket deprioritised; reporting the projection gets it fixed while the network is quiet, which is the only time the fix is free.
And it teaches that evidence_below_floor must gate every negative conclusion. A link carrying only minimum-size frames has a mean late fraction of essentially zero, so c_late_collisions stays at zero however broken the configuration is — and Section 6 showed that is a structural zero rather than a small number. "No late collisions" is not evidence of no mismatch on such a link; it is an absence of instrument.
Deliberately simplified: P_COLL_FULL_PPT uses the correct 632 while Section 7's predicted_loss_ppt uses a linear approximation, so the two blocks disagree by 58% at full load — the same inconsistency Section 7 flagged, still unfixed here. alarm's threshold of 5% projected loss is a judgement. amplification divides by loss_now_ppt and returns zero when today's loss is zero, which is exactly the quiet-link case the block exists for — the projection is still right and the ratio is undefined.
Production implication: the undefined amplification on a perfectly quiet link is the case to design for rather than around. A mismatch on a link carrying nothing has a projected loss of 60% and a current loss of zero, and the ratio is infinite. The output that matters there is loss_at_full_load_ppt alone, and quiet_link_warning exists to say that the projection is the whole content of the report — because a mismatch discovered on a quiet Sunday is a configuration change, and the same mismatch discovered on a busy Monday is an outage.
14. What a Mismatch Diagnosis Must Never Do
Six prohibitions. Two are about the load, two about the two ends, and two about the frame size — and every one of them turns a present fault into an absent one.
| Never | Because | |
|---|---|---|
| 1 | conclude "no mismatch" from zero late collisions | Section 6 — minimum-size traffic makes it a structural zero |
| 2 | report today's loss as the fault's cost | Section 13 — the amplification is up to 6 400% |
| 3 | test one direction only | Section 8 — the mismatch damages one direction |
| 4 | diagnose from one end | Chapter 11.4 §3 — the decisive symptoms need both |
| 5 | add early and late collisions into one figure | Section 3 — one costs time, the other costs the frame |
| 6 | read fragments at the full-duplex end as a cable fault | Section 10 — they are the far end's aborts |
Row one is the prohibition that hides the fault for years. A link between two devices exchanging mostly acknowledgements and control frames carries almost nothing above 64 octets, so its late-collision counter reads zero forever. The mismatch is present, costly, and completely invisible — until somebody runs a backup across it. The test that does work on such a link is the collision counter at one end against zero at the other, which is Chapter 11.4 §3's 1.81-bit symptom and needs the far end.
Row six is the prohibition that dispatches a technician. fcs_errors and fragments climbing together with a healthy frames_in is the textbook signature of a damaged medium, and on a mismatched link it is produced by a perfectly good cable. Chapter 21.2 §17's archaeology is what makes it convincing: the fragment counter's name says "collision remnant" and here, uniquely on a switched link, that is exactly what it is — from collisions that should have been impossible.
And the two that look like presentation choices and are not:
| Why it is a prohibition | |
|---|---|
| row two | the fault's cost is a projection; its symptom is a measurement of the network's quietness |
| row five | ten thousand early collisions is zero frames lost; ten thousand late ones is ten thousand |
Both report a number that describes the traffic rather than the fault, which is what all six share — and on this fault, more than on any other in Module 21, the traffic decides whether there is anything to see at all.
And two things a mismatch diagnosis should always do, stated positively.
| Always | Costs | |
|---|---|---|
| 1 | compare both ends' resolved duplex before reading a counter | two management reads |
| 2 | report the projected loss beside the measured one | one multiplication |
Row one finds the fault; row two gets it fixed, and neither needs traffic, load or a particular frame size. Everything else in this chapter exists because those two are not always available — a far end on somebody else's management domain, a link whose configuration is opaque — and because the projection needs the size mix, which only the counters know.
15. RTL 7 — Mismatch Telemetry
// ---------------------------------------------------------------------
// dupmis_telemetry -- three groups, and the split is by WHO acts.
//
// Evidence: what was observed, at which end.
// Cost: what it is costing now and what it will cost.
// Blindness: whether this link's traffic can show the fault at all.
// ---------------------------------------------------------------------
module dupmis_telemetry
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
input logic we_are_half_duplex,
input logic one_sided_collisions,
input logic cross_correspondence,
input logic late_seen,
input logic [15:0] late_ratio_ppt,
input logic [15:0] predicted_ppt,
input logic [15:0] loss_now_ppt,
input logic [15:0] loss_at_full_load_ppt,
input logic [15:0] mean_late_fraction_ppt,
input logic [15:0] bucket0_err_ppt,
input logic far_valid,
// Evidence.
output logic [2:0] confidence, // 0 none .. 4 proven
output logic proven_by_late,
output logic which_end_is_half,
// Cost.
output logic [15:0] cost_now_ppt,
output logic [15:0] cost_projected_ppt,
// Blindness.
output logic traffic_cannot_show_it,
output logic bucket0_is_zero,
output logic need_far_end
);
// Chapter 9.1 Section 5: a conforming half-duplex segment cannot
// produce a late collision. So one is proof, not evidence.
assign proven_by_late = late_seen;
assign which_end_is_half = we_are_half_duplex;
always_comb begin
if (proven_by_late && cross_correspondence) confidence = 3'd4;
else if (proven_by_late) confidence = 3'd3;
else if (one_sided_collisions) confidence = 3'd2;
else if (far_valid && cross_correspondence) confidence = 3'd1;
else confidence = 3'd0;
cost_now_ppt = loss_now_ppt;
cost_projected_ppt = loss_at_full_load_ppt;
// Section 6's structural zero, reported as a property of the
// traffic rather than of the link.
traffic_cannot_show_it = (mean_late_fraction_ppt < 16'd10);
// Section 12's free discriminator: a channel fault has errors in
// bucket 0 and a mismatch has none at all.
bucket0_is_zero = (bucket0_err_ppt == 16'd0);
need_far_end = !far_valid && (confidence < 3'd3);
end
endmoduleClassification: a reporting block whose third output group describes the instrument rather than the link.
What it teaches: that proven_by_late is a stronger word than anything else in Module 21 and it is earned. Chapter 9.1 §5 established that the slot time was sized so that a conforming half-duplex segment cannot produce a late collision — so one late collision is a proof of a configuration fault rather than evidence of one. Nothing in Chapter 21.1, Chapter 21.2 or Chapter 21.3 reaches that standard; all three produce candidate sets.
And it teaches that traffic_cannot_show_it outranks confidence of zero. A confidence of zero on a link whose traffic can show the fault is real evidence of no mismatch; the same zero on minimum-size traffic is no evidence at all. Reporting them as the same number is Section 14's first prohibition, and the two bits cost nothing to separate.
Deliberately simplified: confidence is a five-level ordinal with no meaning attached to the gaps. bucket0_is_zero is exported and not folded into confidence, where Section 12 argued it is a free discriminator worth having. cost_now_ppt and cost_projected_ppt are pass-throughs. And which_end_is_half is an input — the block cannot determine which end it is on from the counters, which is correct and worth noting: a MAC knows its own configured duplex and that is the one fact in this chapter nobody has to infer.
Production implication: the fact that a MAC knows its own duplex is the shortest path to this whole diagnosis and it is almost never used. A management system that collects duplex alongside link_up for every port can find every mismatch in a building by comparing the two ends of each cable — no counters, no load, no traffic, no waiting for somebody to notice a slow transfer. It is a configuration audit rather than a diagnosis, and it finds the fault before Section 13's alarm has anything to project.
16. RTL 8 — The Mismatch Conformance Monitor
// ---------------------------------------------------------------------
// dupmis_conformance_monitor -- six verdicts, and three of them are
// about whether the link's traffic can reveal the fault at all.
// ---------------------------------------------------------------------
module dupmis_conformance_monitor
import dupmis_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [31:0] c_frames_tx,
input logic [15:0] mean_late_fraction_ppt,
input logic [15:0] far_end_util_ppt,
input logic far_valid,
input logic late_seen,
input logic one_sided_collisions,
input logic model_disagrees,
input logic bidirectional_tested,
input logic collisions_summed,
output logic blind_traffic,
output logic load_too_low,
output logic one_ended_diagnosis,
output logic one_directional_test,
output logic counters_conflated,
output logic slot_time_wrong,
output logic diagnosis_sound
);
// Section 6: minimum-size traffic makes the late-collision counter a
// structural zero. No amount of running time changes it.
assign blind_traffic = (mean_late_fraction_ppt < 16'd10);
// Section 4: the collision probability is the far end's utilisation.
// Below about 1% there is nothing to observe in any reasonable window.
assign load_too_low = far_valid && (far_end_util_ppt < 16'd10);
// Chapter 11.4 Section 3: the two decisive symptoms need both ends.
assign one_ended_diagnosis = !far_valid && !late_seen;
assign one_directional_test = !bidirectional_tested;
assign counters_conflated = collisions_summed;
// Section 3: at 1 Gb/s the slot time is 4096 bit times, not 512.
assign slot_time_wrong = model_disagrees;
assign diagnosis_sound = !one_ended_diagnosis && !counters_conflated &&
!slot_time_wrong;
always_ff @(posedge clk) begin
if (rst_n && blind_traffic && (c_frames_tx > 32'd1000000))
$display("[dupmis] this link's frame sizes cannot produce a late collision");
end
endmoduleClassification: an auditor whose three loudest verdicts describe the traffic rather than the design.
What it teaches: that blind_traffic and load_too_low are deliberately not terms of diagnosis_sound. A diagnosis run on minimum-size traffic at one per cent load is correctly executed and cannot find anything — the instrument is blind and the engineer did nothing wrong. Folding a traffic property into a verdict about the investigation is the mistake Chapter 21.1 §16 avoided for instrumentation and this chapter avoids for load.
And it teaches that one_ended_diagnosis has an escape. It fires only when the far end is unavailable and no late collision has been seen — because a late collision is proof on its own and needs no correlation. Chapter 11.4 §3 said the decisive symptoms need both ends; one of the two does not, and the monitor encodes the exception rather than the rule.
Deliberately simplified: collisions_summed is an input describing how somebody else's dashboard is configured, which no hardware block can know. slot_time_wrong is model_disagrees renamed, so it conflates a gigabit slot time with a MAC that retries late collisions — two different causes of the same disagreement. And load_too_low's 1% threshold assumes a reasonable observation window rather than deriving one from the frame rate.
Production implication: the $display gated on a million transmitted frames is the block's most useful behaviour and the pattern is worth copying. A link that has sent a million frames without a single one being large enough to produce a late collision has proved something about itself — that this diagnosis will never work here — and saying so once, after enough evidence, is more useful than a flag that is true from reset. The alternative instrument on such a link is the configuration audit of Section 15, which needs no traffic at all.
17. The Cost Curve, and Where the Ticket Gets Filed
The frame loss is the fault's direct cost. What an application experiences is much worse, and the gap is where the ticket comes from.
A loss-based congestion control reacts to a dropped segment by halving its window, so the throughput it achieves falls roughly as the inverse square root of the loss rate. Using the standard approximation, with a 1 460-octet segment:
| Far-end load | Frame loss | TCP throughput, 0.5 ms RTT | 1 ms | 10 ms |
|---|---|---|---|---|
| 0.5% | 0.48% | 414 Mb/s | 207 | 20.7 |
| 1% | 0.95% | 293 | 147 | 14.7 |
| 2% | 1.90% | 208 | 104 | 10.4 |
| 5% | 4.67% | 132 | 66.2 | 6.6 |
| 10% | 9.12% | 94.8 | 47.4 | 4.7 |
| 25% | 21.19% | 62.2 | 31.1 | 3.1 |
| 50% | 37.69% | 46.6 | 23.3 | 2.3 |
Read the 1 ms column against a 100 Mb/s link.
| Value | |
|---|---|
| loss at which TCP falls below 100 Mb/s | 2.05% |
| far-end load that produces it | 2.16% |
| the link's own utilisation graph then | a few per cent |
A mismatched 100 Mb/s link stops delivering its line rate once the far end exceeds about two per cent utilisation, and at that point every counter on the link looks unremarkable: a small number of collisions at one end, a small number of fragments at the other, and a utilisation graph that says the link is almost idle.
And the gigabit case is worse by two orders of magnitude, for a reason that has nothing to do with the fault.
| 100 Mb/s | 1 Gb/s | |
|---|---|---|
| loss at which TCP falls below line rate | 2.05% | 0.020% |
| far-end load that produces it | 2.16% | 0.02% |
A faster link needs a larger congestion window to fill, and a larger window is harder to sustain under loss — so the loss rate a gigabit link tolerates before falling short of its rate is a hundredth of what a 100 Mb/s link tolerates. The fault is identical; the tolerance is not. This is Chapter 20.6 §8's observation in a different domain: the same absolute impairment is a different fraction at each rate.
Which gives the answer to where the ticket gets filed, and it is not where the counters are.
| Observer | What they see at 2% far-end load |
|---|---|
| the link's counters | a handful of collisions and fragments |
| the utilisation graph | 2% — idle |
| the monitoring threshold | not crossed |
| the application | a transfer at half speed |
| the user | "the network is slow" |
Rows one to three are all below any reasonable alarm and row five is a ticket. Chapter 11.4 §3 measured it is slow at 0.49 bits — the least informative symptom in its table — and this is why it is the one that arrives: it is the only observer whose threshold the fault crosses.
The fault is loud in the application and quiet in every instrument that watches the link, and the gap between them is a factor of the congestion control's square root rather than anything about Ethernet.
And there is a third observer whose reading is the most misleading of all, because it is the one a capacity planner uses.
| On a mismatched 100 Mb/s link at 2% far-end load | |
|---|---|
| octets on the wire | includes every retransmission |
| Chapter 8.3 §4's throughput | what the wire carried |
| its goodput | what arrived once |
| the difference | the retransmissions, which grow with the loss |
Chapter 8.3 §5 built a separate goodput accumulator for exactly this reason, and almost no deployed system has one. A utilisation graph counts octets on the wire, so a link whose application traffic has collapsed to a tenth but which is retransmitting heavily can show a utilisation figure that is higher than the useful work would suggest, not lower.
| Loss | Useful fraction of the octets carried |
|---|---|
| 0% | 100% |
| 2% | about 98% |
| 21% | about 79% |
| 37.7% | about 62% |
At the losses this chapter derives, up to 38% of the octets on the wire are frames that will be sent again — and Chapter 8.3 §4's distinction is the only vocabulary that separates them. A link is doing 38% more work than it is delivering, and every instrument that counts octets reports the work.
18. What the Diagnosis Assumes
Nine assumptions. Three are about the model, three about the counters and three about the configuration — and the first is false at gigabit.
| Assumption | From | If false | |
|---|---|---|---|
| 1 | the slot time is 512 bit times | Chapter 9.1 §5 | at 1 Gb/s it is 4 096 — the safe size moves by 8× |
| 2 | a late collision is not retried | 802.3 permits either | loss becomes latency, and the far end cannot tell |
| 3 | the far end's frame starts are Poisson | convenience | bursty traffic raises the collision rate above 1 − e^(−u) |
| 4 | both ends' counters cover one window | a management path | Section 9 — the correspondence test needs deltas |
| 5 | the jam pattern is a whole number of octets | 802.3 | alignment errors appear and Section 12's mask changes |
| 6 | the half-duplex end knows it is half-duplex | its own configuration | Section 15's audit is impossible |
| 7 | the traffic contains frames above 64 octets | nothing | Section 6's structural zero — the fault is invisible |
| 8 | the size histogram's buckets are as published | Chapter 19.7 §2 | Section 7's weighting is wrong |
| 9 | the link is point-to-point | modern practice | on a real shared segment, both ends colliding is normal |
Row one is the assumption that inverts the chapter's headline and it is worth the arithmetic. At 1 Gb/s, carrier extension makes the slot time 4 096 bit times, so the late fraction is:
| Frame | Late fraction at 512 bits | at 4 096 bits |
|---|---|---|
| 64 octets | 0.00% | 0.00% |
| 512 octets | 87.50% | 0.00% |
| 1 000 octets | 93.60% | 48.80% |
| 1 518 octets | 95.78% | 66.27% |
The safe size moves from 64 octets to 512 — a factor of eight — and a diagnosis calibrated at 100 Mb/s reports a gigabit mismatch as healthy for every frame below 512 octets. The saving grace is that gigabit half-duplex is rare; the trap is that "rare" is not "absent", and the model is silently wrong rather than loudly inapplicable.
Row three is the assumption that makes the numbers optimistic. Real traffic is bursty, so the far end's frame starts cluster — and a cluster during our frame is more likely to overlap it than a Poisson process of the same mean rate. The measured collision rate on real traffic runs above 1 − e^(−u), which means Section 6's table is a floor rather than an estimate, and every projection in Section 13 is conservative.
Row nine is the assumption that would have been false for the first twenty years of Ethernet. On a genuinely shared segment both ends collide and that is correct behaviour — Chapter 11.4 §3's "collisions at one end only" is decisive precisely because shared segments no longer exist. The symptom's 1.81 bits are borrowed from the topology, not from the protocol, and on a hub they are worth nothing at all.
And three things deliberately not assumed:
| Not assumed | Why not |
|---|---|
| that zero late collisions means no mismatch | Section 6's structural zero |
| that the loss today is the fault's cost | Section 13 — up to 6 400% amplification |
| that one end's counters are a diagnosis | Chapter 11.4 §3, and Section 10's disjoint sets |
Row three is the chapter's structural finding and it is stronger than the others. The two ends' counter sets do not intersect at all — four transmit counters against three receive counters — so "compare the two ends" is not a comparison and cannot be one. It is a join between two different quantities, and the pattern it looks for is a correspondence rather than an agreement.
19. The Cost, Accounted
A mismatch diagnosis is counters crossed with a size histogram, and the histogram already exists.
| Block | Flops | Nature |
|---|---|---|
dupmis_pkg | 0 | a slot time and a ratio |
collision_kind_split | ~161 | five counters and a sticky bit |
late_collision_discriminator — 7 × 2 × 32 | ~455 | collisions crossed with size |
size_loss_model | 0 | combinational |
two_end_counter_join | 0 | combinational |
fragment_provenance | ~32 | one counter |
load_conditioned_alarm | 0 | combinational |
dupmis_telemetry | 0 | combinational |
dupmis_conformance_monitor | 0 | combinational |
| total | ~648 flops |
Five of the nine blocks are pure combinational logic and one block is 70% of the cost — the collision-by-size cross, which is the same shape as Chapter 21.3 §19's finding: the expensive part is always the one that bins something.
And the addition to a design is smaller than the total, because two of the counters are already required.
| Addition | Flops | Buys |
|---|---|---|
late_collisions, separately from collisions | already required by RMON | Section 3's decisive proof |
| collisions crossed with the size histogram | ~455 | the late ratio, and the prediction |
| fragment lengths logged rather than binned | ~0 — a field | Section 11's provenance |
| the far end's duplex setting, collected | ~0 — a management read | Section 15's audit, which needs no traffic |
Row four is the cheapest and the most effective and it is not a hardware change at all. A MAC already knows its own duplex; a management system that collects it for both ends of every cable finds every mismatch in a building by comparison, before any traffic, before any counter moves, and before Section 13's alarm has anything to project.
| Finds a mismatch | Needs | |
|---|---|---|
| the configuration audit | always | a management read at both ends |
| late collisions | only above 64-octet traffic | traffic, and load |
| the correspondence test | only with both ends' counters | a management path, and load |
| the application's complaint | eventually | a user |
Row one dominates rows two and three on every axis — it is cheaper, faster, and works on a link carrying nothing — and the reason the rest of the chapter exists is that the audit tells you a mismatch is present and nothing about what it is costing. Section 13's projection is the other half, and it is the half that gets the change scheduled.
And the audit has one blind spot that the counters do not, which is why neither replaces the other.
| The audit sees | The counters see | |
|---|---|---|
| a configured mismatch | yes | only under the right traffic |
| a negotiated mismatch | no — both ends report what they resolved to | yes |
| a mismatch on a link carrying nothing | yes | no |
| the cost | no | yes, and only at the current load |
Row two is the gap and it is Chapter 11.4 §2's cause 2. A lost link pulse can make one end negotiate a step down while both ends report themselves as auto-negotiating and both report a resolved duplex — so the two configured settings agree and the two resolved settings do not. The audit has to compare resolved duplex rather than configured duplex, which most management interfaces expose as separate fields and most collectors gather only one of.
Module 21's running instrumentation bill, five 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 | ~792 | 5.6% |
| Chapter 21.4 §19 | ~71 | 0.5% |
| this chapter | ~455 | 3.2% |
| total | ~2 041 | 14.4% |
Fourteen point four per cent, and this chapter's share buys the one fault in the module whose cost is a projection rather than a measurement — which is the only kind that can be fixed before it is felt.
20. Properties Worth Asserting, and One Worth Refusing
Thirty-three properties and eight covers, in four groups: the slot time, the two ends, the loss model, and the blindness.
Group one — the slot time and the two collision kinds.
// Chapter 9.1 Section 5's slot time as a classifier, and the two
// outcomes whose costs differ completely.
p_kind_by_slot: assert property (@(posedge clk) disable iff (!rst_n)
(kind == COLL_LATE) |-> (bits_transmitted > 16'(SLOT_BITS)));
p_early_is_bounded: assert property (@(posedge clk) disable iff (!rst_n)
(kind == COLL_EARLY) |-> (bits_transmitted <= 16'(SLOT_BITS)));
p_early_not_lost: assert property (@(posedge clk) disable iff (!rst_n)
(kind == COLL_EARLY) |=>
(c_frames_lost == $past(c_frames_lost)));
p_late_is_lost: assert property (@(posedge clk) disable iff (!rst_n)
(kind == COLL_LATE) |=>
(c_frames_lost == $past(c_frames_lost) + 32'd1));
p_min_frame_safe: assert property (@(posedge clk) disable iff (!rst_n)
(wire_len == 14'(MIN_FRAME_OCTETS)) |-> (kind != COLL_LATE));
p_late_is_sticky: assert property (@(posedge clk) disable iff (!rst_n)
late_seen |=> always late_seen);
p_kinds_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
kind_valid |-> ((kind == COLL_EARLY) != (kind == COLL_LATE)));
p_no_coll_when_idle: assert property (@(posedge clk) disable iff (!rst_n)
!tx_active |-> (kind == COLL_NONE));Group two — the two ends, and the fact that their counter sets are disjoint.
p_sets_disjoint: assert property (@(posedge clk) disable iff (!rst_n)
((HALF_DUPLEX_END & FULL_DUPLEX_END) == 7'd0));
p_hd_no_fcs: assert property (@(posedge clk) disable iff (!rst_n)
we_are_half_duplex |-> (our_fcs_errors == 32'd0));
p_fd_no_collisions: assert property (@(posedge clk) disable iff (!rst_n)
!we_are_half_duplex |-> (our_collisions == 32'd0));
p_one_sided_needs2: assert property (@(posedge clk) disable iff (!rst_n)
one_sided_collisions |-> far_valid);
p_correspondence: assert property (@(posedge clk) disable iff (!rst_n)
cross_correspondence |-> (hd_late > 32'd64));
p_confirm_needs_both:assert property (@(posedge clk) disable iff (!rst_n)
mismatch_confirmed |->
(one_sided_collisions && cross_correspondence));
p_far_end_flagged: assert property (@(posedge clk) disable iff (!rst_n)
!far_valid |-> need_far_end);
p_late_is_proof: assert property (@(posedge clk) disable iff (!rst_n)
proven_by_late |-> (confidence >= 3'd3));Group three — the loss model, and its predicted values.
p_late_frac_monotone:assert property (@(posedge clk) disable iff (!rst_n)
(wire_len > 14'(MIN_FRAME_OCTETS)) |->
(late_fraction_ppt(int'(wire_len)) > 0));
p_late_frac_bounded: assert property (@(posedge clk) disable iff (!rst_n)
late_fraction_ppt(int'(wire_len)) < 1000);
p_min_frac_zero: assert property (@(posedge clk) disable iff (!rst_n)
(wire_len <= 14'(MIN_FRAME_OCTETS)) |->
(late_fraction_ppt(int'(wire_len)) == 0));
p_ratio_bounded: assert property (@(posedge clk) disable iff (!rst_n)
late_ratio_ppt <= 16'd1000);
p_prediction_checked:assert property (@(posedge clk) disable iff (!rst_n)
prediction_holds |-> (c_frames_tx > 32'd1000));
p_projection_ge_now: assert property (@(posedge clk) disable iff (!rst_n)
(far_end_util_ppt < 16'd632) |->
(loss_at_full_load_ppt >= loss_now_ppt));
p_alarm_on_forecast: assert property (@(posedge clk) disable iff (!rst_n)
alarm |-> (loss_at_full_load_ppt > 16'd50));
p_quiet_needs_low: assert property (@(posedge clk) disable iff (!rst_n)
quiet_link_warning |-> (far_end_util_ppt < 16'd50));
p_model_disagreement:assert property (@(posedge clk) disable iff (!rst_n)
model_disagrees |-> (total_coll > 32'd64));Group four — the blindness, which is the group that exists because of Section 20's refused property.
p_blind_is_traffic: assert property (@(posedge clk) disable iff (!rst_n)
blind_traffic |-> (mean_late_fraction_ppt < 16'd10));
p_blind_not_unsound: assert property (@(posedge clk) disable iff (!rst_n)
blind_traffic |-> (diagnosis_sound || one_ended_diagnosis));
p_zero_needs_qual: assert property (@(posedge clk) disable iff (!rst_n)
((c_late_collisions == 32'd0) && (confidence == 3'd0))
|-> (traffic_cannot_show_it || !blind_traffic));
p_bucket0_zero: assert property (@(posedge clk) disable iff (!rst_n)
(mismatch_confirmed && bucket0_is_zero) |-> 1'b1);
p_load_floor: assert property (@(posedge clk) disable iff (!rst_n)
load_too_low |-> (far_end_util_ppt < 16'd10));
p_bidirectional: assert property (@(posedge clk) disable iff (!rst_n)
!bidirectional_tested |-> one_directional_test);
p_conflation_flag: assert property (@(posedge clk) disable iff (!rst_n)
collisions_summed |-> counters_conflated);
p_sound_excludes: assert property (@(posedge clk) disable iff (!rst_n)
diagnosis_sound |->
(!one_ended_diagnosis && !counters_conflated));
p_gig_slot: assert property (@(posedge clk) disable iff (!rst_n)
slot_time_wrong |-> model_disagrees);And eight covers, because six of these states need a traffic mix a default regression does not generate.
c_late_collision: cover property (@(posedge clk) kind == COLL_LATE);
c_early_only: cover property (@(posedge clk) (c_early > 32'd1000) && (c_late == 32'd0));
c_min_size_only: cover property (@(posedge clk) blind_traffic);
c_both_ends: cover property (@(posedge clk) mismatch_confirmed);
c_one_sided: cover property (@(posedge clk) one_sided_collisions);
c_quiet_link: cover property (@(posedge clk) quiet_link_warning);
c_bucket0_zero: cover property (@(posedge clk) bucket0_is_zero && (c_late > 32'd0));
c_frag_at_slot: cover property (@(posedge clk) collision_abort_likely);21. Verification Scenarios
Fifty-eight scenarios, plus a five-run directed test whose variable is the generator's frame size — because that is what decides whether the fault is observable at all.
The slot time and collision kinds — 11 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 1 | collision at bit 100 of a 1 518-octet frame | COLL_EARLY; the frame is retried |
| 2 | collision at bit 600 of the same | COLL_LATE; the frame is lost |
| 3 | collision at bit 512 exactly | early — the boundary is inclusive |
| 4 | collision at bit 513 | late |
| 5 | any collision on a 64-octet frame | early, always |
| 6 | ten thousand early collisions | c_frames_lost = 0 |
| 7 | ten thousand late collisions | c_frames_lost = 10 000 |
| 8 | the RMON collisions counter on both | 10 000 in both cases |
| 9 | one late collision, ever | late_seen sticky — proof |
| 10 | a collision with tx_active low | COLL_NONE |
| 11 | at a 4 096-bit slot time, a 512-octet frame | early, always — Section 18's row one |
The loss model — 12 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 12 | u = 1%, 1 518-octet frames | loss 0.95% |
| 13 | u = 10%, same | 9.12% |
| 14 | u = 25%, same | 21.19% |
| 15 | u = 50%, same | 37.69% |
| 16 | u = 50%, 64-octet frames | 0.00% |
| 17 | u = 50%, 128-octet frames | 19.67% |
| 18 | late fraction at 1 518 | 95.78% |
| 19 | late fraction at 64 | 0.00% |
| 20 | late fraction at 9 000 | 99.29% |
| 21 | expected attempts at u = 50%, min size | 1.649 |
| 22 | P(sixteen collisions) at u = 50% | 3.3 × 10⁻⁷ |
| 23 | projected loss at full load, 1 518 | 60.5% |
The two ends — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 24 | half-duplex end's fcs_errors | zero |
| 25 | full-duplex end's collisions | zero |
| 26 | the two counter sets intersected | empty |
| 27 | collisions at one end, zero at the other | one_sided_collisions |
| 28 | both ends colliding — a real shared segment | not one-sided; correct behaviour |
| 29 | HD late collisions against FD fragments | ratio near 1 000 ppt |
| 30 | the ratio at 1 200 ppt | outside tolerance; no correspondence |
| 31 | far end unavailable | need_far_end |
| 32 | far end unavailable, one late collision seen | confidence 3 anyway — it is proof |
| 33 | two ends read a minute apart | deltas correlate; absolute counts do not |
What the other chapters' methods say — 9 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 34 | Chapter 21.1's mask at the FD end, four readings | converges on the parser — wrong |
| 35 | the same plus c_oversize | empty mask; contradiction |
| 36 | Chapter 21.3's slope, buckets 1 and 5 | 2.849 |
| 37 | against its thresholds | slope_ambiguous — no verdict |
| 38 | the slope at u = 5% instead of 25% | still 2.849 — load-independent |
| 39 | bucket 0's error rate | exactly zero |
| 40 | a channel fault's bucket 0 | non-zero, 19.86× below bucket 5 |
| 41 | a logic fault's bucket 0 | non-zero, equal to bucket 5 |
| 42 | the bucket-0 test | separates all three in one comparison |
Blindness and load — 8 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 43 | a link carrying only 64-octet frames | blind_traffic; zero late collisions forever |
| 44 | that link, run for a year | still zero |
| 45 | that link, far end at 90% load | still zero |
| 46 | evidence_below_floor on it | asserts |
| 47 | far end at 0.5% load, 1 518-octet traffic | 0.48% loss — below most thresholds |
| 48 | quiet_link_warning on it | asserts — the projection is 60.5% |
| 49 | amplification on it | about 6 400% |
| 50 | a one-directional download test | full rate; healthy — and wrong |
Fragments and provenance — 8 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 51 | fragment lengths after early collisions | clustered near 68 octets |
| 52 | fragment lengths after late collisions | spread across the frame |
| 53 | fragment lengths after a cut cable | spread |
| 54 | collision_abort_likely on a cluster | asserts |
| 55 | the same with far-end late collisions known | provenance 3 regardless of lengths |
| 56 | Chapter 21.2 §17's three causes plus this one | four, and only this one has a companion counter |
| 57 | c_self_inflicted above half the fragments | provenance 2 — our own truncation |
| 58 | fragments with no far-end data and no cluster | provenance 1 — a cut, by elimination |
And the directed test, because random stimulus will not produce it.
The case: one misconfigured link, five generator settings, and the fault is found in two of them.
The variable is not in the design and not on the wire. It is the frame size the generator chose — and Section 6 showed that at one setting the failure probability is exactly zero. A constrained-random generator with a default size distribution weighted towards minimum size, which is what most environments use because it maximises frame rate, will not find this fault in any run of any length.
| Run | Generator's frame size | Far-end load | P(late collision per frame) |
|---|---|---|---|
| A | 64 octets | 50% | 0.000 |
| B | 64 octets | 90% | 0.000 |
| C | 128 octets | 10% | 0.048 |
| D | 1 518 octets | 1% | 0.010 |
| E | 1 518 octets | 50% | 0.377 |
The oracle is four-part and runs A and B are the controls that matter.
| Part | A | B | C | D | E |
|---|---|---|---|---|---|
p_no_late_collisions fails | never | never | often | sometimes | constantly |
| the design's configuration | broken | broken | broken | broken | broken |
blind_traffic | asserted | asserted | low | low | low |
| loss | 0% | 0% | 4.76% | 0.95% | 37.69% |
Row two is the control and it is the whole test. The link is identically misconfigured in all five runs — the same duplex mismatch, the same two devices, the same cable. Rows one and four move from "never" to "constantly" and from 0% to 37.7%, and none of that movement is about the design.
Runs A and B together are the important pair. Raising the far-end load from 50% to 90% — nearly doubling the collision probability — changes the failure count from zero to zero, because the frame size is what makes it structurally impossible. No amount of load, no amount of time, no seed. That is the distinction between this class and a rare event, and it is why Section 20 refuses the unconditioned property.
Run D is the one that is most misleading in practice. A one per cent loss at one per cent load passes most monitoring thresholds and is a 60.5% loss waiting for a busy afternoon — Section 13's projection — and the run that finds it is the run nobody schedules, because a 1% loss in a regression looks like noise.
22. Debugging a Duplex Mismatch
Six complaints.
Complaint 1 — "ping is fine so the link is fine."
| Check | If yes | Meaning |
|---|---|---|
| what size are the pings? | 64 octets, padded | Section 8 — structurally immune |
| does a large ping fail? | often | try 1 400 bytes of payload |
| is the loss size-dependent? | yes | and that is the signature |
| did anybody test the other direction? | no | the mismatch is unidirectional |
A default ping sends a frame that cannot suffer a late collision, and ARP and TCP acknowledgements are the same 64 octets by Chapter 5.6's padding rule. Every small exchange works perfectly and the application does not, which is the report that arrives and the observation that is dismissed.
Complaint 2 — "there are collisions but it is a switched link, so the counter is broken."
| Check | If yes | Meaning |
|---|---|---|
| is our end half-duplex? | yes | then it runs CSMA/CD |
| is the far end full-duplex? | check it | that is the fault |
| are there late collisions? | yes | proof — Chapter 9.1 §5 |
| does the far end report collisions? | no | one_sided_collisions |
The counter is not broken and the link is not shared. A half-duplex MAC runs CSMA/CD whatever is at the other end, and a full-duplex partner transmitting into it produces exactly the collisions the counter is for. Rows three and four are the two decisive symptoms Chapter 11.4 §3 measured at 1.81 bits each.
Complaint 3 — "the FCS errors say the cable is bad and we replaced it."
| Check | If yes | Meaning |
|---|---|---|
| which end sees the FCS errors? | the full-duplex one | Section 10 |
| does the other end see collisions? | yes | it is aborting frames |
| do fragment and late-collision counts track? | yes | cross_correspondence |
| is bucket 0's error rate zero? | yes | no channel fault has that |
Row four is the free discriminator and it needs no far-end access. A channel fault damages minimum-size frames too, at a rate 19.86× below the maximum-size rate but not at zero. A duplex mismatch damages none of them at all — Chapter 21.3's size cross already has the data, and one comparison against zero separates this fault from both of that chapter's categories.
Complaint 4 — "the monitoring system says the link is 3% utilised and the users say it is slow."
| Check | If yes | Meaning |
|---|---|---|
| is there a duplex mismatch? | check both ends' settings | Section 15's audit |
| what is the frame loss? | about 2% | Section 17 |
| what does TCP do at 2% loss? | falls below 100 Mb/s | the inverse square root |
| what is the link's utilisation? | 3% | and it is not wrong |
Both numbers are correct. The link really is 3% utilised because TCP has collapsed its window and is not offering more, and the loss that caused the collapse is 2% — below every sensible alarm. Chapter 8.3 §4's throughput-against-goodput distinction is the vocabulary; the operational fact is that a utilisation graph cannot show this fault by construction.
Complaint 5 — "we fixed the duplex and the collisions stopped but throughput is still poor."
| Check | If yes | Meaning |
|---|---|---|
| are late collisions zero now? | yes | the mismatch is fixed |
| are FCS errors zero at the far end? | yes | confirmed from both ends |
| is TCP still slow? | sometimes, for a while | congestion state persists |
| is there a second fault? | check the conservation identity | Chapter 21.1 §17 |
Row three is usually the answer and it is not a network fault. A connection that has spent hours at a collapsed window recovers over its own timescale, and a benchmark run immediately after the fix measures the recovery rather than the link. Row four is the check that costs six register reads and distinguishes "still recovering" from "a second fault was masked by the first."
Complaint 6 — "our gigabit link has a duplex mismatch and none of these numbers match."
| Check | If yes | Meaning |
|---|---|---|
| what is the slot time? | 4 096 bit times | Section 18's row one |
| what is the safe frame size? | 512 octets, not 64 | eight times larger |
model_disagrees? | yes | the model is calibrated at 100 Mb/s |
| is gigabit half-duplex even configured? | rarely, and check | it is unusual, not impossible |
Carrier extension moves the boundary by a factor of eight, so every frame below 512 octets is immune and the late fraction at 1 518 octets falls from 95.78% to 66.27%. The chapter's arithmetic is right and its constant is wrong for this link, which model_disagrees catches and a reader who trusts the table does not.
Complaint 7 — "both ends are set to auto-negotiate, so there cannot be a mismatch."
| Check | If yes | Meaning |
|---|---|---|
| are both ends really auto? | check, do not assume | one is usually forced |
| does one end report a forced setting? | yes | Chapter 11.4 §2's cause 7 |
| what does the auto end detect? | parallel detection | Chapter 11.1 §8 |
| what duplex does parallel detection choose? | half | and the forced end is full |
This is the commonest way a mismatch is created and it involves no fault at all. A forced end sends no negotiation bursts; the auto end falls back to Chapter 11.1 §8's parallel detection, which can determine the speed from the signalling and cannot determine the duplex — so it chooses half, per the standard. The forced end is full duplex because somebody configured it that way. Two correctly-behaving devices, one mismatch, and Chapter 11.1 §9 says exactly what parallel detection can and cannot determine.
And the three symptoms this chapter is systematically blamed for:
| Symptom | Blamed on | Usually is |
|---|---|---|
| FCS errors at one end | the cable | the other end aborting frames |
| collisions on a switched link | a broken counter | a half-duplex MAC doing its job |
| slow transfers on an idle link | the application | 2% loss and an inverse square root |
23. Misconceptions
Misconception 1 — "a duplex mismatch shows up as errors."
The wrong model: a misconfigured link produces error counts, so the counters will find it.
What it costs: it produces error counts only on frames larger than the slot time. A link carrying acknowledgements, ARP and control traffic — all 64 octets by Chapter 5.6's padding rule — shows zero late collisions forever, at any load, for any length of time. The fault is present, costly the moment anybody sends a large frame, and completely invisible until then.
The corrected model: the counters are conditional on the traffic. The unconditional instrument is the configuration: both ends know their own duplex, and comparing them finds every mismatch in a building with no traffic at all. Section 19 prices it at zero hardware.
Misconception 2 — "collisions mean a shared segment."
The wrong model: collisions are a CSMA/CD phenomenon and CSMA/CD implies a shared medium.
What it costs: a half-duplex MAC runs CSMA/CD whatever is at the other end, and a full-duplex partner transmitting into it produces genuine collisions on a point-to-point link. The counter is correct and the conclusion is not. An engineer who dismisses the counter as broken has dismissed the 1.81-bit symptom Chapter 11.4 §3 identified as one of only two decisive ones.
The corrected model: collisions at one end only are impossible under contention — on a shared segment both ends collide. One-sided collisions are the fault's signature, and a late collision is stronger still: Chapter 9.1 §5 sized the slot time so that a conforming segment cannot produce one.
Misconception 3 — "larger frames are always better."
The wrong model: Chapter 8.3 §2's efficiency curve says use large frames, so use large frames.
What it costs: on a mismatched link the large frames are precisely the ones that do not arrive. The late fraction at 1 518 octets is 95.78% and at 64 octets is zero — the mirror image of the efficiency curve, from the same fixed-quantity-against-variable-payload arithmetic, with the opposite conclusion. Both chapters are right in their own context.
The corrected model: the advice is conditional on the link being correctly configured, which is true of almost all performance advice and is almost never stated. On a mismatched link the efficient frame size and the surviving frame size are at opposite ends of the range.
Misconception 4 — "the loss we measure is what the fault costs."
The wrong model: the frame loss is 0.95%, so the fault costs 0.95%.
What it costs: 0.95% is what it costs at the far end's current one per cent utilisation. At full load the same fault costs 60.5% — an amplification of about 6 400% — and the difference is entirely the network's quietness. A ticket that reports today's figure gets deprioritised; the same ticket reporting the projection gets fixed on the quiet afternoon when the fix is free.
The corrected model: report two numbers. The measurement describes the traffic; the projection describes the fault, and only the second one is a property of the link. Section 13's amplification is the ratio between them.
Misconception 5 — "a healthy utilisation graph means a healthy link."
The wrong model: the link is 3% utilised, so it has plenty of headroom.
What it costs: at 2% frame loss a loss-based congestion control collapses its window, so the link is 3% utilised because nothing is offering more — and most of those octets are retransmissions. Chapter 8.3 §4's throughput-against-goodput distinction is exactly this, and a utilisation graph measures the wrong one of the two by construction.
The corrected model: a link whose utilisation is low and whose users complain is not a contradiction; it is the signature. The loss rate that produces it — about 2% on a 100 Mb/s link, 0.02% at gigabit — is below every ordinary alarm threshold, and the threshold is not the problem: the inverse square root is.
Misconception 6 — "we tested it and it ran at line rate."
The wrong model: a throughput test passed, so the link is fine.
What it costs: the mismatch damages one direction only — the half-duplex end's transmissions — so a test that measured the other direction reports full rate, correctly. A download from a server across a mismatched link is unaffected; the upload collapses, and which one a benchmark chose is a coin flip.
The corrected model: test both directions, always, and on this fault more than on any other in Module 21, because it is the only one in the module whose effect is unidirectional by construction rather than by accident.
24. Interview Questions
Question 1 — "Why does ping work and the file transfer fail on a duplex-mismatched link?"
What the answer should establish: the slot time is 512 bit times and a minimum frame is 512 bits. A collision on a 64-octet frame can never be late, and a collision that is not late is retried by the MAC and succeeds. A strong answer gives the other end of the curve: a 1 518-octet frame is 12 144 bits, of which 95.78% lie beyond the slot time, and a late collision is not retried. The strongest answer notes that ARP and TCP acknowledgements are padded to 64 octets too, so almost everything small is immune.
Question 2 — "How much does a duplex mismatch cost?"
What the answer should establish: that the question needs two parameters — the far end's load and the frame size — and the fault contributes neither. Loss is about (1 − e^(−u)) times the late fraction, so at 10% load with maximum-size frames it is 9.12% and at any load with minimum-size frames it is exactly zero. A strong answer gives the projection: 60.5% at full load, and notes that the number worth reporting is the projection rather than today's measurement.
Question 3 — "The two ends of a mismatched cable report different faults. Which is right?"
What the answer should establish: both, and their counter sets do not intersect. The half-duplex end sees collisions, late collisions and deferrals — four transmit counters; the full-duplex end sees FCS errors, fragments and undersize — three receive counters. A strong answer names the join: the half-duplex end's late-collision count and the full-duplex end's fragment count are the same events counted from two sides and should track one-to-one.
Question 4 — "You are at the full-duplex end and see FCS errors and fragments. What does the standard method conclude?"
What the answer should establish: an empty candidate mask, if the oversize counter is read. Chapter 21.1's intersection eliminates the channel, PHY and xMII on the zero alignment count, narrows to the parser on the undersize count, and then eliminates the parser on the zero oversize count — a contradiction, which is the correct answer because the fault is outside its twelve sites. A strong answer notes what happens without that one register: a confident, wrong diagnosis of the frame parser.
Question 5 — "What single free observation separates this from a channel fault?"
What the answer should establish: the error rate in the smallest size bucket, against zero. A channel fault damages minimum-size frames at a rate 19.86× below the maximum-size rate — non-zero. A logic fault damages them at the same rate as everything else — non-zero. A duplex mismatch damages none of them at all. A strong answer notes that Chapter 21.3's size cross already has the data, and that its slope test lands at 2.849 — inside its own ambiguous band — so it correctly refuses rather than answering wrongly.
Question 6 — "How would you find every duplex mismatch in a building?"
What the answer should establish: not with counters. Every MAC knows its own configured duplex; a management system that collects it for both ends of every cable finds every mismatch by comparison — no traffic, no load, no waiting for a frame large enough to produce evidence. A strong answer contrasts it with the counter-based route: that one works only above 64-octet traffic, only under load, and only with both ends' counters, and it tells you the cost, which the audit does not.
25. Questions and Answers
26. What's Next
Module 21 has four chapters left, and this one has established the pattern the next three follow: a fault whose visibility is a function of the traffic rather than of the fault.
| Chapter | Takes | What makes it invisible |
|---|---|---|
| this chapter | a duplex mismatch | the frame size — a structural zero at 64 octets |
| Chapter 21.6 | Chapter 21.1 §4's class G | five sites with no counter between them |
| Chapter 21.7 | Chapter 19.6's descriptors | a fault no frame can produce |
| Chapter 21.8 | a link that is slow and correct | no error counter moves at all |
Chapter 21.6 is the batch's hardest and it inherits this chapter's most useful instrument. Section 12 showed that Chapter 19.7 §2's size histogram is the only thing in a MAC conditioned on size, and that a structural zero in bucket 0 named this fault for free. An MTU mismatch drops only frames above a threshold — the same shape, a different threshold — and that is the one member of class G a size-conditioned instrument can reach. Which of the other four it cannot reach is the question that chapter has to answer honestly.
Chapter 21.7 takes the fault Chapter 20.5 §12 proved no frame can produce. A consumer stall long enough to overflow Chapter 19.5's buffer is not reachable from the wire at any rate, in any pattern — so it arrives in that chapter as the thing being diagnosed rather than injected, and the descriptor-ownership space around it is Chapter 19.6's outstanding count read as evidence.
And the series is now ninety-eight classes long. Chapter 21.4 §20's class 97 was a property silenced by the fault; class 98 is a property silenced by the generator, and the two are the same shape with different agency. Together with Chapter 20.4 §20's class 91 — a metric the environment can improve by shrinking a denominator — they form a family about who controls a property's evaluation count, which is a ninth group and is a different axis entirely from Chapter 21.2 §26's eighth. Chapter 21.8 is where it will have to be settled, because a chapter about a link with no errors at all is a chapter about properties that never evaluate.
Continue learning
Related tutorials
- Related topic
Negotiation Failures and Duplex Mismatch
Seven ways a link comes up wrong and six of them report no error, because every device behaved correctly. Diagnosis is set narrowing over evidence, and three causes cannot be seen from one end at all.
- 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.
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.
