Ethernet · Module 21
Packet Drops
Five sites, one signature, and two ratios over counters that already exist take them to three classes — plus the finding that two of the four optional counters are nearly redundant.
Chapter 21.1 §4 found five fault sites with one signature between them and deferred the problem. This is the chapter that takes it.
| Site | Why the frame vanished | Is it an error? |
|---|---|---|
| the address filter | not addressed to this port | no — Chapter 7.4 |
| VLAN membership | the port is not in that VLAN | no — Chapter 13.2 |
| the receive FIFO | the consumer stalled | no — Chapter 19.5 §14 |
| the DMA path | no descriptor, or a buffer too small | no — Chapter 19.6 |
| the host driver | buffers not reaped | no |
Every one of the five is a legal discard, so no error counter moves, so the RMON set is silent — Chapter 21.2 §10 established exactly why: the standard counts errors and none of these is one. Chapter 21.3 §17 priced the four counters that would separate them at about 297 flops.
This chapter asks what can be done without them, and the answer is two ratios over counters every MAC already has.
| Value | |
|---|---|
| classes the RMON six separate the five sites into | 1 |
| classes two free ratios separate them into | 3 |
| largest class after the two ratios | 3 |
| hardware added | none |
| classes the four optional counters give | 5 |
| hardware they cost | ~297 flops |
And the first of the two ratios is the chapter's headline, because it separates two runs that are identical in every aggregate number.
| Run A | Run B | |
|---|---|---|
| frames received | 1 000 000 | 1 000 000 |
| frames delivered | 600 000 | 600 000 |
| total loss | 40.0% | 40.0% |
| mean received size | 645.6 octets | 645.6 |
| mean delivered size | 645.6 | 64.0 |
| selectivity index | 1.000 | 0.099 |
Every number a monitoring system collects is identical in the two columns except the last two, and the last two come from octet counters the RMON set already requires. Run A is a flat drop — a filter, a full ring, a stalled driver. Run B dropped every large frame and nothing else, which is one site of the five.
1. Scope, and Five Sites With One Signature
Scope: the frames that vanish without incrementing anything. Which of the five sites produced them, what free evidence separates which, and what the four optional counters buy that the free evidence cannot.
Not in scope: the drops that do increment something. Chapter 21.2 §12's thirteen classes cover those — a bad check value, an alignment error, a runt, a giant — and every one of them has a counter. Nor a mid-frame truncation: Chapter 19.5 §14 makes that a fragment, which is an error class, so it leaves class G by being visible.
Start from the exact statement of the problem, because the previous chapters have narrowed it precisely.
Chapter 21.1 §2's incidence table gives the five sites one row between them:
| Reading | Filter | VLAN | FIFO | DMA | Driver |
|---|---|---|---|---|---|
c_crc_errors | no | no | no | no | no |
c_alignment_errors | no | no | no | no | no |
c_undersize | no | no | no | no | no |
c_oversize | no | no | no | no | no |
frames_in | healthy | healthy | healthy | healthy | healthy |
frames_out | short | short | short | short | short |
Six readings, five sites, one signature — and Chapter 21.1 §6 measured what that costs: the RMON six separate twelve fault sites into seven classes, and this is the class that holds five of them.
Chapter 21.2 §10 explained why it is five and not one or zero.
| Needs | Decided by | |
|---|---|---|
| filtered | the address table | configuration |
| VLAN discard | port membership | configuration |
| buffer drop | FIFO occupancy | the system's timing |
| descriptor error | the host's ring state | software |
| driver | the host's scheduling | software |
None of the five is a property of the frame, so none of them is a predicate a receiver can evaluate from the octets it holds — and the RMON standard specifies counters for frame properties. The omission is principled and it is what produces the class.
Three things this chapter is and is not.
| Choice | |
|---|---|
| it uses only counters that already exist | the four optional ones are the comparison, not the method |
| it works on aggregate counters, not per-frame data | there is no per-frame data — the frames are gone |
| it stops at three classes | and says which three, and why not five |
Row two is the structural constraint and it is worth stating early. Every other chapter in Module 21 reasons about frames that arrived — Chapter 21.3 bins them by residue, Chapter 21.5 by size. These frames did not arrive anywhere, so the only evidence is what the aggregate counters say about the ones that did, and the whole of Sections 4 to 12 is what can be inferred from a difference between two populations.
2. Why the Standard Is Silent — the Four Legal Discards
The RMON counter set is not incomplete by oversight. It counts a specific thing and these five are not it.
An RMON error counter records a frame the receiver judged invalid. A class G drop is a frame the receiver judged valid and then did not deliver.
| Invalid | Valid, not delivered | |
|---|---|---|
| decided by | the octets | configuration or system state |
| Chapter 7.3 covers it | yes | no |
| a required counter exists | yes, six of them | no |
| the frame was | damaged | perfect |
Row four is the distinction that matters operationally. A frame dropped by the address filter was a correct frame that the port was correct to discard — Chapter 7.4's whole purpose. Counting it as an error would make every switch port on a shared segment report a permanent fault, which is why nobody does.
And the five divide into two groups by who decides.
| Group | Sites | Decided by | Changes when |
|---|---|---|---|
| configuration | filter, VLAN | a table somebody wrote | somebody changes it |
| system state | FIFO, DMA, driver | timing and memory pressure | continuously |
That split is the chapter's second free axis and Section 10 measures it. A configuration drop is steady — the same share of traffic, hour after hour, because the table does not change. A system-state drop is an event: a stall, a burst, a scheduling gap, and it clusters in time.
One more property of the five, because it decides what the free evidence can reach.
| Site | Does the drop depend on the frame's size? |
|---|---|
| the address filter | no — the decision is in the first six octets |
| VLAN membership | no — the decision is in the tag |
| the receive FIFO, whole-frame drop | no — the decision is at start-of-frame |
| the DMA, no descriptor posted | no — the ring is empty or it is not |
| the DMA, buffer smaller than the frame | YES — a hard threshold |
| the host driver | no |
One of the six mechanisms is size-selective and five are not, which bounds what Section 6's index can do before any measurement is taken: it names one site and rules out the rest. Whether that is worth having is Section 17's arithmetic, and the answer is yes, because it costs nothing and it names the site whose symptom is the most confusing.
3. RTL 1 — The Drop Package and the Class-G Mask
// ---------------------------------------------------------------------
// dropdiag_pkg -- five sites, six mechanisms, and the two free axes
// that separate them.
//
// The package's one claim: the evidence available is two RATIOS over
// counters that already exist, and neither of them is a drop counter.
// A drop counter for these sites is exactly what does not exist.
// ---------------------------------------------------------------------
package dropdiag_pkg;
// Chapter 21.1 Section 4's class G, and the DMA appears twice
// because it has two mechanisms with different signatures.
typedef enum logic [2:0] {
G_FILTER = 3'd0,
G_VLAN = 3'd1,
G_FIFO = 3'd2,
G_DMA_RING = 3'd3, // no descriptor posted
G_DMA_SIZE = 3'd4, // descriptor buffer smaller than the frame
G_DRIVER = 3'd5,
G_NONE = 3'd7
} gsite_e;
typedef logic [5:0] gmask_t;
localparam gmask_t ALL_G = 6'b11_1111;
// Section 2's two groups. Configuration drops are steady because a
// table does not change; system-state drops are events.
localparam gmask_t CONFIG_SITES = 6'b00_0011; // filter, VLAN
localparam gmask_t STATE_SITES = 6'b11_1100; // FIFO, ring, size, driver
// Section 2's size axis. Exactly one mechanism is size-selective.
localparam gmask_t SIZE_SELECTIVE = 6'b01_0000; // G_DMA_SIZE
localparam gmask_t SIZE_FLAT = 6'b10_1111;
// The selectivity index, in parts per thousand of 1.000. A flat
// drop reads 1000; a drop that removes only large frames reads far
// below it; one that removes only small frames reads above.
localparam int S_FLAT_PPT = 1000;
localparam int S_TOLERANCE_PPT = 30; // Section 6's sensitivity
function automatic int popcount6(gmask_t m);
int n = 0;
for (int i = 0; i < 6; i++) if (m[i]) n++;
return n;
endfunction
// Section 12: the three classes two free ratios produce.
localparam gmask_t CLASS_SELECTIVE = 6'b01_0000; // 1 site
localparam gmask_t CLASS_STEADY = 6'b00_0011; // 2 sites
localparam gmask_t CLASS_CLUSTERED = 6'b10_1100; // 3 sites
endpackageClassification: a package whose site list has six members for five sites, and the duplication is the point.
What it teaches: that the DMA path has two mechanisms with opposite signatures and treating it as one site loses the only free discrimination available. A ring with no posted descriptors drops every frame equally; a descriptor buffer smaller than the frame drops only the frames above its size. Same block, same counter, and one of the two is the single size-selective mechanism in class G.
And it teaches that the two free axes are orthogonal by construction. CONFIG_SITES against STATE_SITES splits by who decides — a table or the system's timing — and SIZE_SELECTIVE against SIZE_FLAT splits by the frame's length. Neither axis is derived from the other, so the two together give at most four classes and in practice give three, which Section 12 works out.
Deliberately simplified: S_TOLERANCE_PPT of 30 is a judgement against Section 6's sensitivity analysis, which showed a 10% large-frame loss shifts the index by 56 parts per thousand — so the tolerance admits anything below about a 5% selective loss. The site enum has no representation of two simultaneous mechanisms. And CLASS_CLUSTERED contains G_DMA_SIZE's sibling G_DMA_RING alongside the FIFO and the driver, which means the DMA path appears in two of the three classes — correct, and awkward to report.
Production implication: the awkwardness is worth keeping rather than smoothing away. A diagnosis that says "the DMA path, or the FIFO, or the driver" and separately "the DMA path" is telling an operator something true: the DMA is a candidate under both shapes, for two different reasons, and the two reasons have different fixes — post more descriptors, or make the buffers large enough for the MTU. Collapsing the enum to five sites would report one candidate where there are two questions.
// ---------------------------------------------------------------------
// mean_size_tracker -- the mean frame size on each side of the MAC,
// from counters the RMON set already requires.
//
// Received octets and received frames give the mean that arrived.
// Delivered octets and delivered frames give the mean that survived.
// Their ratio is Section 6's index, and no new counter is involved.
// ---------------------------------------------------------------------
module mean_size_tracker
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
// Receive side -- RMON requires both of these.
input logic [47:0] octets_in,
input logic [31:0] frames_in,
// Delivered to the host. Every DMA engine counts both.
input logic [47:0] octets_delivered,
input logic [31:0] frames_delivered,
output logic [15:0] mean_in,
output logic [15:0] mean_delivered,
output logic [31:0] frames_lost,
output logic [15:0] loss_ppt,
output logic both_sides_valid
);
always_comb begin
mean_in = (frames_in == 32'd0) ? 16'd0
: 16'(octets_in / 48'(frames_in));
mean_delivered = (frames_delivered == 32'd0) ? 16'd0
: 16'(octets_delivered / 48'(frames_delivered));
frames_lost = (frames_in > frames_delivered)
? (frames_in - frames_delivered) : 32'd0;
loss_ppt = (frames_in == 32'd0) ? 16'd0
: 16'((frames_lost * 32'd1000) / frames_in);
// Both means need a population. Below a thousand frames the means
// are dominated by whatever happened to arrive.
both_sides_valid = (frames_in > 32'd1000) && (frames_delivered > 32'd1000);
end
endmoduleClassification: four divisions over four counters, and every one of the four is already required.
What it teaches: that the octet counters are the instrument and nobody uses them for this. RMON requires octets alongside frames on the receive side, and every DMA engine counts bytes delivered because a driver needs them — so both means are computable today, on any platform, with no change at all. The reason nobody computes them is that a mean frame size looks like a traffic statistic rather than a diagnostic.
And it teaches that both_sides_valid is not a formality. A mean over a few hundred frames is dominated by whichever sizes happened to arrive, and the index Section 6 builds is a ratio of two such means — so the noise compounds. A thousand frames on each side is a floor rather than a target.
Deliberately simplified: the divisions are combinational over 48-bit octet counters, which is four wide dividers and belongs in software. There is no windowing: the means are since reset, so a fault that began an hour ago is diluted — the same gap Chapter 21.3 §3 flagged for its size cross. And octets_delivered is assumed to count the same octets as octets_in, which a DMA engine that strips the check value before delivery does not: a four-octet-per-frame bias that shifts the index by about 0.6% at this traffic mix.
Production implication: the check-value stripping bias is real and it is the one systematic error in the method. If the receive path delivers the frame without its four-octet check sequence — which most do — then mean_delivered is understated by exactly four octets. At a mean of 645.6 that is 0.62%, well inside Section 6's 3% tolerance and comfortably outside it on a link carrying only minimum-size frames, where four octets of sixty-four is 6.25%. The correction is one subtraction and it must be applied before the index is believed on small-frame traffic.
4. What the Size Histogram Actually Bins
The obvious instrument for a size-conditioned diagnosis is Chapter 19.7 §2's RMON histogram, and it does not do what this chapter needs.
| What RMON's histogram bins | |
|---|---|
| side | receive only |
| population | frames that arrived at the port |
| what it can compute | the size distribution of arrivals |
| what it cannot compute | the size distribution of deliveries |
A per-bucket delivery ratio would name every size-selective mechanism immediately — a step at the descriptor buffer size, a slope with occupancy, a flat line for a filter — and it requires a second histogram on the delivered side, which no standard requires and few implementations have.
| Counters | Flops | |
|---|---|---|
| a second, delivered-side size histogram | 7 | ~693 |
| Chapter 21.3 §17's four optional counters | 4 | ~297 |
The size-conditioned route costs more than twice the direct one and separates fewer sites, which is the honest answer to whether it is the way to attack class G. It is not. Chapter 21.1 §8's argument applies unchanged: the counters that observe a non-error are the ones that separate non-error drops, and there is no clever substitute.
What is available for nothing is a projection of the same information onto one number.
A histogram is a distribution. A mean is one moment of it. The delivered-side mean is free because the octet and frame counters already exist; the delivered-side histogram is not free because it does not.
| Delivered-side histogram | Delivered-side mean | |
|---|---|---|
| cost | ~693 flops | 0 — both counters exist |
| detects a step | yes, and locates it | yes, and does not locate it |
| detects a slope | yes | yes, weakly |
| distinguishes step from slope | yes | no |
| detects a flat drop | yes | yes — the index reads 1.000 |
Row four is what the free instrument gives up, and Section 8 argues it does not matter much: only one mechanism in class G is size-selective at all, so distinguishing a step from a slope separates nothing that is not already separated.
And there is one more thing the received-side histogram alone can do, which is worth noting because it is outside class G.
| Observation | Means |
|---|---|
| no frames arrive above some size | something upstream is not forwarding them |
| that is not one of the five sites | it is a path MTU problem, before us |
| the evidence | a truncated arrival distribution |
A hard absence of arrivals above a threshold is a path problem rather than a port problem, and it is visible in the received histogram with no second instrument. It is the one size-conditioned diagnosis the existing hardware fully supports, and it is about a site that is not ours.
5. RTL 2 — The Selectivity Index
// ---------------------------------------------------------------------
// size_selectivity_index -- one ratio, four existing counters, and it
// separates a flat drop from a size-selective one.
//
// S = mean(delivered) / mean(received). A drop that removes frames
// without regard to size leaves the mean unchanged and S reads 1.000.
// A drop that removes large frames pulls the mean down.
// ---------------------------------------------------------------------
module size_selectivity_index
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] mean_in,
input logic [15:0] mean_delivered,
input logic both_sides_valid,
input logic [15:0] loss_ppt,
input logic fcs_stripped_on_delivery,
output logic [15:0] s_index_ppt,
output logic s_valid,
output logic drop_is_flat,
output logic drops_large_frames,
output logic drops_small_frames,
output logic below_detection_floor
);
logic [15:0] mean_corrected;
always_comb begin
// Section 3: a receive path that strips the four-octet check value
// before delivery understates the delivered mean by exactly four.
// At a 645-octet mean that is 0.62%; on minimum-size traffic it is
// 6.25% and it dominates the measurement.
mean_corrected = fcs_stripped_on_delivery ? (mean_delivered + 16'd4)
: mean_delivered;
s_index_ppt = (mean_in == 16'd0) ? 16'd0
: 16'((32'(mean_corrected) * 32'd1000) / 32'(mean_in));
s_valid = both_sides_valid;
drop_is_flat = s_valid &&
(s_index_ppt > (16'(S_FLAT_PPT) - 16'(S_TOLERANCE_PPT))) &&
(s_index_ppt < (16'(S_FLAT_PPT) + 16'(S_TOLERANCE_PPT)));
drops_large_frames = s_valid &&
(s_index_ppt <= (16'(S_FLAT_PPT) - 16'(S_TOLERANCE_PPT)));
drops_small_frames = s_valid &&
(s_index_ppt >= (16'(S_FLAT_PPT) + 16'(S_TOLERANCE_PPT)));
// Section 6: the index moves 5.63% for a 10% large-frame-only
// loss. Below about a 4% total loss the shift is inside the
// tolerance and the instrument says nothing.
below_detection_floor = s_valid && (loss_ppt < 16'd40);
end
endmoduleClassification: a single division whose value is entirely in what its two operands are.
What it teaches: that a flat drop leaves the mean exactly unchanged, and that is an identity rather than an approximation. Removing a random 40% of frames removes 40% of the octets too, so both the numerator and the denominator of the delivered mean fall by the same factor and the mean is untouched. The index reads 1.000 and it reads it precisely — which makes any departure from 1.000 evidence rather than noise.
And it teaches that the check-value correction is not cosmetic. Four octets of 645.6 is 0.62% and four octets of 64 is 6.25% — more than twice the tolerance. On a link carrying control traffic, an uncorrected index reads 0.938 and drops_large_frames asserts on a perfectly healthy link. The correction is one addition and the flag that says whether to apply it is a property of the DMA engine.
Deliberately simplified: S_TOLERANCE_PPT of 30 is a judgement, and the right value depends on the traffic's size variance — a link carrying one frame size has an index that is exactly 1.000 or exactly the ratio of two sizes, with nothing in between. The index is computed since reset with the same windowing gap as Section 3. And drops_small_frames is implemented and has no member in class G: no mechanism in the five drops small frames preferentially, so an assertion of it means the model is wrong for this platform.
Production implication: drops_small_frames firing is the most informative thing this block can do and it is not in the fault space. Nothing in class G removes small frames preferentially — a filter is size-blind, a ring is size-blind, a buffer-size mismatch removes the large ones. So an index above 1.030 is a fault outside the five sites: a policer configured on packet rate, a queue with a minimum-length discipline, or an upstream device doing something nobody documented. It is the chapter's contradiction, and it is worth a distinct message rather than being folded into "not flat".
6. The Size-Selectivity Index, and Two Runs With the Same Loss
Take a classic bimodal traffic mix — 60% minimum-size frames and 40% maximum-size ones — and drop 40% of the frames two different ways.
| Mean received size | |
|---|---|
| 0.60 × 64 + 0.40 × 1 518 | 645.6 octets |
Run A drops 40% of frames at random. Run B drops every maximum-size frame and nothing else.
| Run A — flat | Run B — selective | |
|---|---|---|
| frames delivered | 600 000 of 1 000 000 | 600 000 of 1 000 000 |
| total loss | 40.0% | 40.0% |
| mean delivered size | 645.6 | 64.0 |
| selectivity index | 1.000 | 0.099 |
Identical in every aggregate figure a monitoring system collects, and different by a factor of ten in one ratio it does not.
The index across the whole range of selectivity:
| What is dropped | Total loss | Index |
|---|---|---|
| 40% at random | 40.0% | 1.000 |
| 10% of the large frames only | 4.0% | 0.944 |
| 25% of the large only | 10.0% | 0.850 |
| 50% of the large only | 20.0% | 0.662 |
| all of the large | 40.0% | 0.099 |
| all of the small | 60.0% | 2.351 |
The index is monotone in selectivity and independent of the loss rate, which is the property that makes it usable: run A's 40% loss and a 4% loss that is entirely large frames are distinguishable, and the distinguishing number is not the loss.
Now the sensitivity, because an instrument is only as good as the smallest effect it sees.
| Large-frame-only loss | Total loss | Index | Shift from 1.000 |
|---|---|---|---|
| 2% | 0.80% | 0.989 | 1.1% |
| 5% | 2.00% | 0.972 | 2.8% |
| 10% | 4.00% | 0.944 | 5.6% |
| 20% | 8.00% | 0.883 | 11.8% |
With a 3% tolerance the instrument detects a selective loss from about 5% of large frames upward — a 2% total loss — and says nothing below that. That floor is the honest limit and it is not tight: 2% total loss is well below most alarm thresholds, so the index reaches faults that nothing else in the free instrument set reaches at all.
Two properties of the index worth stating as rules.
A flat drop reads exactly 1.000, whatever the loss rate. The identity is exact because removing a random share of frames removes the same share of octets.
The index is a shape measurement and the loss is a magnitude one. Neither implies the other, and reporting only the second is why class G has stayed fused.
And one caveat that decides whether the index works on a given link.
| Traffic | Index behaviour |
|---|---|
| a broad size mix | works — the mean has room to move |
| two sizes, well separated | works best |
| one frame size only | the index is exactly 1.000 or undefined |
| all minimum-size | plus the check-value bias, which dominates |
Row three is the blind case and it is the same shape as Chapter 21.5 §6's structural zero: a link carrying one frame size cannot have a size-selective drop distinguished from a flat one, because every frame is the same size and selecting by size selects everything or nothing. The instrument is blind and both_sides_valid does not say so — Section 16's monitor does.
One more property of the index that decides how it should be reported, because it is not symmetric.
The index's distance from 1.000 is not proportional to the selectivity. It is a ratio of means, so it is compressed above 1.000 and stretched below it:
| What is dropped | Index | Distance from 1.000 |
|---|---|---|
| all large frames | 0.099 | 0.901 |
| all small frames | 2.351 | 1.351 |
| half the large | 0.662 | 0.338 |
| half the small | 1.386 | 0.386 |
Removing all the small frames moves the index further than removing all the large ones, even though the second removes far more octets — because the surviving mean is bounded below by the smallest frame present and bounded above only by the largest. The index is compressed below 1.000 and stretched above it, so the two directions are not on the same scale at large effects.
At the detection floor, though, they very nearly are.
| Selectivity | 5% of that population dropped | Distance |
|---|---|---|
| large frames | index 0.972 | 0.028 |
| small frames | index 1.028 | 0.028 |
So a symmetric tolerance of 30 parts per thousand gives essentially the same floor in both directions — about five per cent of either population — and the asymmetry only matters for reporting large effects, where the number below 1.000 understates how selective the drop was. A report that quotes the index should quote the mean sizes beside it, because 0.099 and 64 octets against 645.6 are the same fact and only the second is legible.
7. RTL 3 — The Drop-Shape Classifier
// ---------------------------------------------------------------------
// drop_shape_classifier -- turn the two free ratios into a mask over
// Section 3's six mechanisms.
//
// Two axes, four quadrants, three of them occupied. The fourth --
// selective AND clustered -- has no member in class G, which makes it
// this chapter's contradiction rather than a fourth class.
// ---------------------------------------------------------------------
module drop_shape_classifier
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic s_valid,
input logic drop_is_flat,
input logic drops_large_frames,
input logic drops_small_frames,
input logic clustering_valid,
input logic drop_is_clustered,
input logic below_detection_floor,
output gmask_t candidates,
output logic [2:0] n_candidates,
output logic classified,
output logic outside_class_g,
output logic instrument_blind
);
gmask_t by_size, by_time;
always_comb begin
// Axis one: the selectivity index.
if (!s_valid || below_detection_floor) by_size = ALL_G;
else if (drops_large_frames) by_size = SIZE_SELECTIVE;
else if (drop_is_flat) by_size = SIZE_FLAT;
else by_size = 6'd0; // small-selective
// Axis two: steady or clustered. Section 2's configuration group
// is steady because a table does not change; the system-state
// group is an event.
if (!clustering_valid) by_time = ALL_G;
else if (drop_is_clustered) by_time = STATE_SITES;
else by_time = CONFIG_SITES;
candidates = by_size & by_time;
n_candidates = 3'(popcount6(candidates));
// A size-selective drop is G_DMA_SIZE, which is in STATE_SITES --
// but the buffer size does not change, so it is STEADY. The
// intersection is empty unless the time axis agrees, and when it
// does not, the model is wrong.
classified = s_valid && clustering_valid && (n_candidates > 3'd0);
// Section 5: nothing in class G removes small frames.
outside_class_g = s_valid && drops_small_frames;
instrument_blind = !s_valid || below_detection_floor || !clustering_valid;
end
endmoduleClassification: a two-axis intersection over six mechanisms, and one of its four quadrants is empty by construction.
What it teaches: that outside_class_g is a real terminal state and it is the block's most informative output. No mechanism among the five sites removes small frames preferentially — a filter is size-blind, a ring is size-blind, and a buffer-size mismatch removes the large ones — so an index above 1.030 means the fault is not one of the five. It is a policer, a rate-limiting discipline, or an upstream device, and saying so is worth more than naming a candidate.
And it teaches that G_DMA_SIZE sits awkwardly across the two axes, which is correct. It is a system-state site by ownership and a steady fault by behaviour — a descriptor buffer's size does not change from second to second — so the two axes disagree about it, and the intersection has to be read with that in mind. Section 12's table resolves it explicitly rather than letting the mask do it silently.
Deliberately simplified: by_size returns the empty mask on a small-selective reading rather than a distinct value, so outside_class_g is the only way to tell an empty intersection from a contradiction. The two axes are ANDed with no weighting, where the size axis is far more reliable than the time axis. And instrument_blind folds three different blindnesses into one bit — too few frames, too little loss, too little time — with three different responses.
Production implication: the three blindnesses deserve separating because their fixes differ completely. Too few frames is solved by waiting; too little loss is solved by nothing — the fault is genuinely small; too little time is solved by the clustering window, which must be free-running. A single flag sends an operator to wait in all three cases, and in the second case waiting is exactly wrong: a 1% selective loss is invisible to this instrument at any duration, and the right response is the four optional counters.
8. Which of the Five a Selective Drop Names
Section 2's table has one size-selective mechanism. This section says precisely which and why the others are not.
| Mechanism | Where the decision is made | Size-selective? |
|---|---|---|
| the address filter | the destination address, octets 0 to 5 | no |
| VLAN membership | the tag, octets 12 to 15 | no |
| the FIFO, whole-frame drop | occupancy at start-of-frame | no |
| the DMA, ring empty | the descriptor ring's state | no |
| the DMA, buffer too small | the frame's length against the buffer's | YES |
| the driver | the host's scheduling | no |
Every one of the five "no" rows makes its decision from information available before the frame's length is known — the first six octets, the tag, the FIFO's state at the first beat, the ring's state. A decision taken at the start of a frame cannot depend on how long the frame turns out to be.
The one "yes" row is the only mechanism that compares a length against something.
A descriptor buffer of
Boctets accepts a frame ofLoctets if and only ifL ≤ B, assuming the driver does not chain descriptors. That is a step function in the frame's length and nothing else in class G has one.
And the step's location is diagnostic on its own.
| Buffer size | Frames dropped | Typical cause |
|---|---|---|
| 2 048 octets | jumbo frames only | an MTU raised without resizing buffers |
| 1 536 octets | nothing on a standard MTU | correct |
| 1 518 octets | VLAN-tagged frames at 1 522 | Chapter 21.2 §18's four octets |
| 256 octets | most traffic | a misconfigured small-buffer pool |
Row three is the case worth expecting. Chapter 21.2 §18 showed the MTU boundary moves from 1 518 to 1 522 with one VLAN tag and 1 526 with two — and a descriptor buffer sized for an untagged MTU drops exactly the tagged frames, silently, while every error counter stays at zero. The selectivity index sees it and the four-octet difference is invisible to everything else.
Now what the index does not do, stated precisely.
| The index | |
|---|---|
names G_DMA_SIZE when it fires | yes |
| locates the step | no — that needs the delivered-side histogram |
| separates the other five from each other | no |
| rules the other five out when it fires | yes |
Row two is the cost of projecting a distribution onto a mean. The index says "large frames are being removed"; it cannot say at what size the removal begins, and knowing the threshold is what identifies the buffer. The workaround is a bisection with the traffic: send frames at 1 518 and at 1 500 and compare — which is an experiment rather than an observation, and the chapter's only one.
Row four is why the index is worth having despite row two. Firing eliminates five of the six mechanisms, which is 2.58 bits from four counters that already exist — better than any single observation in Chapter 21.1 §6's table except the partner's status in Chapter 21.4.
And there is a cheap experiment that recovers row two, which is worth spelling out because it is the chapter's only active step.
The index says large frames are being removed and does not say above what size. A bisection with the traffic finds the threshold in a handful of steps:
| Step | Send | If delivered | If dropped |
|---|---|---|---|
| 1 | 1 518 octets | the threshold is above 1 518 | continue |
| 2 | 1 024 | between 1 024 and 1 518 | continue |
| 3 | 512 | between 512 and 1 024 | continue |
| 4 | 256 | between 256 and 512 | below 256 — a small-buffer pool |
Four frames locate the threshold to within a factor of two, and the threshold is what identifies the buffer: 2 048 means an MTU raised without resizing; 1 518 means Chapter 21.2 §18's four octets of VLAN tag; 256 means a pool nobody meant to use.
| Cost | |
|---|---|
| the bisection | four frames and the ability to send them |
| a delivered-side size histogram | ~693 flops, permanently |
| what each gives | the same threshold |
Four frames against 693 flops is the trade, and the only thing the histogram has over the bisection is that it works retrospectively, on a fault that has already stopped. That is a real advantage and it is not worth 693 flops, because a descriptor buffer's size does not change and the fault does not stop by itself.
9. RTL 4 — The Drop Clustering Probe
// ---------------------------------------------------------------------
// drop_clustering_probe -- the second free axis: is the loss steady or
// is it an event?
//
// Section 2's split. A configuration drop is a fixed share of traffic,
// hour after hour, because a table does not change. A system-state
// drop happens when something stalls, and a stall has a beginning and
// an end.
// ---------------------------------------------------------------------
module drop_clustering_probe
import dropdiag_pkg::*;
#(
parameter int WINDOW_FRAMES = 4096,
parameter int N_WINDOWS = 64
)(
input logic clk,
input logic rst_n,
input logic frame_in,
input logic frame_delivered,
output logic [15:0] c_windows,
output logic [15:0] c_windows_with_loss,
output logic [15:0] max_loss_in_window_ppt,
output logic [15:0] mean_loss_ppt,
output logic drop_is_clustered,
output logic clustering_valid
);
logic [15:0] in_win, lost_win;
logic [31:0] total_in, total_lost;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
in_win <= '0; lost_win <= '0;
c_windows <= '0; c_windows_with_loss <= '0;
max_loss_in_window_ppt <= '0;
total_in <= '0; total_lost <= '0;
end else begin
if (frame_in) begin
in_win <= in_win + 16'd1;
total_in <= total_in + 32'd1;
if (!frame_delivered) begin
lost_win <= lost_win + 16'd1;
total_lost <= total_lost + 32'd1;
end
end
if (in_win == 16'(WINDOW_FRAMES) - 16'd1) begin
c_windows <= c_windows + 16'd1;
if (lost_win != 16'd0)
c_windows_with_loss <= c_windows_with_loss + 16'd1;
if (((lost_win * 16'd1000) / 16'(WINDOW_FRAMES)) > max_loss_in_window_ppt)
max_loss_in_window_ppt <= (lost_win * 16'd1000) / 16'(WINDOW_FRAMES);
in_win <= '0; lost_win <= '0;
end
end
end
always_comb begin
mean_loss_ppt = (total_in == 32'd0) ? 16'd0
: 16'((total_lost * 32'd1000) / total_in);
// A steady drop puts its share in EVERY window. A clustered one
// concentrates it: the worst window is far above the mean and
// many windows have none at all.
drop_is_clustered = (c_windows > 16'd8) && (mean_loss_ppt > 16'd0) &&
(max_loss_in_window_ppt > (mean_loss_ppt * 16'd4)) &&
(c_windows_with_loss < ((c_windows * 16'd3) / 16'd4));
clustering_valid = (c_windows >= 16'(N_WINDOWS));
end
endmoduleClassification: a temporal variance test, and it is Chapter 21.3 §9's clustering probe applied to drops instead of to errors.
What it teaches: that the two conditions in drop_is_clustered are checking different things and both are needed. The worst window being four times the mean says the loss has a peak; fewer than three quarters of windows having any loss says it has gaps. A steady drop at 5% puts about 5% in every window — a peak and no gaps is a burst, gaps and no peak is a low-rate steady drop measured too finely, and only both together is an event.
And it teaches why this axis separates exactly what Section 2's grouping predicted. A filter's drop rate is the foreign-traffic share of the segment, which changes over minutes and hours rather than milliseconds; a FIFO overflow is a consumer stall, which Chapter 20.5 §12 proved no frame can cause and which therefore arrives as an external event with a duration.
Deliberately simplified: the window is a fixed number of frames rather than of time, so on a link whose rate varies the windows have varying durations — and a stall correlates with time. frame_delivered is assumed to be available per frame, which requires the delivery decision to be traceable back to the frame; on a real DMA path the delivery is asynchronous and the pairing is exactly Chapter 20.6's join problem. And the factor of four is a judgement.
Production implication: the per-frame pairing is the practical obstacle and there is a cheaper approximation that works. Comparing frames_in and frames_delivered deltas per window needs no pairing at all — two counters read at each window boundary — and it is exactly as good for this test, because the test is about the loss's variance rather than about which frame was lost. The pairing version is what a testbench builds; the delta version is what a monitoring system can actually collect, and they answer the same question.
10. Steady Against Clustered: the Second Free Axis
Section 2 split the five sites by who decides. This section is what that split looks like in a counter over time.
| Site | What sets the drop rate | How fast it changes |
|---|---|---|
| the address filter | the foreign-traffic share of the segment | minutes to hours |
| VLAN membership | the non-member traffic share | minutes to hours |
| the receive FIFO | whether the consumer is stalled | microseconds — an event |
| the DMA, ring empty | whether the driver has posted | milliseconds — an event |
| the DMA, buffer too small | the buffer's size against the traffic's | never — it is a constant |
| the driver | the host's scheduling | milliseconds — an event |
Three rates of change and they map onto the axis directly. Rows one and two are steady at any window a diagnosis uses; rows three, four and six are events; row five does not change at all, which makes it steady too — and that is the awkwardness Section 7's classifier has to carry.
So the time axis alone gives:
| Reading | Candidates |
|---|---|
| steady | filter, VLAN, and the descriptor-buffer size — 3 |
| clustered | FIFO, empty ring, driver — 3 |
Three and three, which is 1.000 bits — a perfectly balanced question, and it is the only such question in this chapter. Chapter 21.4 §6 found the same thing about its partner's-status reading, and for the same structural reason: a question that splits a space evenly is worth exactly one bit, and most questions do not.
And the two axes together are what Section 12 counts.
| Size axis | Time axis | |
|---|---|---|
| what it splits on | the frame's length | the drop's variance in time |
| cost | 0 — four existing counters | 0 — two existing counters, per window |
| bits | 2.58 when it fires, 0.22 when it does not | 1.000 |
| blind when | one frame size, or loss below 2% | the loss is very low, or the window is wrong |
Row three's asymmetry is worth explaining. The size axis is lopsided: it separates one mechanism from five. Firing is worth log₂ 6 = 2.58 bits — it names a single site out of six — and not firing is worth only 0.22, because five of six were already the likely answer. The time axis is balanced and worth one bit either way.
The size axis is a rare, decisive question. The time axis is a common, moderate one. Together they are worth about 1.58 bits on the flat branch and 2.58 on the selective one, which is why the flat branch keeps three candidates and the selective branch keeps one.
One caution about the time axis that the size axis does not have.
| Problem | |
|---|---|
| a filter drop looks clustered | if the foreign traffic is itself bursty |
| a FIFO drop looks steady | if the consumer stalls regularly and often |
| the axis measures the traffic | as much as the fault |
Row three is the honest limit and it is the same shape as Chapter 21.5's load dependence: the instrument reads a property of the environment as well as of the fault, and on a link whose foreign traffic arrives in bursts the two branches blur. The size axis has no such problem — a frame's length is a property of the frame — which is why Section 7's classifier weights it first in practice even though the code ANDs them.
There is a third free axis worth naming and rejecting, because it is the one people reach for.
The rate of the drop — what fraction of traffic vanishes — looks like it should discriminate. It does not:
| Site | Typical drop rate | Bounded? |
|---|---|---|
| the address filter | anything from 0% to 99% | by the segment's traffic |
| VLAN membership | anything from 0% to 99% | by the VLAN mix |
| the receive FIFO | anything | by the stall's duration |
| the DMA, ring empty | anything | by the driver |
| the DMA, buffer too small | the large-frame share | by the traffic's size mix |
| the driver | anything | by the host |
Every row's range overlaps every other row's, so the magnitude separates nothing — which is why Sections 6 and 10 are both about shape. A 90% drop rate is consistent with a filter on a busy shared segment and with a driver that stopped, and a 0.5% rate is consistent with both too.
Magnitude separates nothing in class G. Shape separates it into three. That is the chapter's whole method, and it is why the loss figure — the one number a monitoring system does collect — is the one that has never helped.
11. RTL 5 — The Optional-Counter Gate
// ---------------------------------------------------------------------
// optional_counter_gate -- decide whether this platform needs the free
// method at all, and say what it is giving up by using it.
//
// Chapter 21.3 Section 17 priced the four optional counters at about
// 297 flops. If they exist, use them: they give five classes where
// this chapter's free ratios give three. This block is the switch.
// ---------------------------------------------------------------------
module optional_counter_gate
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
// Chapter 21.1 Section 11's probe result, per counter.
input logic [3:0] optional_present, // filtered, vlan, fifo, desc
input logic [3:0] optional_proven,
input gmask_t free_candidates,
input logic [31:0] c_filtered,
input logic [31:0] c_vlan_discards,
input logic [31:0] c_fifo_drops,
input logic [31:0] c_desc_errors,
output gmask_t best_candidates,
output logic used_optional,
output logic [2:0] ceiling,
output logic free_method_only,
output logic probe_required
);
gmask_t direct;
always_comb begin
// With the optional counters, the answer is whichever one moved.
direct = ALL_G;
if (optional_present[0] && optional_proven[0])
direct = (c_filtered != 32'd0) ? (6'd1 << G_FILTER) : (direct & ~(6'd1 << G_FILTER));
if (optional_present[1] && optional_proven[1])
direct = (c_vlan_discards != 32'd0) ? (6'd1 << G_VLAN) : (direct & ~(6'd1 << G_VLAN));
if (optional_present[2] && optional_proven[2])
direct = (c_fifo_drops != 32'd0) ? (6'd1 << G_FIFO) : (direct & ~(6'd1 << G_FIFO));
if (optional_present[3] && optional_proven[3])
direct = (c_desc_errors != 32'd0) ? (6'd1 << G_DMA_RING) : (direct & ~(6'd1 << G_DMA_RING));
used_optional = (optional_present != 4'd0);
best_candidates = used_optional ? (direct & free_candidates) : free_candidates;
// Section 17's arithmetic, as a number an operator can read.
ceiling = (optional_present == 4'hF) ? 3'd1 : 3'd3;
free_method_only = (optional_present == 4'd0);
// Chapter 21.1 Section 10: an unimplemented counter reads zero,
// which is indistinguishable from a real zero. The probe is a
// precondition, not a refinement.
probe_required = (optional_present != 4'd0) &&
(optional_proven != optional_present);
end
endmoduleClassification: a selector, and its most useful behaviour is to intersect rather than to replace.
What it teaches: that the two methods are complementary rather than alternative. The optional counters say which site fired; the free ratios say what shape the loss has — and intersecting them catches a case neither catches alone: c_desc_errors moving and the index reading 0.94 is a descriptor problem of the size kind, which the counter alone does not distinguish from an empty ring. Section 3's two DMA mechanisms share one counter and the free axis splits them.
And it teaches that probe_required is inherited wholesale from Chapter 21.1 §10 and must not be skipped. An unimplemented optional counter reads zero exactly like an implemented one at rest, and reading a zero as "this site is exonerated" eliminates the site the fault is at. The probe is four thousand frames, once per platform, and it is the precondition for every use of direct in this block.
Deliberately simplified: direct sets a one-hot mask on the first counter that moved rather than accumulating, so two simultaneous mechanisms report the last one tested. The four if statements are written as a sequence with an assignment that later statements can override — which is a bug in the style of Chapter 21.2 §7's priority chain, left in deliberately for the same reason. And ceiling returns 1 or 3 with nothing in between, where a platform with two of the four counters sits at two.
Production implication: the intersection is the design decision worth defending in review. A team that has the optional counters will be tempted to skip the free ratios entirely — the counters are direct evidence and the ratios are inference — and the case they lose is the DMA's two mechanisms, which share one counter. c_desc_errors firing tells you the DMA path dropped frames; the index tells you whether the buffers were too small or the ring was empty, and those are two different bugs in two different pieces of software. Two lines of arithmetic, and no hardware.
12. Three Classes of Five, for Nothing
Run both axes over Section 3's six mechanisms and count the distinct signatures.
| Mechanism | Size axis | Time axis |
|---|---|---|
| the address filter | flat | steady |
| VLAN membership | flat | steady |
| the receive FIFO | flat | clustered |
| the DMA, ring empty | flat | clustered |
| the DMA, buffer too small | SELECTIVE | steady |
| the host driver | flat | clustered |
Three distinct signatures:
| Class | Signature | Members |
|---|---|---|
| A | selective, steady | the descriptor buffer size — 1 |
| B | flat, steady | the filter, VLAN membership — 2 |
| C | flat, clustered | the FIFO, the empty ring, the driver — 3 |
Six mechanisms, five sites, three classes, largest three — and the cost is zero.
Against what Chapter 21.1 §4 started with:
| Instrument set | Classes | Largest class | Flops |
|---|---|---|---|
| the RMON six alone | 1 | 5 | 0 |
| plus the two free ratios | 3 | 3 | 0 |
| plus the four optional counters | 5 | 1 | ~297 |
| plus a delivered-side histogram | 5 | 1 | ~990 |
Row two is this chapter's result and row four is the instructive one. A delivered-side size histogram — the obvious instrument for a size-conditioned diagnosis — costs 693 flops more than the optional counters and separates nothing further, because the sites it would separate are separated already. The size-conditioned route is not the cheap way to attack class G and it is worth saying so plainly.
What the free ratios are good for is a platform that has none of the optional counters, which is most of them.
| Without the free ratios | With them | |
|---|---|---|
| candidates | 5 | 1, 2 or 3 |
| expected candidates, uniform prior | 5 | 2.33 |
| what the operator does next | check all five | check two or three |
| hardware | — | none |
Row two is the honest average: (1 + 2 + 3) / 3 = 2.00 classes weighted by class, or 2.33 weighted by site — a better than twofold reduction from four counters, two divisions and one variance test, all over registers that are already there.
And the one thing the free method does that the optional counters do not:
It splits the DMA path's two mechanisms, which share one counter.
c_desc_errorssays the DMA dropped frames; the selectivity index says whether the buffers were too small or the ring was empty.
Which is why Section 11's gate intersects rather than replaces, and why a platform with all four optional counters should still compute the index: five classes become six, and the sixth is the one that distinguishes two bugs in two different pieces of software.
And one accounting of the whole module's class-G problem, because four chapters have now touched it.
| Chapter | What it contributed |
|---|---|
| Chapter 21.1 §4 | found the class and measured it: 5 sites, 1 signature |
| Chapter 21.2 §10 | explained it: four of the five are legal discards |
| Chapter 21.3 §17 | priced the direct fix: four counters, ~297 flops |
| this chapter | found three classes for free, and halved the bill |
Four chapters, one class, and the useful result is the last row's second half. Chapter 21.3 §17's four counters were priced as a block; this chapter shows that two of them do work the free ratios already do, so the marginal value of c_filtered and c_vlan_discards after the time axis is one split between two configuration tables on the same switch.
| Flops | Marginal classes | |
|---|---|---|
| the free ratios | 0 | 1 → 3 |
c_fifo_drops + c_desc_errors | ~198 | 3 → 5 |
c_filtered + c_vlan_discards | ~99 | 5 → 5, largest 2 → 1 |
Row three buys a smaller largest class and no new class at all, which is a real improvement and the least valuable of the three purchases — and it is the one a team reading Chapter 21.1 §19's list alphabetically would buy first.
13. RTL 6 — The Reason-Field Absence
// ---------------------------------------------------------------------
// reason_field_absence -- make explicit what the design does NOT carry.
//
// A frame is dropped. Five sites could have dropped it. The design's
// representation of which one is: nothing. There is no reason field,
// no tag, no per-drop record. This block exists to say so in a place a
// reviewer will read, and to compute what the absence costs in bits.
// ---------------------------------------------------------------------
module reason_field_absence
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic frame_dropped,
// The ONLY thing the design carries about a class G drop.
input logic drop_is_silent,
// What a reason field would have carried, if one existed. These are
// inputs from a TESTBENCH model, and that is the whole difficulty.
input gsite_e model_reason,
input logic model_valid,
output logic [31:0] c_drops,
output logic [2:0] reason_bits_carried,
output logic [2:0] reason_bits_needed,
output logic reason_is_modelled,
output logic model_is_the_only_source
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_drops <= '0;
else if (frame_dropped && drop_is_silent) c_drops <= c_drops + 32'd1;
end
always_comb begin
// The design carries one bit: it happened. Six mechanisms need
// ceil(log2 6) = 3 bits to name. The gap is the chapter.
reason_bits_carried = 3'd0;
reason_bits_needed = 3'd3;
reason_is_modelled = model_valid;
// Section 20's rejected class: if the reason exists only in the
// testbench, a property quantified over reasons checks the model.
// If it is added to the design, the property checks the design's
// own label and is a tautology.
model_is_the_only_source = model_valid && (reason_bits_carried == 3'd0);
end
endmoduleClassification: a block whose outputs are two constants and a flag, and whose purpose is to make an absence reviewable.
What it teaches: that the design carries zero bits about why a frame was dropped and naming the mechanism needs three. Six mechanisms is ceil(log₂ 6) = 3 bits; the receive path has a drop signal and no companion field. Every inference in this chapter — the selectivity index, the clustering test, the optional counters — exists to reconstruct those three bits from side effects, and none of them reconstructs all three.
And it teaches that model_is_the_only_source is the flag Section 20's rejected property turns on. When the reason exists only in a testbench model, a property quantified over reasons is checking the model; when it is added to the design, the property is checking the design's own label. There is no third option and the block makes which one you are in a signal rather than an assumption.
Deliberately simplified: reason_bits_carried is a hardcoded zero, which is true of the MACs this track has built and not of every MAC — some carry a drop-reason field and the block should read it rather than assume its absence. model_reason is an input of a type the design has no producer for, which is the point and is also why this block is unsynthesisable in any useful sense. And c_drops counts only silent drops, which requires drop_is_silent to be computed somewhere, which requires knowing the reason.
Production implication: the last simplification is not a flaw in the block; it is the problem restated. drop_is_silent means "no error counter moved for this frame", which is a statement about five counters at the moment the frame was dropped — and a MAC that could compute it could equally well have recorded the reason. The circularity is real and it is the argument for the three bits: a three-bit reason field on the drop path, about 3 flops plus a small encoder, replaces this entire chapter's inference with a read. Chapter 21.3 §19's rule applies exactly — log what has a frame — and a drop has one.
14. What a Drop Diagnosis Must Never Do
Six prohibitions. Two are about the free ratios, two about the optional counters, and two about what a silent drop is not.
| Never | Because | |
|---|---|---|
| 1 | read a zero optional counter as exonerating | Chapter 21.1 §10 — it may not exist |
| 2 | compute the index without the check-value correction | Section 5 — 6.25% bias on minimum-size traffic |
| 3 | use the index on single-size traffic | Section 6 — the mean cannot move |
| 4 | treat the DMA as one mechanism | Section 8 — two mechanisms, opposite signatures |
| 5 | conclude a site from the time axis alone | Section 10 — it measures the traffic too |
| 6 | call a silent drop an error | Section 2 — four of the five are legal |
Row two is the prohibition that produces a false positive on the commonest traffic there is. A receive path that strips the four-octet check value before delivery understates the delivered mean by exactly four octets — 0.62% at a mixed-traffic mean of 645.6 and 6.25% on a link carrying only 64-octet frames. Uncorrected, the index reads 0.938 on a perfectly healthy control-traffic link and drops_large_frames asserts. One addition, and the flag that says whether to apply it is a property of the DMA engine rather than of the link.
Row six is the prohibition with the operational cost. A port on a shared segment filters most of what it hears, and that is Chapter 7.4 working exactly as designed. Chapter 21.2 §15's discards_exceed_errors made the same point: a link discarding sixteen times more than it errors is configured that way. Reporting class G drops in an "errors" figure makes a healthy busy port look like a failing one.
And the two that look like precision and are not:
| Why it is a prohibition | |
|---|---|
| row three | on single-size traffic the index is 1.000 by construction, not by evidence |
| row five | bursty foreign traffic makes a filter drop look clustered |
Both produce a reading that is a property of the traffic — which is what this chapter shares with Chapter 21.5 and what makes the two of them a pair: in both, the fault's visibility is set by what the network happens to be carrying.
15. RTL 7 — Drop Telemetry
// ---------------------------------------------------------------------
// dropdiag_telemetry -- three groups: the shape, the candidates, and
// what the platform's instrumentation ceiling is.
//
// The ceiling is knowable at step zero, exactly as in Chapter 21.1
// Section 15 and Chapter 21.4 Section 15. Three chapters, one pattern.
// ---------------------------------------------------------------------
module dropdiag_telemetry
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] s_index_ppt,
input logic s_valid,
input logic drop_is_clustered,
input logic clustering_valid,
input gmask_t best_candidates,
input logic [15:0] loss_ppt,
input logic [3:0] optional_present,
input logic instrument_blind,
input logic outside_class_g,
// Shape.
output logic [15:0] selectivity_ppt,
output logic [1:0] shape, // 0 unknown 1 flat-steady 2 flat-clustered 3 selective
output logic [15:0] loss_reported_ppt,
// Candidates.
output logic [2:0] n_candidates,
output logic single_site,
output logic not_in_class_g,
// Ceiling.
output logic [2:0] best_possible,
output logic free_method_ceiling,
output logic blind
);
assign selectivity_ppt = s_index_ppt;
assign loss_reported_ppt = loss_ppt;
assign n_candidates = 3'(popcount6(best_candidates));
assign single_site = (n_candidates == 3'd1);
assign not_in_class_g = outside_class_g;
assign blind = instrument_blind;
always_comb begin
if (!s_valid || !clustering_valid) shape = 2'd0;
else if (s_index_ppt < 16'd970) shape = 2'd3;
else if (drop_is_clustered) shape = 2'd2;
else shape = 2'd1;
// Section 12's table, as arithmetic. Four optional counters give a
// ceiling of one; the free ratios alone give three.
best_possible = (optional_present == 4'hF) ? 3'd1 : 3'd3;
free_method_ceiling = (optional_present != 4'hF);
end
endmoduleClassification: a reporting block whose third group is a property of the platform and not of the fault.
What it teaches: that shape is the output a report should lead with, ahead of the candidate list. "Flat and clustered" is a sentence an operator can act on — something stalls, periodically, and it does not care about frame sizes — where "the FIFO, the ring or the driver" is three teams. The shape is the finding; the candidate list is its consequence.
And it teaches that best_possible is the third instance of a pattern this module has now used three times. Chapter 21.1 §15 computed it from which counters exist; Chapter 21.4 §15 from whether a partner can be asked; this chapter from whether the four optional counters are present — and in all three the answer is available before any measurement. Telling an operator the ceiling at the start changes the plan; telling them at the end changes the mood.
Deliberately simplified: shape has four values where the two axes give four quadrants, and the selective-and-clustered quadrant is folded into shape = 3 — Section 7 showed it has no member in class G, so the fold hides a contradiction. The 970 threshold hardcodes Section 3's tolerance rather than reading it. And best_possible returns 1 or 3 with nothing between, so a platform with two optional counters is reported as if it had none.
Production implication: the intermediate case is the common one and deserves the arithmetic. A platform with c_filtered and c_vlan_discards but not the FIFO or descriptor counters — which is a very ordinary combination, because the first two are cheap switch-side counters and the last two live in the datapath and the driver — has a ceiling of three, not one, and the three are the FIFO, the ring and the driver. That is exactly class C, which the free ratios reach anyway — so on such a platform the optional counters add nothing at all, and knowing that before the investigation is worth the two comparisons.
16. RTL 8 — The Drop Conformance Monitor
// ---------------------------------------------------------------------
// dropdiag_conformance_monitor -- six verdicts, and three of them say
// the traffic cannot support the measurement.
// ---------------------------------------------------------------------
module dropdiag_conformance_monitor
import dropdiag_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [31:0] frames_in,
input logic [15:0] loss_ppt,
input logic [15:0] mean_in,
input logic [15:0] size_variance_ppt,
input logic fcs_correction_applied,
input logic fcs_stripped_on_delivery,
input logic [3:0] optional_present,
input logic [3:0] optional_proven,
input logic clustering_valid,
input logic outside_class_g,
output logic single_size_traffic,
output logic loss_below_floor,
output logic correction_missing,
output logic probe_skipped,
output logic window_too_short,
output logic model_wrong,
output logic diagnosis_sound
);
// Section 6: a link carrying one frame size has an index that is
// 1.000 by construction rather than by evidence.
assign single_size_traffic = (size_variance_ppt < 16'd50);
// Section 6's sensitivity: below about 2% total loss the index shift
// is inside the tolerance.
assign loss_below_floor = (loss_ppt < 16'd20);
// Section 5 and Section 14's row two.
assign correction_missing = fcs_stripped_on_delivery && !fcs_correction_applied;
// Chapter 21.1 Section 10, inherited whole.
assign probe_skipped = (optional_present != optional_proven);
assign window_too_short = !clustering_valid;
// Section 5: nothing in class G removes small frames preferentially.
assign model_wrong = outside_class_g;
assign diagnosis_sound = !correction_missing && !probe_skipped && !model_wrong;
always_ff @(posedge clk) begin
if (rst_n && single_size_traffic && (frames_in > 32'd1000000))
$display("[dropdiag] one frame size: the selectivity index cannot distinguish anything");
end
endmoduleClassification: an auditor whose three loudest verdicts are about the traffic and are deliberately not faults.
What it teaches: that single_size_traffic and loss_below_floor are excluded from diagnosis_sound for the reason this module has excluded such things three times now. A diagnosis run on single-size traffic is correctly executed and cannot distinguish anything — the index is 1.000 by construction. Calling it unsound would blame the engineer for the link's traffic, which Chapter 21.1 §16, Chapter 21.2 §16 and Chapter 21.5 §16 all declined to do for their own equivalents.
And it teaches that correction_missing is a term of soundness, unlike the others. Applying the check-value correction is entirely within the investigator's control and its absence produces a false positive — drops_large_frames on a healthy link — rather than a blind spot. The distinction the monitor draws is between what the traffic denies you and what you forgot to do, and only the second makes a diagnosis unsound.
Deliberately simplified: size_variance_ppt is an input the block cannot compute from a mean, so the single-size test depends on somebody else measuring a second moment — which Chapter 19.7 §2's histogram supports and the two octet counters do not. model_wrong is outside_class_g renamed. And probe_skipped fires whenever any optional counter is unproven, including ones this diagnosis does not need.
Production implication: the size_variance_ppt dependency is the one gap between this chapter's claim of zero added hardware and its implementation. The selectivity index needs four counters that exist; knowing whether the index is meaningful needs a second moment of the size distribution, which they do not give. The cheap substitute is the RMON histogram's bucket occupancy — already present, receive side, and enough to say whether more than one bucket is populated — which is Chapter 21.3 §3's cross_populated doing the same job in a different chapter.
17. Zero Flops Against 297: What Each Buys
The direction of this chapter was to find what can be done without the four optional counters. The answer is three classes instead of one, and this section is the comparison that decides whether it is enough.
| Instrument set | Flops | Classes | Largest | Expected candidates |
|---|---|---|---|---|
| the RMON six alone | 0 | 1 | 5 | 5.00 |
| plus the selectivity index | 0 | 2 | 5 | 4.33 |
| plus the clustering test | 0 | 3 | 3 | 2.33 |
| plus the four optional counters | ~297 | 5 | 1 | 1.00 |
| plus a delivered-side histogram | ~990 | 5 | 1 | 1.00 |
Row three is free and row four is 297 flops, and the gap between them is 1.33 candidates.
Which is worth it depends on a number this chapter can compute: what a candidate costs.
| Cost of one extra candidate | |
|---|---|
| a site the same team owns | a register read |
| a site another team owns | a conversation |
| class C's three sites | the datapath team, the driver team, and the memory system |
| the expected cost at 2.33 candidates | usually two teams |
Class C is the expensive class and the free method does not break it. The FIFO, an empty descriptor ring and an unreaped driver buffer are three different owners — and the free ratios put all three in one bucket, so a class C result routes a ticket to nobody in particular.
The four optional counters break exactly that class.
| Counter | Splits off | Flops |
|---|---|---|
c_fifo_drops | the FIFO | ~99 |
c_desc_errors | the ring | ~99 |
c_filtered | the filter | ~99 |
c_vlan_discards | VLAN membership | ~99 |
The first two are the ones that matter after this chapter's free method, and they are about 198 flops — 1.4% of Chapter 19.7 §19's 14 166-flop datapath. c_filtered and c_vlan_discards split class B, which the free method has already reduced to two sites that are both configuration and both the same team's.
After the free ratios, two of the four optional counters are worth having and two are almost redundant. That is the chapter's most actionable result and it halves Chapter 21.3 §17's bill.
| Flops | Classes | Largest | |
|---|---|---|---|
| free ratios only | 0 | 3 | 3 |
+ c_fifo_drops and c_desc_errors | ~198 | 5 | 2 |
| + all four | ~297 | 5 | 1 |
Row two gets to five classes for two thirds of the cost, because the free method has already separated the filter and VLAN pair from everything else by their steadiness. The last 99 flops buy the split within class B — the filter against VLAN membership — which is one configuration table against another, both on the same switch, both readable by the same person.
And the three-bit alternative, for completeness.
| Flops | Classes | |
|---|---|---|
| a three-bit drop-reason field | ~3 plus an encoder | 6 — every mechanism |
| the four optional counters | ~297 | 5 |
| the free ratios | 0 | 3 |
Row one is the cheapest and most complete option in the table by two orders of magnitude and nobody builds it. Section 13 derived why it is not there rather than why it should be: a drop-reason field is a per-frame record, and Chapter 21.3 §19's rule — count what has no frame, log what has one — says a drop has a frame and should be logged. The reason it is not is that the frame is being discarded, and a discard path is the last place anybody adds a field.
18. What the Diagnosis Assumes
Nine assumptions. Three are about the index, three about the traffic and three about the platform — and the first is the one that produces a false positive.
| Assumption | From | If false | |
|---|---|---|---|
| 1 | the delivered octet count includes the check value | the DMA engine | a 6.25% bias on minimum-size traffic — Section 5 |
| 2 | a flat drop leaves the mean unchanged | arithmetic | it is an identity, not an assumption |
| 3 | the traffic has more than one frame size | nothing | the index is 1.000 by construction |
| 4 | the drop's rate is stationary over the window | convenience | Section 10 — bursty traffic blurs the time axis |
| 5 | only one mechanism is active | convenience | two mechanisms give a mixed shape and no class |
| 6 | the optional counters, if present, are implemented | Chapter 21.1 §11 | an absent counter reads zero |
| 7 | the DMA does not chain descriptors | the driver | Section 8's step disappears entirely |
| 8 | the delivered count is of the same frames | the DMA | a coalescing engine breaks the pairing |
| 9 | nothing in class G removes small frames | Section 8's table | the index above 1.030 is unexplained |
Row seven is the assumption that decides whether Section 8's one positive result exists at all. A driver that chains descriptors — scatter-gather, which most modern drivers do — can deliver a frame larger than any single buffer, so the step at the buffer size does not exist and G_DMA_SIZE has no signature. The selectivity index then fires for nothing in class G, and its only remaining value is outside_class_g.
| Descriptors chained | Not chained | |
|---|---|---|
| a frame larger than one buffer | delivered across several | dropped |
G_DMA_SIZE exists | no | yes |
| the index's positive result | none | one site named |
| the index's negative result | still rules out nothing | rules out one site |
Row three is the cost and it is worth knowing before the measurement. On a platform with scatter-gather receive, the selectivity index is a test for a fault that cannot happen — and it still has value as outside_class_g, which is a test for something that is not in the fault space at all.
Row one is the assumption that is false on most platforms and produces a false positive rather than a blind spot. Most receive paths strip the four-octet check value before delivery, because the host has no use for it. At a mean of 645.6 octets that is 0.62% and inside the tolerance; at 64 octets it is 6.25% and twice outside it — so an uncorrected index on a control-traffic link reads 0.938 and reports a size-selective drop on a healthy port.
And three things deliberately not assumed:
| Not assumed | Why not |
|---|---|
| that a size histogram exists on the delivered side | Section 4 — it does not, and it is not worth building |
| that the loss is an error | Section 2 — four of the five are legal |
| that the free method reaches one site | Section 12 — three classes, largest three |
Row three is the chapter's honest position and it differs from the direction it was given. The question was what can be done without the optional counters; the answer is three classes of five, not five of five — and Section 17 then finds that two of the four counters are enough to finish, which is the useful half of the result.
And there is a tenth assumption that is not in the table because it is about the measurement's window rather than about the link.
Both free ratios are computed over a window, and neither block chooses one.
| Since reset | Over a fixed window | |
|---|---|---|
| the index's denominator | a week of traffic mixes | one mix |
| a fault that began an hour ago | diluted by a week | visible |
| a traffic-mix change overnight | moves the index | does not |
| what it costs | 0 — it is the default | a shadow copy of four counters |
Row three is the false positive the windowing gap produces and Section 22's seventh complaint is about it: an index computed since reset mixes populations, so a change in what the link carries reads as a change in how it drops. The fix is a shadow copy and a subtraction — about 160 flops for the four counters — and it is the same fix Chapter 21.3 §3 needed for its size cross and Chapter 19.7 §15 needed for its telemetry. Three chapters, one gap, one answer.
19. The Cost, Accounted
A drop diagnosis is two ratios and a variance test, and the ratios are free.
| Block | Flops | Nature |
|---|---|---|
dropdiag_pkg | 0 | six mechanisms, two axes |
mean_size_tracker | 0 | combinational over existing counters |
size_selectivity_index | 0 | one division |
drop_shape_classifier | 0 | a mask intersection |
drop_clustering_probe — 64 windows | ~128 | the time axis, and it must be free-running |
optional_counter_gate | 0 | combinational |
reason_field_absence | ~38 | one counter and two constants |
dropdiag_telemetry | 0 | combinational |
dropdiag_conformance_monitor | 0 | combinational |
| total | ~166 flops |
Seven of the nine blocks are pure combinational logic and the total is the second-smallest in Module 21 — just above Chapter 21.4 §19's 168 and far below Chapter 21.3's 4 856. The reason is that this chapter's instruments are ratios over counters somebody else already pays for.
And the addition to a design is a choice between three options with wildly different costs.
| Option | Flops | Classes of the five |
|---|---|---|
| nothing — the free ratios | 0 | 3 |
c_fifo_drops and c_desc_errors | ~198 | 5, largest 2 |
| all four optional counters | ~297 | 5, largest 1 |
| a three-bit drop-reason field | ~3 plus an encoder | 6 — every mechanism |
Row four is a hundredth of row three's cost and strictly better, and Section 13 explained why it does not exist: it is a field on a discard path, and a discard path is where fields go to be omitted.
Module 21's running bill, six 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% |
| Chapter 21.5 §19 | ~455 | 3.2% |
| this chapter — the free route | 0 | 0.0% |
| total | ~2 041 | 14.4% |
This chapter adds nothing to the bill and removes something from it: Section 17 showed that after the free ratios, two of Chapter 21.1 §19's four optional counters are nearly redundant — the filter and VLAN split is one configuration table against another, on the same switch, for the same person. The module's total falls to about 1 942 flops, 13.7%, and the chapter that spends nothing is the one that finds the saving.
20. Properties Worth Asserting, and One Worth Refusing
Thirty-three properties and eight covers, in four groups: the index's arithmetic, the two axes, the optional counters, and the absence the chapter is built around.
Group one — the index, and the identity it rests on.
// Section 6: a flat drop leaves the mean exactly unchanged, because
// removing a random share of frames removes the same share of octets.
// It is an identity, so it is assertable.
p_flat_is_exact: assert property (@(posedge clk) disable iff (!rst_n)
(drop_is_uniform && both_sides_valid) |->
(s_index_ppt == 16'(S_FLAT_PPT)));
p_index_bounded: assert property (@(posedge clk) disable iff (!rst_n)
s_valid |-> (s_index_ppt <= 16'd4000));
p_index_needs_pop: assert property (@(posedge clk) disable iff (!rst_n)
s_valid |-> both_sides_valid);
p_correction_applied:assert property (@(posedge clk) disable iff (!rst_n)
fcs_stripped_on_delivery |->
(mean_corrected == mean_delivered + 16'd4));
p_three_outcomes: assert property (@(posedge clk) disable iff (!rst_n)
s_valid |-> ($countones({drop_is_flat, drops_large_frames,
drops_small_frames}) == 1));
p_large_is_below: assert property (@(posedge clk) disable iff (!rst_n)
drops_large_frames |->
(s_index_ppt <= (16'(S_FLAT_PPT) - 16'(S_TOLERANCE_PPT))));
p_mean_from_ratio: assert property (@(posedge clk) disable iff (!rst_n)
(frames_in > 32'd0) |-> (mean_in == 16'(octets_in / 48'(frames_in))));
p_loss_consistent: assert property (@(posedge clk) disable iff (!rst_n)
(frames_in >= frames_delivered) |->
(frames_lost == (frames_in - frames_delivered)));Group two — the two axes and their intersection.
p_axes_independent: assert property (@(posedge clk) disable iff (!rst_n)
((SIZE_SELECTIVE | SIZE_FLAT) == ALL_G) &&
((CONFIG_SITES | STATE_SITES) == ALL_G));
p_size_partition: assert property (@(posedge clk) disable iff (!rst_n)
((SIZE_SELECTIVE & SIZE_FLAT) == 6'd0));
p_time_partition: assert property (@(posedge clk) disable iff (!rst_n)
((CONFIG_SITES & STATE_SITES) == 6'd0));
p_mask_intersect: assert property (@(posedge clk) disable iff (!rst_n)
classified |-> (candidates == (by_size & by_time)));
p_blind_is_all: assert property (@(posedge clk) disable iff (!rst_n)
instrument_blind |-> (candidates == ALL_G) || !classified);
p_selective_is_one: assert property (@(posedge clk) disable iff (!rst_n)
(drops_large_frames && clustering_valid && !drop_is_clustered)
|-> (n_candidates == 3'd1));
p_clustered_is_three:assert property (@(posedge clk) disable iff (!rst_n)
(drop_is_flat && drop_is_clustered) |-> (n_candidates == 3'd3));
p_steady_is_two: assert property (@(posedge clk) disable iff (!rst_n)
(drop_is_flat && !drop_is_clustered && clustering_valid)
|-> (n_candidates == 3'd2));
p_small_is_outside: assert property (@(posedge clk) disable iff (!rst_n)
drops_small_frames |-> outside_class_g);Group three — the optional counters, and the probe that must precede them.
p_probe_first: assert property (@(posedge clk) disable iff (!rst_n)
used_optional |-> !probe_required);
p_gate_intersects: assert property (@(posedge clk) disable iff (!rst_n)
used_optional |->
((best_candidates & free_candidates) == best_candidates));
p_ceiling_from_set: assert property (@(posedge clk) disable iff (!rst_n)
(optional_present == 4'hF) |-> (ceiling == 3'd1));
p_free_ceiling: assert property (@(posedge clk) disable iff (!rst_n)
(optional_present == 4'd0) |-> (ceiling == 3'd3));
p_dma_split: assert property (@(posedge clk) disable iff (!rst_n)
((c_desc_errors > 32'd0) && drops_large_frames) |->
((best_candidates & (6'd1 << G_DMA_SIZE)) != 6'd0));
p_no_optional_no_direct:assert property (@(posedge clk) disable iff (!rst_n)
free_method_only |-> (best_candidates == free_candidates));
p_zero_is_not_proof:assert property (@(posedge clk) disable iff (!rst_n)
(!optional_proven[2] && (c_fifo_drops == 32'd0)) |->
((best_candidates & (6'd1 << G_FIFO)) != 6'd0));
p_ceiling_static: assert property (@(posedge clk) disable iff (!rst_n)
$stable(optional_present) |=> $stable(ceiling));Group four — the absence, which is Section 13's subject and Section 20's.
p_no_reason_carried:assert property (@(posedge clk) disable iff (!rst_n)
(reason_bits_carried == 3'd0));
p_reason_needs_3: assert property (@(posedge clk) disable iff (!rst_n)
(reason_bits_needed == 3'd3));
p_model_is_tb: assert property (@(posedge clk) disable iff (!rst_n)
model_is_the_only_source |->
(reason_is_modelled && (reason_bits_carried == 3'd0)));
p_drops_counted: assert property (@(posedge clk) disable iff (!rst_n)
(frame_dropped && drop_is_silent) |=>
(c_drops == $past(c_drops) + 32'd1));
p_silent_no_error: assert property (@(posedge clk) disable iff (!rst_n)
drop_is_silent |->
(!fcs_error && !alignment_error && !undersize && !oversize));
p_g_is_legal: assert property (@(posedge clk) disable iff (!rst_n)
drop_is_silent |-> frame_was_valid);
p_sound_excludes: assert property (@(posedge clk) disable iff (!rst_n)
diagnosis_sound |->
(!correction_missing && !probe_skipped && !model_wrong));
p_blind_not_unsound:assert property (@(posedge clk) disable iff (!rst_n)
(single_size_traffic || loss_below_floor) |->
(diagnosis_sound || correction_missing || probe_skipped));
p_single_size_flat: assert property (@(posedge clk) disable iff (!rst_n)
(single_size_traffic && s_valid) |-> drop_is_flat);And eight covers, because six of these need a traffic mix and a fault a default regression produces neither of.
c_flat_drop: cover property (@(posedge clk) drop_is_flat && (loss_ppt > 16'd20));
c_selective_drop: cover property (@(posedge clk) drops_large_frames);
c_small_selective: cover property (@(posedge clk) drops_small_frames);
c_clustered: cover property (@(posedge clk) drop_is_clustered);
c_steady: cover property (@(posedge clk) clustering_valid && !drop_is_clustered);
c_one_candidate: cover property (@(posedge clk) single_site);
c_single_size: cover property (@(posedge clk) single_size_traffic);
c_both_methods: cover property (@(posedge clk) used_optional && s_valid);21. Verification Scenarios
Fifty-eight scenarios, plus a five-run directed test whose variable is which of five legal discards is active — none of which any frame can cause.
The index's arithmetic — 11 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 1 | 40% of frames dropped at random | index exactly 1.000 |
| 2 | the same at 5% loss | still 1.000 |
| 3 | the same at 90% loss | still 1.000 |
| 4 | every 1 518-octet frame dropped | index 0.099 |
| 5 | half the 1 518-octet frames | 0.662 |
| 6 | a quarter of them | 0.850 |
| 7 | 10% of them | 0.944 — 4% total loss |
| 8 | 5% of them | 0.972 — at the tolerance |
| 9 | 2% of them | 0.989 — below the floor |
| 10 | every 64-octet frame dropped | 2.351 — outside_class_g |
| 11 | fewer than 1 000 delivered frames | both_sides_valid low |
The check-value correction — 8 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 12 | FCS stripped, correction applied, mixed traffic | index 1.000 |
| 13 | FCS stripped, correction NOT applied, mixed | 0.994 — inside tolerance |
| 14 | the same on 64-octet-only traffic | 0.938 — a false positive |
| 15 | drops_large_frames on scenario 14 | asserts wrongly |
| 16 | correction_missing on it | asserts — and it is a soundness term |
| 17 | FCS delivered, correction applied anyway | index 1.006 — the opposite bias |
| 18 | the bias at a 645.6-octet mean | 0.62% |
| 19 | at a 64-octet mean | 6.25% |
The time axis — 9 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 20 | 5% loss in every window | steady |
| 21 | 100% loss in two windows of sixty-four | clustered |
| 22 | the mean loss in both | about the same |
| 23 | max_loss_in_window in both | 50 ppt against 1 000 ppt |
| 24 | fewer than 64 windows | clustering_valid low |
| 25 | bursty foreign traffic, filter drop | looks clustered — Section 10's limit |
| 26 | a consumer stalling every window | looks steady |
| 27 | the axis's information | 3 sites against 3 — 1.000 bits |
| 28 | windows measured in frames, varying rate | durations vary; a stall correlates with time |
The three classes — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 29 | selective and steady | 1 candidate — the descriptor buffer size |
| 30 | flat and steady | 2 — the filter, VLAN membership |
| 31 | flat and clustered | 3 — the FIFO, the ring, the driver |
| 32 | selective and clustered | no member; the model is wrong |
| 33 | index blind, time axis valid | 3 or 3 — no size split |
| 34 | time axis blind, index valid | 1 or 5 |
| 35 | both blind | 5 — where Chapter 21.1 §4 started |
| 36 | expected candidates, both axes | 2.33 |
| 37 | with the RMON six alone | 5.00 |
| 38 | with the four optional counters | 1.00 |
The optional counters — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 39 | c_fifo_drops moves | 1 candidate — the FIFO |
| 40 | c_desc_errors moves, index flat | the ring |
| 41 | c_desc_errors moves, index 0.94 | the buffer size — the split only the index makes |
| 42 | c_fifo_drops reads zero, unproven | the FIFO stays a candidate |
| 43 | the same, proven present | the FIFO is eliminated |
| 44 | two of the four present | ceiling 3, not 1 |
| 45 | c_filtered and c_vlan_discards only | class B split; class C untouched |
| 46 | c_fifo_drops and c_desc_errors only | class C split — the useful pair |
| 47 | the flops for scenario 46 | ~198, and it reaches 5 classes |
| 48 | all four | ~297, largest class 1 |
The absence, and the platform — 10 scenarios.
| # | Scenario | Expected |
|---|---|---|
| 49 | reason_bits_carried | 0, always |
| 50 | reason_bits_needed | 3 |
| 51 | a reason modelled in the testbench | model_is_the_only_source |
| 52 | p_one_reason bound against that model | checks the model, not the design |
| 53 | a three-bit reason field added to the design | $onehot becomes a tautology |
| 54 | the same field, read instead of inferred | 6 classes for ~3 flops |
| 55 | a driver that chains descriptors | G_DMA_SIZE has no signature at all |
| 56 | the index on that platform | only outside_class_g remains useful |
| 57 | single-size traffic | index 1.000 by construction |
| 58 | the $display after a million frames | fires once, and says the instrument is blind |
And the directed test, because random stimulus will not produce it.
The case: five legal discards, activated one at a time, on a link where nothing is wrong with any frame.
Chapter 20.5 §10 divided injections into what a wire can produce and what needs a fault inside the design. None of this chapter's five is in either category. They are not faults at all: a filter table, a VLAN membership list, a consumer that stalls, a descriptor ring, a driver's scheduler. Reaching them needs the environment's configuration to change, and no field of any frame touches any of it.
| Run | What is configured | Where it lives |
|---|---|---|
| A | an address filter that excludes most traffic | a table in the MAC |
| B | a port removed from the VLAN most traffic carries | a port-mode register |
| C | a consumer that stalls for 3 µs every 10 ms | the memory model |
| D | a descriptor ring the driver stops refilling | the bus functional model |
| E | descriptor buffers of 1 518 octets, traffic tagged at 1 522 | the ring's configuration |
The oracle is four-part and no part of it looks at a frame.
| Part | A | B | C | D | E |
|---|---|---|---|---|---|
| selectivity index | 1.000 | 1.000 | 1.000 | 1.000 | below 0.97 |
| time axis | steady | steady | clustered | clustered | steady |
| candidates | 2 | 2 | 3 | 3 | 1 |
| every error counter | zero | zero | zero | zero | zero |
Row four is the control and it is the reason class G exists. Five completely different configurations, five sets of frames that never reach the host, and not a single error counter moves in any of them — because in all five the frames were valid and the receiver was correct to discard them.
Run E is the chapter's one positive result and it is four octets wide. Chapter 21.2 §18 showed the MTU boundary moves from 1 518 to 1 522 with a single VLAN tag; a descriptor buffer sized for the untagged MTU drops exactly the tagged frames — and the selectivity index is the only free instrument that sees it. Runs A and B are indistinguishable from each other and runs C and D are indistinguishable from each other and from the driver, which is the honest limit.
And run C is the one that cannot be produced from the wire at all. Chapter 20.5 §12 proved it: the arrival rate is the line rate and Chapter 19.5 §4's drain rate exceeds it, so no sequence of frames overflows a correctly sized FIFO. The consumer has to stop, which is a memory-system action — and that is the fault Chapter 21.7 takes as its subject rather than as its stimulus.
22. Debugging a Silent Drop
Six complaints.
Complaint 1 — "frames are disappearing and every counter is zero."
| Check | If yes | Meaning |
|---|---|---|
is link_up high? | yes | this chapter, not Chapter 21.4 |
is frames_in moving? | yes | the port is receiving |
is frames_delivered short? | yes | class G |
| what is the selectivity index? | compute it | four counters you already have |
Row four is the step nobody takes. The octet and frame counters on both sides are already collected by every monitoring system on the planet, and dividing them gives a number that separates a size-selective drop from a flat one. The reason it is not computed is that a mean frame size reads as a traffic statistic rather than as a diagnostic.
Complaint 2 — "the index says 0.94 and the DMA counters are clean."
| Check | If yes | Meaning |
|---|---|---|
| is the check value stripped on delivery? | usually | Section 5 |
| was the correction applied? | no | the false positive |
| what is the mean received size? | 64 | four octets is 6.25% of it |
| apply the correction | index 1.000 | there was no selective drop |
A control-traffic link with an uncorrected index reads 0.938 and asserts drops_large_frames on a perfectly healthy port. The bias is exactly four octets per frame; it is negligible on mixed traffic and dominant on minimum-size traffic, and correction_missing is a soundness term for precisely that reason.
Complaint 3 — "the index says 1.000 so there is no drop."
| Check | If yes | Meaning |
|---|---|---|
is loss_ppt non-zero? | yes | there is certainly a drop |
| what does 1.000 mean? | it is flat | five of the six mechanisms |
| is the traffic single-size? | check the histogram | then 1.000 is by construction |
| what does the time axis say? | it is the other half | steady or clustered |
The index is a shape measurement and not a magnitude one, which Section 6 stated as a rule and which is the commonest misreading of it. 1.000 with a 40% loss means forty per cent of the frames are gone and the drop does not care about their size — which eliminates one mechanism of six and leaves five.
Complaint 4 — "we added the four optional counters and the answer did not improve."
| Check | If yes | Meaning |
|---|---|---|
| which four? | c_filtered, c_vlan_discards and two others | check which |
| were they probed? | no | Chapter 21.1 §11 — a zero may be an absence |
| what class was the fault in? | C — flat and clustered | the FIFO, the ring, the driver |
do c_filtered and c_vlan_discards split class C? | no | they split class B, which was already two |
Row four is Section 17's finding arriving as a complaint. After the free ratios, c_filtered and c_vlan_discards are nearly redundant — they separate the filter from VLAN membership, which are two configuration tables on the same switch, owned by the same person. The counters worth having are c_fifo_drops and c_desc_errors, which split the three-site class, and they are two thirds of the cost.
Complaint 5 — "the drop looks clustered but nothing stalls."
| Check | If yes | Meaning |
|---|---|---|
| is the foreign traffic bursty? | yes | Section 10's limit |
| is this an address filter drop? | possibly | and it will look clustered |
| does the index help? | it says flat, which both are | no |
is c_filtered available? | that is the answer | ~99 flops |
The time axis measures the traffic as well as the fault, which the size axis does not — a frame's length is a property of the frame and a drop's timing is a property of the arrival process. On a link whose foreign traffic arrives in bursts the two branches of the time axis blur, and the free method degrades to two classes.
Complaint 6 — "our driver uses scatter-gather so the index found nothing."
| Check | If yes | Meaning |
|---|---|---|
| does the driver chain descriptors? | yes | Section 18's row seven |
| can a frame exceed one buffer? | it is delivered across several | no step exists |
does G_DMA_SIZE have a signature? | no | the mechanism cannot occur |
| is the index still worth computing? | yes — outside_class_g | it tests for faults outside the five |
On a scatter-gather platform the index's one positive result is testing for a mechanism that cannot happen. What remains is its negative value: an index above 1.030 means small frames are being removed preferentially, which no member of class G does — so the fault is a policer, a queue discipline or an upstream device, and that is worth one division.
Complaint 7 — "the index moved from 1.000 to 0.96 overnight and nothing changed."
| Check | If yes | Meaning |
|---|---|---|
| did the traffic mix change? | check the received histogram | the index's denominator moved |
is mean_in the same as yesterday? | no | that is the whole finding |
| is the loss rate the same? | yes | so the drop did not change |
| is the index computed since reset? | yes | an hour of new traffic against a week of old |
The index is a ratio of two means and both of them move with the traffic. A link whose overnight backup shifts the mix towards maximum-size frames changes mean_in and, if any drop is even slightly size-selective, changes the ratio — without anything about the fault changing at all. The windowing gap Section 3 flagged is what makes this severe: an index since reset mixes a week of one traffic mix with an hour of another, and the fix is a delta over a fixed window.
Complaint 8 — "the free method says class C and the DMA counter is clean."
| Check | If yes | Meaning |
|---|---|---|
is c_desc_errors implemented? | check the probe | Chapter 21.1 §11 |
| was it proven present? | no | a zero may be an absence |
is c_fifo_drops implemented? | also no | then class C stands at three |
| what is left? | the driver | and it has no counter at all |
Class C's third member is the host driver and nothing in the MAC can see it. c_fifo_drops and c_desc_errors split off two of the three; the driver is named by elimination, which is the weakest form of naming and the only one available — because a buffer that the driver failed to reap is a fact about software, and a MAC has no register for it.
And the three symptoms this chapter is systematically blamed for:
| Symptom | Blamed on | Usually is |
|---|---|---|
| "every counter is zero" | the instrumentation | four existing counters nobody divided |
| a selective index on control traffic | the DMA | four octets of check value |
| optional counters that did not help | the method | the wrong two of the four |
23. Misconceptions
Misconception 1 — "every counter is zero, so there is no evidence."
The wrong model: the RMON error counters are the instrument, and they read zero, so nothing can be said.
What it costs: the evidence is in the octet counters, not the error ones. octets_in / frames_in and octets_delivered / frames_delivered are four counters every platform already collects, and their ratio separates a size-selective drop from a flat one — a factor of ten between two runs with identical loss. The reason it is not computed is that a mean frame size looks like a traffic statistic.
The corrected model: class G is silent in the error counters by design — Chapter 21.2 §10 showed why — and not silent in the octet counters at all. Two divisions and a variance test take five candidates to at most three, for no hardware.
Misconception 2 — "a selectivity index of 1.000 means nothing is wrong."
The wrong model: the index is a health measure, so 1.000 is healthy.
What it costs: the index is a shape measurement. 1.000 with a 40% loss means forty per cent of the frames are gone and the drop is indifferent to their size — which is five of the six mechanisms, including every one in the expensive class C. Reading a shape as a magnitude discards the loss figure, which is the other half of the report.
The corrected model: report the shape and the magnitude as two numbers. The index says which mechanism; the loss says how much. Neither implies the other, and Section 6 states it as a rule for exactly this reason.
Misconception 3 — "add a size histogram on the delivered side."
The wrong model: the diagnosis is size-conditioned, so build the size-conditioned instrument.
What it costs: about 693 flops, and it separates nothing the four optional counters do not. A delivered-side histogram would locate the step at a descriptor buffer's size — useful — and it would not split class C at all, because the FIFO, the ring and the driver are all size-blind. The size-conditioned route is more expensive than the direct one and reaches fewer sites.
The corrected model: project the distribution onto its mean, which costs nothing because both operands exist. The mean detects the one size-selective mechanism and cannot locate it — and locating it is a bisection with the traffic rather than a register.
Misconception 4 — "the DMA is one site."
The wrong model: class G has five members and the DMA is one of them.
What it costs: the DMA path has two mechanisms with opposite signatures — an empty ring, which is flat and clustered, and a buffer too small, which is selective and steady. They share one counter and they are two different bugs in two different pieces of software. A diagnosis that reports "the DMA" has named a block and not a question.
The corrected model: six mechanisms, five sites. c_desc_errors moving with a flat index is an empty ring; the same counter with an index below 0.97 is a buffer-size mismatch — which is the one split the optional counters cannot make and the free index can.
Misconception 5 — "a drop is a drop."
The wrong model: frames are being lost, so something is broken.
What it costs: four of the five sites are legal discards. A port on a shared segment filters most of what it hears; a port outside a VLAN discards that VLAN's traffic; both are Chapter 7.4 and Chapter 13.2 working exactly as specified. Reporting them in an "errors" figure makes a healthy busy port look like a failing one, which is Chapter 21.2 §15's discards_exceed_errors argument.
The corrected model: a class G drop is a frame the receiver judged valid and then did not deliver. The question is not "why is it broken" but "which of five correct behaviours removed it" — and only two of the five are faults at all.
Misconception 6 — "we need a drop counter."
The wrong model: the problem is that drops are not counted.
What it costs: drops are counted — frames_in minus frames_delivered is the count. What is missing is the reason, which is three bits and which the design carries none of. A sixth aggregate drop counter would add a number that is already computable and no information at all.
The corrected model: the missing thing is a three-bit reason field on the discard path — about three flops and a small encoder — which would give six classes where 297 flops of optional counters give five. Section 13 derived why it is not there: it is a field on a path whose purpose is to throw the frame away, and a discard path is where fields are omitted.
24. Interview Questions
Question 1 — "Frames vanish and every error counter is zero. What do you do?"
What the answer should establish: that the error counters are the wrong instrument and the octet counters are the right one. A strong answer computes the selectivity index: mean delivered size over mean received size, from four counters every platform has, and explains what 1.000 means — a drop indifferent to size, which is five of six mechanisms. The strongest answer mentions the check-value correction, because without it a control-traffic link reads 0.938 and reports a fault that is not there.
Question 2 — "Two runs lose 40% of frames. How do you tell them apart?"
What the answer should establish: the mean delivered size. A flat drop leaves the mean exactly unchanged — removing a random share of frames removes the same share of octets — so the index reads 1.000 precisely. A drop that removes every maximum-size frame from a bimodal mix pulls the mean from 645.6 to 64 and the index to 0.099. A strong answer notes that every other aggregate figure is identical in the two runs.
Question 3 — "Which of class G's five sites does a size-conditioned instrument reach?"
What the answer should establish: one, and only in one of its two mechanisms. Every other decision — the address filter, VLAN membership, the FIFO at start-of-frame, an empty descriptor ring, the driver — is taken before the frame's length is known, so it cannot depend on it. Only a descriptor buffer's size is a comparison against a length. A strong answer adds the corollary: firing eliminates five of six, which is 2.58 bits from counters that already exist.
Question 4 — "Is a delivered-side size histogram worth building?"
What the answer should establish: no. About 693 flops, more than twice the four optional counters' 297, and it separates nothing further — because the sites it would distinguish are already distinguished and the sites it cannot help with are size-blind. A strong answer contrasts it with the free projection: a mean is one moment of the distribution, it costs nothing, and it detects the one selective mechanism without locating it.
Question 5 — "You can add two counters. Which two?"
What the answer should establish: c_fifo_drops and c_desc_errors, not c_filtered and c_vlan_discards. The free time axis already separates the configuration sites from the system-state ones; what it cannot do is split the three system-state sites from each other, and those are three different owners. The two configuration sites are one switch and one person. A strong answer prices it: ~198 flops reaches five classes where ~297 reaches five with a smaller largest class.
Question 6 — "What would make this whole chapter unnecessary?"
What the answer should establish: a three-bit reason field on the drop path. Six mechanisms need ceil(log₂ 6) = 3 bits; the design carries zero. About three flops and a small encoder gives six classes where 297 flops of counters give five. A strong answer explains why it does not exist: it is a field on a path whose purpose is to discard the frame, and Chapter 21.3 §19's rule — count what has no frame, log what has one — says a drop has a frame and should carry a reason.
25. Questions and Answers
26. What's Next
Module 21 has three chapters left, and this one has handed two of them their subject directly.
| Chapter | Takes | From here |
|---|---|---|
| Chapter 21.7 | the descriptor path | class C's ring and buffer mechanisms, in detail |
| Chapter 21.8 | a link with no drops and no errors | the case where even frames_delivered is healthy |
| Chapter 21.9 | the capture | Chapter 21.1 §12's step 6 |
Chapter 21.7 takes class C apart and it starts from the one fault no frame can produce. Chapter 20.5 §12 proved that the arrival rate is the line rate and Chapter 19.5 §4's drain rate exceeds it, so no sequence of frames overflows a correctly sized FIFO — the consumer has to stop. In Chapter 20.5 that was a limit on what could be injected; in the next chapter it is the thing being diagnosed, and Chapter 19.6's outstanding count is the instrument.
Chapter 21.8 is the inversion this chapter makes possible. Everything here rests on frames_in exceeding frames_delivered — a difference that can be counted. A link that is slow and correct has no difference at all: every frame arrives, every frame is delivered, every counter is where it should be, and the complaint is a number that is smaller than the line rate. Chapter 8.3's efficiency arithmetic replaces the counters entirely, which is a different instrument set for the same ticket queue.
And the series is now ninety-nine classes long. The last three have been about who controls a property's evaluation: Chapter 21.4's class 97, where the fault silenced the antecedent; Chapter 21.5's class 98, where the generator did; and class 99, where nothing silences it because the field it quantifies over does not exist. The third is not a member of that family — it is a property with an empty domain rather than an unvisited one — which puts it back with Chapter 19.2 §19's class 82 and Chapter 20.5 §20's class 92, the family about a property's relationship to the design's own representations. Three families now have three members each, and Chapter 21.8 will make a hundred.
Continue learning
Related tutorials
- Related topic
A Method for Debugging Ethernet
The receive path has twelve fault sites and the RMON-required counters separate them into seven classes, one holding five — so the method's ceiling is knowable from a datasheet.
- Related topic
The Ethernet Error Taxonomy
Seven of the twelve reachable frame shapes belong to more than one error class, so exclusivity comes from a priority rule — and the two obvious rules disagree on exactly half the space.
- Related topic
CRC Errors
A channel fault's error rate is proportional to frame length and every logic fault's is flat, so the ratio is 23.72 or 1.00 — measurable on counters a MAC already computes.
- Related topic
Link Failures
A down link has no frames, so every instrument in Module 21 is unavailable at once; the replacement space has fourteen sites and MDIO separates nine classes of them.
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.
