Ethernet · Module 17
What Determinism Requires
Five of a hop's seven latency terms are bounded and sum to 37 microseconds. The other two have no bound at all, and no amount of engineering gives them one.
Chapter 16.5 closed on a network that knows what time it is to about 24 nanoseconds. This module asks a different question with the same dependency: can a frame be guaranteed to arrive by a deadline?
On standard switched Ethernet the answer is no, and the reason is not that the latency is large. It is that two of its seven terms have no upper bound at all.
Five terms are bounded and they sum to 37.12 µs per hop at 1 Gb/s — serialisation, propagation, the lookup, Chapter 12.6's store-and-forward hold, and the blocking from a frame already in flight. All five are bounded by the MTU, the cable and the clock, and none of them is a problem: five hops of bounded terms is 185.6 µs and it is a number.
The other two are Chapter 13.4 §11's strict-priority interference and Chapter 14.1's same-priority queueing, and neither has a bound. Strict priority serves a higher class whenever one is ready, so a lower-priority frame waits for every higher-priority arrival and nothing limits how many there are. At 99% higher-priority utilisation the expected wait is 1214 µs; at 100% it is infinite.
And that is the whole argument. Determinism is not a matter of making the latency small. It is a matter of every term having a bound, and standard Ethernet has two that do not.
1. Scope — What This Chapter Owns
This chapter owns the requirement: what determinism means, the seven terms of a hop's latency, which two are unbounded and why, the worst-case sum across N hops, and what must be added to bound the remainder.
It does not own the schedule. Gate schedules, the gate-control list and their operation are Chapter 17.2. Section 16 states what a schedule must provide and does not build one.
It does not own preemption. Express and preemptable traffic, fragment framing and what preemption costs the MAC are Chapter 17.3. Section 17's guard-band arithmetic shows why that chapter exists.
It does not own the clock. Chapter 16.5 assembled a 24.2 ns budget; Section 17 converts it into bandwidth, which is the form Module 17 needs it in.
And it does not own the queueing. Chapter 14.1 built the queues, Chapter 14.3 derived the 58.6% bound and Chapter 13.4 §11 built the scheduler. This chapter takes all three as inputs and asks what they bound.
2. What Determinism Actually Requires
The word is used for three different properties and only the third is what an application needs.
| Property | Statement | Is standard Ethernet this? |
|---|---|---|
| low latency | the typical delay is small | yes — microseconds |
| low jitter | the delay varies little | usually |
| bounded latency | the delay never exceeds D | no |
Rows one and two are statistical and row three is a guarantee, and the distinction is exactly Chapter 16.5 §2's random-against-systematic split arriving in a different subject: a distribution's shape says nothing about its support.
A network whose latency is 50 µs on 99.999% of frames and 4 ms on the rest is low-latency, low-jitter, and useless to a motion controller — because the controller's deadline is missed on the frames that matter and it has no way to know which those will be.
And the applications that need row three are specific:
| Application | Deadline | Consequence of a miss |
|---|---|---|
| a motion controller's update | ~1 ms, hard | a machined part out of tolerance |
| a protective relay's trip | ~4 ms, hard | equipment damage |
| an automotive brake-by-wire command | ~10 ms, hard | — |
| an audio stream's sample | ~2 ms, hard | an audible dropout |
| a video frame | ~33 ms, soft | a visible glitch |
| a file transfer | none | — |
The deadlines are not tight by the standards of anything in this track — a millisecond is eighty maximum frames at 1 Gb/s. What makes them hard is that they must be met every time, and Section 11 shows that standard Ethernet cannot promise that at any deadline.
Which is why the chapter's question is not "how fast" but "what is the bound", and why Section 11's answer is that two terms do not have one.
3. RTL 1 — A Latency Accountant
Before deciding what is bounded, measure what happens. This module attributes a frame's delay to the seven terms, which is what makes the argument checkable rather than theoretical.
// -----------------------------------------------------------------------
// det_pkg -- shared types for the determinism analysis.
// -----------------------------------------------------------------------
package det_pkg;
localparam int NS_W = 32;
typedef logic [NS_W-1:0] ns_t;
// The seven terms of a hop's latency. The kind matters more than the
// magnitude: a bounded term is a number and an unbounded one is not.
typedef enum logic [2:0] {
T_SERIALISE = 3'd0, // 8.1 -- bounded by the MTU
T_PROPAGATE = 3'd1, // 8.2 -- bounded by the cable
T_LOOKUP = 3'd2, // 12.1 -- bounded by the design
T_STORE_FWD = 3'd3, // 12.6 -- bounded by the MTU
T_BLOCKING = 3'd4, // a frame already transmitting -- bounded
T_INTERFERE = 3'd5, // 13.4 s11 -- UNBOUNDED
T_QUEUE = 3'd6 // 14.1 -- UNBOUNDED
} term_e;
function automatic bit is_bounded(input term_e t);
is_bounded = (t <= T_BLOCKING);
endfunction
typedef struct packed {
logic [15:0] frame_tag;
ns_t arrive_ns;
ns_t depart_ns;
ns_t per_term [7];
} hop_record_t;
endpackage// -----------------------------------------------------------------------
// hop_latency_accountant -- decomposes one hop's delay into the seven
// terms, so a measured latency can be attributed rather than merely
// recorded.
//
// The point is section 11's: a total tells you nothing about a bound,
// and the attribution is what says which term to go and bound.
// -----------------------------------------------------------------------
module hop_latency_accountant
import det_pkg::*;
(
input logic clk,
input logic rst_n,
input logic frame_arrived, // 16.3's ingress SFD capture
input logic [15:0] arrive_tag,
input ns_t now_ns,
input logic [13:0] frame_octets,
input logic [15:0] line_rate_mbps,
// Events during the frame's stay, from the datapath.
input logic lookup_done,
input logic store_fwd_done,
input logic enqueued,
input logic head_of_queue,
input logic higher_prio_served, // 13.4 s11's scheduler chose another class
input logic same_prio_ahead, // 14.1's backlog in our own class
input logic tx_started,
input logic frame_departed,
input logic [15:0] depart_tag,
output logic record_valid,
output hop_record_t record,
output ns_t bounded_total_ns,
output ns_t unbounded_total_ns,
output logic unbounded_dominates,
output ns_t worst_unbounded_seen,
output logic [31:0] c_frames
);
ns_t t_start, t_mark;
ns_t acc [7];
logic [15:0] tag_q;
logic in_flight;
always_ff @(posedge clk or negedge rst_n) begin
int i;
if (!rst_n) begin
for (i = 0; i < 7; i++) acc[i] <= '0;
t_start <= '0; t_mark <= '0; tag_q <= '0;
in_flight <= 1'b0; record_valid <= 1'b0;
bounded_total_ns <= '0; unbounded_total_ns <= '0;
unbounded_dominates <= 1'b0; worst_unbounded_seen <= '0;
c_frames <= '0;
end else begin
record_valid <= 1'b0;
if (frame_arrived) begin
for (i = 0; i < 7; i++) acc[i] <= '0;
t_start <= now_ns;
t_mark <= now_ns;
tag_q <= arrive_tag;
in_flight <= 1'b1;
// Serialisation is computable from the frame's own length and
// the line rate: it needs no event at all.
acc[T_SERIALISE] <= ns_t'((32'(frame_octets) * 8000) /
32'(line_rate_mbps));
end
if (in_flight) begin
if (lookup_done) begin acc[T_LOOKUP] <= now_ns - t_mark; t_mark <= now_ns; end
if (store_fwd_done) begin acc[T_STORE_FWD] <= now_ns - t_mark; t_mark <= now_ns; end
// The two unbounded terms accumulate per cycle while their
// cause is present. They are the only terms that are not a
// single interval.
if (higher_prio_served) acc[T_INTERFERE] <= acc[T_INTERFERE] + 1;
if (same_prio_ahead) acc[T_QUEUE] <= acc[T_QUEUE] + 1;
if (tx_started) begin acc[T_BLOCKING] <= now_ns - t_mark; t_mark <= now_ns; end
end
if (frame_departed && in_flight && (depart_tag == tag_q)) begin
automatic ns_t b, u;
b = '0; u = '0;
for (i = 0; i < 7; i++)
if (is_bounded(term_e'(i))) b = b + acc[i];
else u = u + acc[i];
record.frame_tag <= tag_q;
record.arrive_ns <= t_start;
record.depart_ns <= now_ns;
for (i = 0; i < 7; i++) record.per_term[i] <= acc[i];
bounded_total_ns <= b;
unbounded_total_ns <= u;
unbounded_dominates <= (u > b);
if (u > worst_unbounded_seen) worst_unbounded_seen <= u;
record_valid <= 1'b1;
in_flight <= 1'b0;
c_frames <= c_frames + 1;
end
end
end
endmoduleClassification: an attribution engine. It changes no behaviour and is the only way the chapter's claim becomes checkable.
What it teaches: that the seven terms split into five that are intervals and two that are accumulations, and the difference is exactly the boundedness. Serialisation, propagation, lookup, store-and-forward and blocking each happen once and take a computable time. Interference and queueing accumulate for as long as their cause persists — and nothing in the datapath limits how long that is. The module's structure encodes the argument.
And it teaches that worst_unbounded_seen is a high-water mark and explicitly not a bound. Section 19's rejected property is exactly the mistake of treating one for the other. The name is chosen to resist it, and Section 13 is why the resistance matters.
Deliberately simplified: the frame's tag threads through the whole hop, which assumes Chapter 16.3 §13's tagging is present. A switch without it cannot attribute at all — the departure cannot be matched to the arrival — and the same argument that made tags necessary for timestamps makes them necessary for latency accounting.
Production implication: unbounded_dominates is the single bit that says whether a measured latency means anything. A frame whose delay was mostly bounded terms is a frame whose delay is repeatable; one whose delay was mostly interference or queueing is a sample from a distribution with no upper limit. Reporting a mean latency without this bit reports a number whose reproducibility is unknown.
4. The Terms of a Hop's Latency
Seven terms, priced at 1 Gb/s over 100 m, with the boundedness column doing the work.
| Term | Formula | At 1 Gb/s | Bounded by |
|---|---|---|---|
| serialisation — Chapter 8.1 | L / R | 12.14 µs | the MTU |
| propagation — Chapter 8.2 | d / c | 0.50 µs | the cable |
| lookup and fabric — Chapter 12.1 §12 | fixed | 0.03 µs | the design |
| store-and-forward — Chapter 12.6 | L / R | 12.14 µs | the MTU |
| blocking — a frame already transmitting | L_max / R | 12.30 µs | the MTU |
| interference — Chapter 13.4 §11 | higher-priority arrivals | — | nothing |
| queueing — Chapter 14.1 | same-priority backlog | — | nothing |
| bounded subtotal | — | 37.12 µs | — |
Rows one and four are both L/R and both are present, which is worth noticing because it looks like double-counting and is not. Chapter 12.6 established that a store-and-forward switch receives the whole frame before forwarding any of it — so the frame is serialised into the switch and then serialised out of it, and both intervals are real. A cut-through switch removes the fourth row and keeps the first, which is that chapter's entire latency argument in one line.
Row five is the one that is easy to forget and impossible to remove. When a frame becomes ready to transmit, the port may already be mid-frame — and Chapter 12.6 §8 established that Ethernet cannot abort a frame in progress. So the new frame waits up to a full maximum frame, including preamble and interframe gap: 12.30 µs. This is Chapter 14.2 §5's dead-time term appearing in a different chapter for a different reason, and it is the term Chapter 17.3's preemption exists to shrink.
And the bounded subtotal is a real number that a design can work with:
| Line rate | Bounded per hop |
|---|---|
| 1 Gb/s | 37.12 µs |
| 10 Gb/s | 3.71 µs |
| 100 Gb/s | 0.87 µs |
Which is the point worth carrying into rows six and seven: the bounded terms are not the problem. Five hops at 1 Gb/s is 185.6 µs against a 1 ms deadline, with room to spare — and the deadline is missed anyway, by terms that are not in this table.
==
5. RTL 2 — The Queueing Term
The first of the two unbounded terms, modelled so its unboundedness is visible rather than asserted.
// -----------------------------------------------------------------------
// queueing_term_model -- measures how long a frame waits behind
// same-priority frames, and why nothing bounds it.
//
// 14.1's queue holds what arrives. The bound on the WAIT is the
// queue's occupancy times the drain time -- and the occupancy is
// bounded only by the queue's depth, which is a memory decision and
// not a latency one.
// -----------------------------------------------------------------------
module queueing_term_model
import det_pkg::*;
#(
parameter int QUEUE_CELLS = 4096, // 14.1 section 5
parameter int CELL_OCTETS = 128,
parameter int LINE_RATE_MBPS = 1000
)(
input logic clk,
input logic rst_n,
input logic enq,
input logic deq,
input logic [15:0] enq_octets,
output logic [15:0] occupancy_cells,
output ns_t wait_at_current_occupancy_ns,
output ns_t wait_at_full_ns,
output ns_t worst_wait_seen_ns,
output logic queue_is_the_bound,
output logic [31:0] c_enq,
output logic [31:0] c_deq
);
// Draining the whole queue at line rate. This is the ONLY bound the
// term has, and it is a memory-sizing decision -- 14.1 section 14
// priced the same quantity as "what a buffer buys, in time".
localparam int FULL_NS = (QUEUE_CELLS * CELL_OCTETS * 8000) / LINE_RATE_MBPS;
logic [15:0] occ;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
occ <= '0; worst_wait_seen_ns <= '0;
c_enq <= '0; c_deq <= '0; queue_is_the_bound <= 1'b0;
end else begin
if (enq && !deq) occ <= occ + 1'b1;
if (deq && !enq) occ <= occ - 1'b1;
if (enq) c_enq <= c_enq + 1;
if (deq) c_deq <= c_deq + 1;
if (enq) begin
automatic ns_t w;
w = ns_t'((32'(occ) * CELL_OCTETS * 8000) / LINE_RATE_MBPS);
if (w > worst_wait_seen_ns) worst_wait_seen_ns <= w;
// The queue is the dominant term once its wait exceeds the
// whole bounded subtotal -- section 4's 37.12 us at 1 Gb/s.
queue_is_the_bound <= (w > ns_t'(37_120));
end
end
end
assign occupancy_cells = occ;
assign wait_at_current_occupancy_ns =
ns_t'((32'(occ) * CELL_OCTETS * 8000) / LINE_RATE_MBPS);
assign wait_at_full_ns = ns_t'(FULL_NS);
endmoduleClassification: an occupancy-to-latency converter. Two counters and a multiply, and the multiply is the argument.
What it teaches: that the queueing term's only bound is the queue's depth, which was sized for a completely different reason. Chapter 14.1 §14 sized buffers by how long a burst they absorb; the same 4096-cell queue, read as a latency, is 4.19 ms at 1 Gb/s. So a design that added buffer to reduce drops added 4 ms to its worst-case latency, and the two decisions were made by different people for opposite reasons.
And it teaches the direction of that trade, which is genuinely uncomfortable. A deeper queue drops fewer frames and has a worse bound. A shallower queue has a better bound and drops more. Chapter 14.1's whole chapter argued for depth; this one argues against it, and there is no configuration that satisfies both.
Deliberately simplified: occupancy is counted in cells regardless of frame size, so the wait estimate assumes cells drain at line rate. A queue holding many small frames drains more slowly than one holding few large ones — Chapter 8.3's interframe-gap overhead — so the estimate is optimistic by up to 20% on minimum-size frames.
Production implication: wait_at_full_ns is the number a latency budget must use and worst_wait_seen_ns is the number a monitoring system reports, and confusing them is Section 19's rejected property. At 1 Gb/s they are 4.19 ms and whatever happened to be observed — which on a lightly loaded network is a few hundred microseconds and is not a bound.
6. Why the Queueing Term Is Unbounded
Section 5 gives the queueing term a bound — the queue's depth — so calling it unbounded needs justifying. The justification is that the bound is useless and that the mechanism refills.
First, the bound's size. Chapter 14.1 §5's 4096-cell queue, drained at line rate:
| Line rate | Full-queue wait |
|---|---|
| 1 Gb/s | 4194 µs |
| 10 Gb/s | 419 µs |
| 100 Gb/s | 41.9 µs |
Against the millisecond deadlines Section 2 listed, a 4.19 ms bound is not a bound — it is a statement that the deadline cannot be met.
Second, and more fundamentally: the queue refills. A bound of depth × drain assumes the frame waits for the queue that was there when it arrived and for nothing that arrives afterwards. With Chapter 13.4 §11's FIFO within a class that is true; with any scheduler that can serve a later arrival first, it is not — and Section 8's strict priority is exactly such a scheduler.
Third: the depth was chosen for a different objective and will not be reduced. Chapter 14.1 §14's argument for depth is drop avoidance, and Chapter 14.1 §8 showed one congested port starving twenty-three others when the pool is small. A design that shrinks its queues to bound latency has undone that chapter's work.
Which gives the honest statement: the queueing term is bounded by a number that is four orders of magnitude too large, derived from a parameter chosen to be large for good reasons. It is not unbounded in the mathematical sense. It is unbounded in the sense that matters: no achievable value of it satisfies the requirement.
And there is a fourth point that makes the first three decisive. Even a 4.19 ms bound is a bound on the wait behind this queue's occupancy at this hop. Across five hops it is 21 ms, and the deadline was 1 ms — so shrinking the queue to a tenth still misses by a factor of two, and shrinking it to a tenth is Chapter 14.1 §6's absorption cut by ten.
7. RTL 3 — Store-and-Forward as a Latency Term
A bounded term, built so its size is visible next to the unbounded ones — because it is the largest bounded term and it is frequently blamed for the problem.
// -----------------------------------------------------------------------
// store_forward_term -- the latency a store-and-forward switch adds,
// and what cut-through would save.
//
// 12.6 established the two disciplines. This module prices them as
// latency terms and shows that the choice, while real, does not
// change whether a bound exists.
// -----------------------------------------------------------------------
module store_forward_term
import det_pkg::*;
#(
parameter int LINE_RATE_MBPS = 1000,
parameter int CUT_THROUGH_OCTETS = 64 // 12.6's commit point
)(
input logic clk,
input logic rst_n,
input logic frame_start,
input logic [13:0] frame_octets,
input logic cut_through_enabled,
input logic rate_mismatch, // 12.6 s10: cut-through needs equal rates
output ns_t store_forward_ns,
output ns_t cut_through_ns,
output ns_t saving_ns,
output logic cut_through_available,
output ns_t worst_case_ns, // the MTU, either way
output logic [31:0] c_frames,
output logic [31:0] c_forced_store_fwd
);
localparam int MTU_OCTETS = 1518;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
store_forward_ns <= '0; cut_through_ns <= '0; saving_ns <= '0;
cut_through_available <= 1'b0; worst_case_ns <= '0;
c_frames <= '0; c_forced_store_fwd <= '0;
end else if (frame_start) begin
automatic ns_t sf, ct;
sf = ns_t'((32'(frame_octets) * 8000) / LINE_RATE_MBPS);
ct = ns_t'((CUT_THROUGH_OCTETS * 8000) / LINE_RATE_MBPS);
store_forward_ns <= sf;
cut_through_ns <= ct;
// 12.6 section 10: cut-through requires the ingress and egress
// rates to match, or the egress underruns. A 10 G -> 1 G hop
// must store and forward whatever the configuration says.
cut_through_available <= cut_through_enabled && !rate_mismatch;
if (cut_through_enabled && rate_mismatch)
c_forced_store_fwd <= c_forced_store_fwd + 1;
saving_ns <= (cut_through_enabled && !rate_mismatch) ? (sf - ct) : '0;
// The WORST case is what a bound is made of, and it is the MTU
// regardless of the typical frame size.
worst_case_ns <= ns_t'((MTU_OCTETS * 8000) / LINE_RATE_MBPS);
c_frames <= c_frames + 1;
end
end
endmoduleClassification: a latency calculator with an availability predicate. No state beyond the current frame.
What it teaches: that cut-through's saving is real and large and changes nothing about boundedness. At 1 Gb/s it removes 12.14 µs and leaves 0.51 µs — a 24× reduction on that term — and the two unbounded terms are untouched. So a design that adopted cut-through to gain determinism gained latency and not a bound, which is Chapter 12.6's trade read in a new light.
And it teaches that rate_mismatch forces store-and-forward regardless of configuration — Chapter 12.6 §10's requirement that the egress rate not exceed the ingress. A 10 Gb/s to 1 Gb/s hop cannot cut through at all, so a mixed-rate path has store-and-forward at exactly the hops where the frame is slowest. c_forced_store_fwd makes that visible, and a design that assumed cut-through everywhere has budgeted for a latency it does not get.
Deliberately simplified: the cut-through commit point is a constant 64 octets, which is Chapter 12.6's runt-check threshold. Production designs commit later when the forwarding decision needs more of the header — a Chapter 13.2 tag plus an IP header pushes the commit to 60 or more octets anyway — so the saving is slightly smaller and the shape is the same.
Production implication: worst_case_ns is deliberately the MTU rather than the observed frame size, because a bound is made of worst cases and a latency budget built from typical frame sizes is not a budget. A network carrying mostly 64-octet frames still has a 12.14 µs store-and-forward term, because one 1518-octet frame is enough to produce it — and Section 13 is the general form of that argument.
8. RTL 4 — Strict Priority and the Interference Term
The second unbounded term, and the one with no bound at all rather than a uselessly large one.
// -----------------------------------------------------------------------
// interference_model -- measures how long a frame waits because a
// strict-priority scheduler served higher classes instead.
//
// 13.4 section 11 built the scheduler and its callout named starvation
// as the default behaviour of the default discipline. This module
// prices that as a latency term and shows it has no bound.
// -----------------------------------------------------------------------
module interference_model
import det_pkg::*;
#(
parameter int NUM_CLASSES = 8,
parameter int LINE_RATE_MBPS = 1000
)(
input logic clk,
input logic rst_n,
input logic our_frame_waiting,
input logic [2:0] our_class,
input logic higher_served,
input logic [13:0] higher_octets,
// The offered load of the higher classes, as a fraction x 1000.
input logic [15:0] higher_util_x1000,
input logic eval,
output ns_t interference_ns,
output ns_t worst_interference_seen_ns,
output ns_t expected_wait_ns, // L / (R(1-u)) -- the M/D/1 form
output logic utilisation_is_one, // the wait is infinite
output logic has_a_bound, // always low, and that is the point
output logic [31:0] c_interference_events
);
localparam int MTU_NS = (1518 * 8000) / LINE_RATE_MBPS;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
interference_ns <= '0; worst_interference_seen_ns <= '0;
expected_wait_ns <= '0; utilisation_is_one <= 1'b0;
has_a_bound <= 1'b0; c_interference_events <= '0;
end else begin
if (our_frame_waiting && higher_served) begin
// Every higher-priority frame served while we wait is added
// to our delay, and NOTHING limits how many there are.
interference_ns <= interference_ns +
ns_t'((32'(higher_octets) * 8000) / LINE_RATE_MBPS);
c_interference_events <= c_interference_events + 1;
end
if (!our_frame_waiting) begin
if (interference_ns > worst_interference_seen_ns)
worst_interference_seen_ns <= interference_ns;
interference_ns <= '0;
end
if (eval) begin
// The expected wait for a lower class under a strict-priority
// scheduler: L / (R (1 - u)), which diverges as u -> 1.
if (higher_util_x1000 >= 16'd1000) begin
utilisation_is_one <= 1'b1;
expected_wait_ns <= '1; // saturate: it is infinite
end else begin
utilisation_is_one <= 1'b0;
expected_wait_ns <= ns_t'((32'(MTU_NS) * 1000) /
(1000 - 32'(higher_util_x1000)));
end
end
// There is no configuration, no queue depth and no line rate at
// which this term acquires an upper bound. The output is tied
// low deliberately, as documentation.
has_a_bound <= 1'b0;
end
end
endmoduleClassification: a divergence model. Its most important output is a constant zero.
What it teaches: that strict priority's interference term has no bound of any size, which is categorically different from Section 6's uselessly large one. The queueing term is bounded by the queue's depth — 4.19 ms, too large but finite. The interference term is bounded by how much higher-priority traffic arrives, and nothing in the switch, the standard or the configuration limits that. At 100% higher-priority utilisation a lower class is served never.
And it teaches the shape of the divergence, which is worth having: L / (R(1 − u)).
| Higher-priority utilisation | Expected wait at 1 Gb/s |
|---|---|
| 10% | 13.5 µs |
| 50% | 24.3 µs |
| 90% | 121 µs |
| 99% | 1214 µs |
| 100% | infinite |
The curve is flat until it is not, which is why a network that behaved for years fails abruptly when a new high-priority flow is added — and why Chapter 13.4 §11's callout called starvation the default behaviour of the default discipline.
Deliberately simplified: the expected-wait formula is the M/D/1 queueing form and assumes Poisson arrivals, which real traffic is not. The shape — a 1/(1−u) divergence — is robust to the arrival process; the constant is not, and a design using this number as anything but an order of magnitude has over-read it.
Production implication: has_a_bound is tied low and is an output rather than a comment because it is the module's specification. A design that later adds a credit-based shaper or Chapter 17.2's gate schedule changes that output to high, and the difference between the two versions is exactly what Module 17 is for. An output that is constant today and meaningful tomorrow is worth the flop.
==
9. Interference, Derived Across N Hops
One hop's interference is unbounded. Across N hops it is worse in a way that is not merely N times worse, and the reason is worth deriving.
At each hop a frame is exposed to that hop's higher-priority traffic, and the traffic at each hop is different: a flow that did not interfere at hop 1 may interfere at hop 3, because it joined the path there.
| one hop | N hops | |
|---|---|---|
| interfering flows | those sharing this egress | the union across every egress |
| bound on the count | none | none |
| correlation between hops | — | none — and that is the problem |
Row three is the subtlety. If the same interfering traffic were present at every hop, a frame delayed at hop 1 would arrive at hop 2 behind that traffic and might be delayed less. Because the interferers differ, the delays are independent and accumulate.
And there is a second-order effect that makes it worse, which Chapter 14.3 §6 met in a different form: a frame delayed at hop 1 arrives at hop 2 at a different time than it would have. So a design that analysed each hop against a stationary traffic model has analysed a situation that does not occur — the frame's arrival pattern at hop k depends on its delays at hops 1 through k−1.
Which is why worst-case latency analysis for a real network is a research field rather than a calculation, and why Section 16's answer sidesteps it entirely: a schedule does not bound the interference, it removes the interferers from the window.
The bounded terms, meanwhile, are simply additive and they are worth having as the floor:
| Hops | Bounded total at 1 Gb/s | at 10 Gb/s | at 100 Gb/s |
|---|---|---|---|
| 1 | 37.1 µs | 3.71 µs | 0.87 µs |
| 3 | 111.4 µs | 11.1 µs | 2.61 µs |
| 5 | 185.6 µs | 18.6 µs | 4.35 µs |
| 7 | 259.8 µs | 26.0 µs | 6.09 µs |
| 10 | 371.2 µs | 37.1 µs | 8.70 µs |
And the comparison that makes the chapter's point: five hops of bounded terms at 1 Gb/s is 185.6 µs, comfortably inside a 1 ms deadline — and adding a single 4096-cell queue's worth of backlog at one hop adds 4194 µs and misses it by a factor of four.
10. RTL 5 — Worst-Case Latency Across N Hops
A calculator that assembles the bounded terms and refuses to produce a total when an unbounded one is present.
// -----------------------------------------------------------------------
// worstcase_latency_calc -- sums a path's bounded terms and reports
// whether a bound exists at all.
//
// The refusal is the module's point. A calculator that produces a
// number regardless is a calculator whose output means different
// things in different configurations, and nobody checks which.
// -----------------------------------------------------------------------
module worstcase_latency_calc
import det_pkg::*;
#(
parameter int MAX_HOPS = 16
)(
input logic clk,
input logic rst_n,
input logic eval,
input logic [7:0] n_hops,
input ns_t per_hop_bounded_ns [MAX_HOPS],
// Per hop: is the interference term bounded at this hop?
input logic [MAX_HOPS-1:0] hop_has_schedule, // 17.2's gates
input logic [MAX_HOPS-1:0] hop_has_shaper, // a credit-based shaper
input ns_t hop_queue_full_ns [MAX_HOPS],
output ns_t bounded_sum_ns,
output ns_t queue_sum_ns,
output logic bound_exists,
output logic [7:0] first_unbounded_hop,
output ns_t worst_case_ns, // valid only if bound_exists
output logic [31:0] c_evals
);
always_ff @(posedge clk or negedge rst_n) begin
int h;
if (!rst_n) begin
bounded_sum_ns <= '0; queue_sum_ns <= '0;
bound_exists <= 1'b0; first_unbounded_hop <= '0;
worst_case_ns <= '0; c_evals <= '0;
end else if (eval) begin
automatic ns_t b, q;
automatic bit ok;
automatic logic [7:0] firstbad;
b = '0; q = '0; ok = 1'b1; firstbad = 8'hFF;
for (h = 0; h < MAX_HOPS; h++) begin
if (h < int'(n_hops)) begin
b = b + per_hop_bounded_ns[h];
q = q + hop_queue_full_ns[h];
// A hop bounds its interference only if something at that
// hop limits higher-priority arrivals -- a gate schedule or
// a credit-based shaper. Strict priority alone does not.
if (!hop_has_schedule[h] && !hop_has_shaper[h]) begin
if (ok) firstbad = h[7:0];
ok = 1'b0;
end
end
end
bounded_sum_ns <= b;
queue_sum_ns <= q;
bound_exists <= ok;
first_unbounded_hop <= firstbad;
// The total is published ONLY when every hop bounds its
// interference. Otherwise it is left at zero, so a consumer
// cannot mistake a partial sum for a guarantee.
worst_case_ns <= ok ? (b + q) : '0;
c_evals <= c_evals + 1;
end
end
endmoduleClassification: an accumulator with a validity predicate. The predicate is the whole module.
What it teaches: that a worst-case calculator must be able to say "no bound exists", and most do not. A tool that sums the terms it can compute produces a number in every configuration, and that number means "the worst case" in one and "a lower bound on the worst case" in another — with nothing distinguishing them. Publishing zero when bound_exists is low forces the consumer to check.
And it teaches that the predicate is per hop and the conjunction is over the path. One hop without a schedule or a shaper removes the bound for the whole path, however well the other nine are engineered — which is Chapter 14.3 §18's composition argument again: a path's guarantee is the conjunction of its hops' and a conjunction fails on one member.
Deliberately simplified: hop_queue_full_ns is added unconditionally, so the total assumes every hop's queue is full simultaneously. That is the correct worst case and it is very pessimistic — a real bound uses a network-calculus argument about arrival curves, which is a research-grade calculation and produces a smaller number. The pessimistic sum is still useful because it is an upper bound and it is computable.
Production implication: first_unbounded_hop is the output an engineer acts on. A path of ten hops with one unscheduled switch has one thing to fix, and the alternative — being told the path has no bound — sends somebody to audit all ten. The index costs eight bits and it converts a verdict into a work item.
11. Which Term Is Unbounded, and Why
Two terms, two different kinds of unboundedness, and the distinction decides what each one needs.
| Queueing — Chapter 14.1 | Interference — Chapter 13.4 §11 | |
|---|---|---|
| the wait is | behind same-priority frames | behind higher-priority frames |
| bounded by | the queue's depth | nothing |
| that bound is | 4194 µs at 1 Gb/s | — |
| so it is | finite and useless | genuinely unbounded |
| shrinking the queue | helps, and costs Chapter 14.1 §6's absorption | does nothing |
| what bounds it | a limit on same-class arrivals | a limit on higher-class arrivals |
Row six is the same answer twice, and it is the chapter's conclusion: both terms are bounded by bounding the arrivals, and nothing inside a switch can do that.
A switch can decide what to do with what arrives. Chapter 13.4 §11's scheduler chooses; Chapter 14.1's allocator discards; Chapter 14.4's gate stops a class. None of them limits what the senders transmit, and a latency bound is a statement about what arrives.
Which is why a credit-based shaper and a gate schedule are the two answers and both act on transmission rather than on reception.
A credit-based shaper limits a class's rate. A class that may send at most X bits per second contributes at most X × T bits of interference in any interval T — so the interference term acquires a bound, and the bound is a configuration parameter rather than a property of the traffic.
A gate schedule limits a class's times. A class that may transmit only during its window contributes zero interference outside it — so a frame sent in its own window waits for nothing at all, and the bound is the window's position rather than any traffic figure.
And the second is stronger, which is why Chapter 17.2 exists. A shaper bounds the interference to a rate-dependent number; a schedule removes it.
| shaper | schedule | |
|---|---|---|
| interference bound | rate × interval | zero, inside the window |
| needs a synchronised clock | no | yes |
| needs the senders to cooperate | no — the switch shapes | yes — they must transmit in their windows |
| bound quality | a number | the best possible |
Row two is where Module 16 enters, and it is the whole reason these two modules are adjacent.
12. RTL 6 — Measuring the Distribution, Not the Mean
Section 19's rejected property is about mistaking an observation for a bound. This module is the instrument that makes the mistake avoidable.
// -----------------------------------------------------------------------
// latency_distribution -- a log-bucketed histogram of observed
// latency, with the tail made explicit.
//
// A mean is the wrong summary for a quantity whose requirement is a
// deadline. What matters is how far the tail reaches and, crucially,
// whether it is still growing -- which a histogram shows and a
// maximum does not.
// -----------------------------------------------------------------------
module latency_distribution
import det_pkg::*;
#(
parameter int BINS = 20, // 1 ns .. ~1 ms, log2
parameter int CNT_W = 32
)(
input logic clk,
input logic rst_n,
input logic sample_valid,
input ns_t latency_ns,
input ns_t deadline_ns,
input logic window_tick,
output logic [CNT_W-1:0] bin [BINS],
output ns_t p50_ns,
output ns_t p999_ns,
output ns_t p99999_ns,
output ns_t worst_seen_ns,
output logic [CNT_W-1:0] n_samples,
output logic [CNT_W-1:0] c_missed_deadline,
output logic tail_still_growing, // the max moved this window
output ns_t highest_occupied_bin_ns
);
ns_t last_worst;
function automatic int unsigned log2_bin(input ns_t v);
int i;
begin
log2_bin = 0;
for (i = NS_W-1; i >= 0; i--)
if (v[i]) begin
log2_bin = (i >= BINS) ? BINS-1 : i;
break;
end
end
endfunction
always_ff @(posedge clk or negedge rst_n) begin
int b;
if (!rst_n) begin
for (b = 0; b < BINS; b++) bin[b] <= '0;
p50_ns <= '0; p999_ns <= '0; p99999_ns <= '0;
worst_seen_ns <= '0; last_worst <= '0;
n_samples <= '0; c_missed_deadline <= '0;
tail_still_growing <= 1'b0; highest_occupied_bin_ns <= '0;
end else begin
if (sample_valid) begin
bin[log2_bin(latency_ns)] <= bin[log2_bin(latency_ns)] + 1'b1;
n_samples <= n_samples + 1'b1;
if (latency_ns > worst_seen_ns) worst_seen_ns <= latency_ns;
if (latency_ns > deadline_ns)
c_missed_deadline <= c_missed_deadline + 1;
end
if (window_tick) begin
automatic logic [CNT_W-1:0] cum;
automatic int i50, i999, i99999, ihigh;
cum = '0; i50 = 0; i999 = 0; i99999 = 0; ihigh = 0;
for (b = 0; b < BINS; b++) begin
cum = cum + bin[b];
if (cum < (n_samples >> 1)) i50 = b + 1;
if (cum < (n_samples - n_samples/1000)) i999 = b + 1;
if (cum < (n_samples - n_samples/100000)) i99999 = b + 1;
if (bin[b] != 0) ihigh = b;
end
p50_ns <= ns_t'(32'd1 << i50);
p999_ns <= ns_t'(32'd1 << i999);
p99999_ns <= ns_t'(32'd1 << i99999);
highest_occupied_bin_ns <= ns_t'(32'd1 << ihigh);
// The finding that matters: did the maximum move? A tail that
// is still growing after millions of samples has not been
// characterised -- section 13.
tail_still_growing <= (worst_seen_ns > last_worst);
last_worst <= worst_seen_ns;
end
end
end
endmoduleClassification: a log-bucketed histogram with a growth detector. Twenty counters.
What it teaches: that tail_still_growing is the output that distinguishes a characterised distribution from an uncharacterised one, and it is the one measurement that speaks to boundedness at all. A maximum that stops moving after millions of samples is weak evidence of a bound; one that is still moving is strong evidence of none — and the second is what an unbounded term produces.
And it teaches why the 99.999th percentile is quoted rather than the 99th. A deadline is a hard requirement — Section 2's table — so the interesting question is about one frame in 10⁵ or 10⁶, not one in a hundred. At a thousand frames per second, a 99.999th-percentile event occurs about once a minute, which is frequent enough to be measured and far too frequent for a controller to tolerate.
Deliberately simplified: the percentiles are recomputed by a full scan on every window tick, which is twenty iterations. A production design exports the bins and lets software do it — the histogram is the valuable part and the percentiles are a convenience, which is Chapter 16.1 §10's argument for the same structure in a different subject.
Production implication: c_missed_deadline is the number an application cares about and it is not a percentile — it is a count of hard failures. A network reporting zero over a month has not demonstrated a bound; it has demonstrated that no failure occurred in that month, which is Section 19's rejected property in operational form. The two outputs together — zero misses and tail_still_growing low — are the strongest empirical statement available, and both are still evidence rather than a guarantee.
13. Latency Is a Distribution and a Bound Is a Tail
Section 12 measures a distribution. This section says why a distribution can never establish a bound, which is the argument Section 19's rejected property gets wrong.
A bound is a statement about every frame, including ones that have not been sent. A measurement is a statement about the frames that were.
| Evidence | Establishes |
|---|---|
| 10⁶ frames, max 87 µs | the max of those 10⁶ frames was 87 µs |
| 10⁹ frames, max 94 µs | the max of those 10⁹ was 94 µs |
| a schedule that admits nothing during the window | a bound |
a shaper limiting a class to X bit/s | a bound |
Rows one and two are the same kind of statement at different sample sizes, and neither becomes the third by growing. A thousand times more samples raised the observed maximum by 8% — which is exactly what a heavy-tailed distribution does, and exactly what would happen if the true support were unbounded.
And the direction of the error is the dangerous one. An observed maximum is always less than or equal to the true worst case, so treating it as a bound produces a budget that is optimistic — and the failure occurs on the frame that exceeded it, which is by construction the one nobody has seen.
Two properties of the interference term make this worse than a generic sampling problem.
First, its distribution has no upper support at all — Section 8's 1/(1−u) divergence. So there is no value the maximum converges to, and any observation period produces a maximum that a longer one will exceed.
Second, the tail is exercised by conditions that are rare and correlated with when it matters. Chapter 14.1's congestion, a backup window, a firmware push, a failover — the events that produce the longest latencies are events that cluster, so a month of quiet operation is not a month of evidence about them.
Which gives the only honest position and it is the one Section 10's calculator encodes:
A latency bound is established by construction — a mechanism that limits arrivals — and verified by measurement. A measurement alone establishes nothing about a bound, however many samples it has, and the strongest empirical statement available is "no miss observed, and the tail has stopped growing", which is still evidence and not a guarantee.
==
14. RTL 7 — Determinism Telemetry
Six numbers, and the useful ones report whether a bound exists rather than what the latency was.
// -----------------------------------------------------------------------
// determinism_telemetry -- reports the path's boundedness and where it
// is lost, alongside the observed distribution.
//
// The two halves must be separate: a measurement says what happened
// and a construction says what can happen, and only the second is a
// bound -- section 13.
// -----------------------------------------------------------------------
module determinism_telemetry
import det_pkg::*;
(
input logic clk,
input logic rst_n,
input logic bound_exists, // section 10
input logic [7:0] first_unbounded_hop,
input ns_t computed_bound_ns,
input ns_t deadline_ns,
input ns_t p999_ns,
input ns_t p99999_ns,
input ns_t worst_seen_ns,
input logic tail_still_growing,
input logic [31:0] c_missed_deadline,
input logic [31:0] n_samples,
input logic window_tick,
output logic bound_meets_deadline,
output logic measurement_consistent, // observed <= computed
output logic evidence_is_weak, // tail growing, or few samples
output logic [15:0] margin_pct,
output logic [7:0] action, // what to do, as an index
output logic [31:0] c_windows
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bound_meets_deadline <= 1'b0; measurement_consistent <= 1'b0;
evidence_is_weak <= 1'b1; margin_pct <= '0;
action <= 8'd0; c_windows <= '0;
end else if (window_tick) begin
// A constructed bound is the only thing that can be compared
// against a deadline.
bound_meets_deadline <= bound_exists && (computed_bound_ns <= deadline_ns);
// A measurement EXCEEDING the computed bound falsifies the
// construction -- a real and useful check, and the only
// direction in which measurement can say anything about a bound.
measurement_consistent <= !bound_exists ||
(worst_seen_ns <= computed_bound_ns);
// Weak evidence: the tail is still moving, or there are too few
// samples for the percentile being quoted.
evidence_is_weak <= tail_still_growing || (n_samples < 32'd1_000_000);
margin_pct <= (deadline_ns == 0) ? 16'd0
: 16'(((32'(deadline_ns) - 32'(computed_bound_ns)) * 100) /
32'(deadline_ns));
// What to do, ranked by what is actionable.
action <= (!bound_exists) ? 8'd1 // bound the hop
: (!measurement_consistent) ? 8'd2 // the model is wrong
: (!bound_meets_deadline) ? 8'd3 // reduce the bound
: (c_missed_deadline != 0) ? 8'd4 // investigate a miss
: (evidence_is_weak) ? 8'd5 // keep measuring
: 8'd6; // nothing
c_windows <= c_windows + 1;
end
end
endmoduleClassification: a comparator between a construction and a measurement, with an action selector.
What it teaches: that measurement_consistent is the one direction in which a measurement can say something about a bound, and it is a falsification rather than a confirmation. An observed latency exceeding the computed bound proves the construction wrong — a missing interferer, a hop whose schedule is not what the model assumed, a queue deeper than configured. An observed latency below it proves nothing, however far below and however many samples.
And it teaches that action ranks by what can be done rather than by severity. A path with no bound has one thing to fix — Section 10's first_unbounded_hop. A path whose bound exceeds its deadline has a different problem entirely — fewer hops, a higher line rate, a smaller MTU — and a path that is fine but under-measured needs only patience. Ranking by severity would put a missed deadline first and leave an engineer no instruction.
Deliberately simplified: evidence_is_weak uses a hard-coded million samples. The correct threshold depends on the percentile being quoted — a 99.999th percentile needs at least 10⁵ samples to have any meaning and 10⁷ to be stable — so a production design derives it from the quoted percentile.
Production implication: margin_pct is computed from the constructed bound and not from the observed maximum, and that is the design decision. A margin computed against a measurement looks generous and is meaningless — Section 13's table, rows one and two. A margin against a construction is a number a safety argument can use, and if it is small the answer is to change the construction rather than to gather more data.
15. RTL 8 — Conformance for a Bounded Path
The monitor checks that the boundedness argument is sound. It cannot check that the bound is met on a frame it has not seen.
// -----------------------------------------------------------------------
// determinism_conformance_monitor -- one bit.
//
// It asserts that every hop bounds its arrivals, that the model's
// assumptions hold, and that no measurement has falsified the
// construction. Section 19's rejected property is the version that
// claims a bound from measurement alone.
// -----------------------------------------------------------------------
module determinism_conformance_monitor
import det_pkg::*;
(
input logic clk,
input logic rst_n,
input logic unbounded_hop_in_path,
input logic measurement_exceeded_bound,
input logic strict_priority_unshaped, // 13.4 s11 with no limit
input logic queue_deeper_than_modelled,
input logic mtu_larger_than_modelled,
input logic cut_through_assumed_unavailable,
input logic clock_unsynchronised, // 16.5's budget, for the gates
input logic bound_claimed_from_samples, // the rejected property, as a check
output logic conformant,
output logic [15:0] fault_vector,
output logic [31:0] c_violations
);
logic v_hop, v_exceed, v_sp, v_queue, v_mtu, v_ct, v_clk, v_claim;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
v_hop <= 1'b0; v_exceed <= 1'b0; v_sp <= 1'b0; v_queue <= 1'b0;
v_mtu <= 1'b0; v_ct <= 1'b0; v_clk <= 1'b0; v_claim <= 1'b0;
c_violations <= '0;
end else begin
// A measurement exceeding the computed bound falsifies the
// construction. It is the most informative fault here.
if (measurement_exceeded_bound) begin v_exceed <= 1'b1; c_violations <= c_violations + 1; end
if (queue_deeper_than_modelled) begin v_queue <= 1'b1; c_violations <= c_violations + 1; end
if (mtu_larger_than_modelled) begin v_mtu <= 1'b1; c_violations <= c_violations + 1; end
if (bound_claimed_from_samples) begin v_claim <= 1'b1; c_violations <= c_violations + 1; end
// Standing properties: the construction's preconditions.
v_hop <= unbounded_hop_in_path;
v_sp <= strict_priority_unshaped;
v_ct <= cut_through_assumed_unavailable;
v_clk <= clock_unsynchronised;
end
end
assign conformant = !(v_hop || v_exceed || v_sp || v_queue ||
v_mtu || v_ct || v_clk || v_claim);
assign fault_vector = {8'b0, v_claim, v_clk, v_ct, v_mtu,
v_queue, v_sp, v_exceed, v_hop};
endmoduleClassification: a sticky aggregator with four construction violations and four standing preconditions.
What it teaches: that mtu_larger_than_modelled is a precondition that gets violated by configuration rather than by traffic, and it invalidates the whole bound. Every bounded term in Section 4 is L_max / R, so enabling Chapter 5.7's jumbo frames on a path with a latency bound multiplies five of the seven terms by six — a 9000-octet MTU takes the bounded subtotal from 37.12 µs to 219 µs per hop. The link still works and the bound is gone.
And it teaches that bound_claimed_from_samples is a fault about a claim rather than about behaviour, which is unusual and deliberate. It fires when a system reports a bound whose only evidence is an observed maximum — Section 13's argument, as a check. A design that cannot detect this in hardware can at least refuse to publish computed_bound_ns when bound_exists is low, which Section 10's calculator does.
Deliberately simplified: clock_unsynchronised is a single bit where Chapter 16.5 §14 produces a budget. The right predicate is that the synchronisation error is small against the guard band — Section 17 — and a production monitor takes the budget and compares.
Production implication: conformant here means the boundedness argument is sound: every hop bounds its arrivals, the model's parameters match the configuration, and no measurement has falsified it. It does not mean the deadline will be met on every frame — that follows from the argument plus the arithmetic, and the arithmetic is bound_meets_deadline in Section 14. Two outputs again, and for the same reason five earlier chapters needed two: one is about this device's reasoning and one is about the world.
16. What Has To Be Added
Three things, and each one closes a specific gap that Sections 6 and 8 opened.
| Added | Closes | Because |
|---|---|---|
| a schedule | the interference term | nothing else transmits during the window |
| a clock | the schedule's meaning | every device must agree when the window is |
| a guard band | the blocking term | a frame in flight cannot be recalled |
The schedule is the mechanism and the other two are what it needs to work.
A gate schedule divides time into a repeating cycle and assigns windows to classes. During a class's window, every other class's gate is shut — so a frame of that class waits for no interference at all, and Section 8's unbounded term becomes zero inside the window and at most one cycle outside it:
| Cycle | Window | Worst wait for the next window |
|---|---|---|
| 1000 µs | 100 µs | 900 µs — bounded |
| 250 µs | 50 µs | 200 µs |
| 125 µs | 20 µs | 105 µs |
Every row is a bound, and it is a configuration parameter rather than a property of the traffic — which is the whole difference from Section 8's table.
The clock is Module 16's, and it is needed because a schedule is a statement about instants. Every device in the path must open and close its gates at the same moments, and "the same" is measured against the synchronisation accuracy Chapter 16.5 §16 assembled: 24.2 ns.
And the guard band is what Chapter 12.6 §8's un-abortable frame forces. A gate that is about to close must stop admitting frames early enough that none is still transmitting when the window ends — because a frame in flight cannot be stopped, and one that overruns into the next window is exactly the interference the schedule removed.
Which is Section 17's arithmetic, and it is where Module 16's nanoseconds become bandwidth.
17. What Standard Ethernet Can and Cannot Promise
| Claim | Status |
|---|---|
| low typical latency | yes — 37.12 µs per hop at 1 Gb/s, bounded terms |
| low jitter, most of the time | usually |
| a bound on the serialisation, propagation and lookup terms | yes |
| a bound on the blocking term | yes — one maximum frame |
| a bound on the queueing term | only the queue's depth — 4194 µs |
| a bound on the interference term | none, at any configuration |
| a latency bound | no |
| the bound with a schedule and a clock | yes — Chapter 17.2 |
Rows five and six are the chapter and rows one and two are why the problem is invisible for years. A network that delivers 50 µs on almost every frame looks deterministic, and its distribution's shape says nothing about its support — Section 13.
And row seven's "no" is categorical rather than a matter of degree. It is not that the bound is large; it is that no statement of the form "every frame arrives within D" is true for any D, because the interference term diverges as the higher-priority utilisation approaches one.
Which makes the guard band's arithmetic the natural place to end, because it is where Module 16's result enters:
| Line rate | MTU term | 2 × sync at 24.2 ns | Guard band | Of a 100 µs window |
|---|---|---|---|---|
| 1 Gb/s | 12.14 µs | 0.048 µs | 12.19 µs | 12.19% |
| 10 Gb/s | 1.21 µs | 0.048 µs | 1.26 µs | 1.26% |
| 100 Gb/s | 0.12 µs | 0.048 µs | 0.17 µs | 0.17% |
And the same table with a poorly synchronised network — 1 µs instead of 24.2 ns:
| Line rate | MTU term | 2 × sync at 1 µs | Guard band | Of a 100 µs window |
|---|---|---|---|---|
| 1 Gb/s | 12.14 µs | 2.00 µs | 14.14 µs | 14.14% |
| 10 Gb/s | 1.21 µs | 2.00 µs | 3.21 µs | 3.21% |
| 100 Gb/s | 0.12 µs | 2.00 µs | 2.12 µs | 2.12% |
Read the 100 Gb/s rows against each other: 0.17% against 2.12%, a factor of twelve, and the difference is entirely the clock. At 1 Gb/s the MTU dominates and the synchronisation barely matters; at 100 Gb/s the MTU term has shrunk by a hundred and the synchronisation term has not shrunk at all, so it becomes the guard band.
Which is Module 16's 24.2 ns converted into the unit Module 17 cares about, and it is the reason the two modules are adjacent: a poorly synchronised fast network wastes bandwidth to stay safe, in proportion to how poorly it is synchronised.
==
18. The Cost of a Bound, Accounted
| Component | Cost | Note |
|---|---|---|
| Section 3's accountant | 7 × 32-bit accumulators | 28 octets, per port |
| Section 5's queue model | two counters and a multiply | — |
| Section 8's interference model | an accumulator and a divide | evaluated per window |
| Section 10's calculator | 16 × 32 bits | 64 octets |
| Section 12's histogram | 20 × 32 bits | 80 octets |
| Section 14's telemetry | ≈40 flops | — |
| total, per port | ≈200 octets | measurement only |
| what a bound actually costs | the guard band | 12.19% at 1 Gb/s, 0.17% at 100 |
The measurement infrastructure is two hundred octets and the bound itself is bandwidth, which is the honest accounting: Module 17's mechanisms cost throughput rather than gates.
And the guard band's composition is what decides where to spend:
| To reduce the guard band | By | Chapter |
|---|---|---|
| shrink the MTU term | preemption — interrupt the frame in flight | Chapter 17.3 |
| shrink the MTU term | a smaller MTU — and lose Chapter 8.3's efficiency | — |
| shrink the sync term | a better clock — Chapter 16.5 | Chapter 16.5 |
| widen the window | a longer cycle — and a worse bound | Chapter 17.2 |
Row one is why Chapter 17.3 exists and it is the largest lever at every line rate below 100 Gb/s. Preemption reduces the MTU term from a maximum frame to a fragment, which at 1 Gb/s takes the guard band from 12.19 µs to well under a microsecond — an order of magnitude of reclaimed bandwidth, at the cost of fragment framing in the MAC.
And row three matters only at high rate, which is the table's second finding: at 1 Gb/s the clock contributes 0.4% of the guard band and at 100 Gb/s it contributes 28%.
19. Properties Worth Asserting, and One Worth Refusing
The properties divide by term: the bounded five, the two unbounded, the path calculation, the distribution and the preconditions.
Group 1 — the bounded terms.
// P1. Serialisation is exactly L/R -- computable, not measured.
property p_serialisation_is_computed;
@(posedge clk) disable iff (!rst_n)
frame_arrived |=> (acc[T_SERIALISE] ==
ns_t'((32'(frame_octets) * 8000) / 32'(line_rate_mbps)));
endproperty
// P2. Every bounded term is at most its MTU-derived worst case.
property p_bounded_terms_are_bounded;
@(posedge clk) disable iff (!rst_n)
record_valid |-> (record.per_term[T_SERIALISE] <= MTU_NS);
endproperty
// P3. Store-and-forward and serialisation are BOTH present on a
// store-and-forward hop -- 12.6, and it is not double-counting.
property p_both_serialisations_present;
@(posedge clk) disable iff (!rst_n)
(record_valid && !cut_through_available) |->
((record.per_term[T_SERIALISE] != 0) &&
(record.per_term[T_STORE_FWD] != 0));
endproperty
// P4. Cut-through removes the store-and-forward term and keeps the
// serialisation term.
property p_cut_through_removes_one;
@(posedge clk) disable iff (!rst_n)
(record_valid && cut_through_available) |->
(record.per_term[T_STORE_FWD] <= ns_t'(CUT_THROUGH_NS));
endproperty
// P5. A rate mismatch forces store-and-forward whatever the
// configuration says -- 12.6 section 10.
property p_rate_mismatch_forces_store_fwd;
@(posedge clk) disable iff (!rst_n)
(frame_start && cut_through_enabled && rate_mismatch)
|=> (!cut_through_available && $changed(c_forced_store_fwd));
endproperty
// P6. The blocking term is at most one maximum frame including
// preamble and IFG -- a frame in flight cannot be aborted.
property p_blocking_is_one_frame;
@(posedge clk) disable iff (!rst_n)
record_valid |-> (record.per_term[T_BLOCKING] <= ns_t'(MAX_FRAME_WIRE_NS));
endpropertyGroup 2 — the unbounded terms.
// P7. The interference term has no bound. This is asserted as a
// TAUTOLOGY about the module's output, which documents the claim.
property p_interference_has_no_bound;
@(posedge clk) disable iff (!rst_n)
!has_a_bound;
endproperty
// P8. The interference term grows while higher classes are served,
// with no limit on how long that is.
property p_interference_accumulates;
@(posedge clk) disable iff (!rst_n)
(our_frame_waiting && higher_served)
|=> (interference_ns > $past(interference_ns));
endproperty
// P9. At 100% higher-priority utilisation the wait is unbounded, and
// the model says so rather than saturating quietly.
property p_full_utilisation_is_infinite;
@(posedge clk) disable iff (!rst_n)
(eval && (higher_util_x1000 >= 16'd1000)) |=> utilisation_is_one;
endproperty
// P10. The queueing term's only bound is the queue's depth.
property p_queue_bound_is_depth;
@(posedge clk) disable iff (!rst_n)
(wait_at_full_ns == ns_t'(FULL_NS));
endproperty
// P11. And the observed wait never exceeds it.
property p_observed_wait_within_depth;
@(posedge clk) disable iff (!rst_n)
enq |=> (wait_at_current_occupancy_ns <= wait_at_full_ns);
endpropertyGroup 3 — attribution.
// P12. Every frame's terms sum to its total delay -- the attribution
// is complete, not a sample of the causes.
property p_attribution_is_complete;
@(posedge clk) disable iff (!rst_n)
record_valid |-> ((record.depart_ns - record.arrive_ns) ==
record.per_term.sum());
endproperty
// P13. A departure is matched to its own arrival by tag, never by
// order -- 16.3 section 13's argument, in a new subject.
property p_matched_by_tag;
@(posedge clk) disable iff (!rst_n)
record_valid |-> (record.frame_tag == $past(depart_tag));
endproperty
// P14. unbounded_dominates is exactly the comparison it claims.
property p_dominance_definition;
@(posedge clk) disable iff (!rst_n)
record_valid |=> (unbounded_dominates == (unbounded_total_ns > bounded_total_ns));
endproperty
// P15. The five bounded terms are classified as bounded and the two
// others are not. The classification is the chapter's content.
property p_classification_is_correct;
@(posedge clk) disable iff (!rst_n)
(is_bounded(T_BLOCKING) && !is_bounded(T_INTERFERE) && !is_bounded(T_QUEUE));
endpropertyGroup 4 — the path calculation.
// P16. A bound exists only if EVERY hop bounds its arrivals. One
// unscheduled hop removes it for the whole path.
property p_bound_needs_every_hop;
@(posedge clk) disable iff (!rst_n)
bound_exists |-> ((hop_has_schedule | hop_has_shaper) == '1);
endproperty
// P17. And the total is published only when a bound exists, so a
// partial sum cannot be mistaken for a guarantee.
property p_no_total_without_a_bound;
@(posedge clk) disable iff (!rst_n)
!bound_exists |-> (worst_case_ns == '0);
endproperty
// P18. first_unbounded_hop names the lowest-numbered offending hop,
// so it is a work item rather than a verdict.
property p_first_unbounded_is_lowest;
@(posedge clk) disable iff (!rst_n)
(!bound_exists) |-> (!hop_has_schedule[first_unbounded_hop] &&
!hop_has_shaper[first_unbounded_hop]);
endproperty
// P19. The bounded sum is additive across hops.
property p_bounded_sum_is_additive;
@(posedge clk) disable iff (!rst_n)
eval |=> (bounded_sum_ns == sum_over_hops(per_hop_bounded_ns, n_hops));
endpropertyGroup 5 — the distribution.
// P20. Every sample lands in exactly one bin.
property p_histogram_is_a_partition;
@(posedge clk) disable iff (!rst_n)
sample_valid |=> (bin.sum() == n_samples);
endproperty
// P21. Percentiles are ordered.
property p_percentiles_ordered;
@(posedge clk) disable iff (!rst_n)
window_tick |=> ((p50_ns <= p999_ns) && (p999_ns <= p99999_ns) &&
(p99999_ns <= worst_seen_ns));
endproperty
// P22. worst_seen_ns is monotonic -- it is a high-water mark.
property p_worst_is_monotonic;
@(posedge clk) disable iff (!rst_n)
worst_seen_ns >= $past(worst_seen_ns);
endproperty
// P23. tail_still_growing reports whether the maximum moved this
// window -- the one measurement that speaks to boundedness.
property p_tail_growth_definition;
@(posedge clk) disable iff (!rst_n)
window_tick |=> (tail_still_growing == (worst_seen_ns > $past(last_worst)));
endproperty
// P24. A measurement EXCEEDING the computed bound falsifies the
// construction -- the only direction in which measurement informs a
// bound at all.
property p_measurement_can_falsify;
@(posedge clk) disable iff (!rst_n)
(bound_exists && (worst_seen_ns > computed_bound_ns))
|=> !measurement_consistent;
endpropertyGroup 6 — the preconditions.
// P25. Standing property: no hop in the path leaves its arrivals
// unbounded.
property p_no_unbounded_hop;
@(posedge clk) disable iff (!rst_n)
!unbounded_hop_in_path;
endproperty
// P26. Standing property: the modelled MTU matches the configured
// one. Jumbo frames multiply five of the seven terms by six.
property p_mtu_matches_model;
@(posedge clk) disable iff (!rst_n)
!mtu_larger_than_modelled;
endproperty
// P27. And the modelled queue depth matches the configured one.
property p_queue_depth_matches_model;
@(posedge clk) disable iff (!rst_n)
!queue_deeper_than_modelled;
endproperty
// P28. Standing property: the clock is synchronised well enough for
// the guard band -- 16.5's budget.
property p_clock_is_adequate;
@(posedge clk) disable iff (!rst_n)
!clock_unsynchronised;
endproperty
// P29. Standing property: nobody has claimed a bound from samples.
property p_no_bound_from_samples;
@(posedge clk) disable iff (!rst_n)
!bound_claimed_from_samples;
endproperty
// P30. bound_meets_deadline compares the CONSTRUCTED bound against
// the deadline, never the observed maximum.
property p_deadline_uses_construction;
@(posedge clk) disable iff (!rst_n)
bound_meets_deadline |-> (bound_exists && (computed_bound_ns <= deadline_ns));
endproperty
// P31. conformant is exactly the conjunction it claims.
property p_conformant_definition;
@(posedge clk) disable iff (!rst_n)
conformant <-> (fault_vector == 16'h0000);
endpropertyP7 asserts that a term has no bound, P16 makes the path's bound a conjunction over hops, P24 is the only way a measurement informs a bound, and P30 keeps the deadline comparison honest. None of them claims a latency bound from observation — which is the property this chapter refuses, and it is the one most likely to be found in a real system's test report.
20. Verification Scenarios
Seventy scenarios. The important ones show a network meeting its deadline on ten million frames and having no bound.
The bounded terms
| # | Scenario | Expected |
|---|---|---|
| 1 | 1518-octet frame, 1 Gb/s | serialisation 12.14 µs |
| 2 | 64-octet frame | serialisation 0.51 µs |
| 3 | Worst case for a budget | the MTU, not the typical size |
| 4 | 100 m copper | propagation 0.50 µs |
| 5 | 2 km fibre | 10 µs |
| 6 | Lookup and fabric | 0.028 µs — Chapter 12.1 §12 |
| 7 | Store-and-forward, 1518 octets | 12.14 µs |
| 8 | Cut-through, 64-octet commit | 0.51 µs — a 24× reduction |
| 9 | Same, on the two unbounded terms | no change |
| 10 | 10 Gb/s → 1 Gb/s hop, cut-through enabled | forced store-and-forward |
| 11 | Blocking, max frame in flight | 12.30 µs including preamble and IFG |
| 12 | Bounded subtotal, 1 Gb/s | 37.12 µs |
| 13 | 10 Gb/s | 3.71 µs |
| 14 | 100 Gb/s | 0.87 µs |
| 15 | Jumbo frames, 9000 octets | 219 µs per hop — the bound is gone |
The queueing term
| # | Scenario | Expected |
|---|---|---|
| 16 | 4096-cell queue at 1 Gb/s | full-queue wait 4194 µs |
| 17 | Same at 10 Gb/s | 419 µs |
| 18 | Same at 100 Gb/s | 41.9 µs |
| 19 | Against a 1 ms deadline | missed by a factor of four |
| 20 | Shrink the queue tenfold | 419 µs — and Chapter 14.1 §6's absorption cut by ten |
| 21 | Five hops, full queues | 21 ms |
| 22 | The queue refills while a frame waits | the depth bound does not hold |
| 23 | Observed wait on a quiet network | a few hundred µs — not a bound |
The interference term
| # | Scenario | Expected |
|---|---|---|
| 24 | Higher-priority utilisation 10% | expected wait 13.5 µs |
| 25 | 50% | 24.3 µs |
| 26 | 90% | 121 µs |
| 27 | 99% | 1214 µs |
| 28 | 100% | infinite — utilisation_is_one |
| 29 | has_a_bound at any configuration | low |
| 30 | A new high-priority flow added | the curve is flat until it is not |
| 31 | Strict priority with no shaper | strict_priority_unshaped |
| 32 | A credit-based shaper added | bounded by rate × interval |
| 33 | A gate schedule added | zero inside the window |
Across N hops
| # | Scenario | Expected |
|---|---|---|
| 34 | 1 hop, bounded terms, 1 Gb/s | 37.1 µs |
| 35 | 5 hops | 185.6 µs |
| 36 | 10 hops | 371.2 µs |
| 37 | 5 hops at 100 Gb/s | 4.35 µs |
| 38 | 5 hops plus one full queue | 4380 µs — misses a 1 ms deadline |
| 39 | Interferers differ at each hop | delays accumulate — no correlation |
| 40 | A frame delayed at hop 1 | arrives at hop 2 at a different time |
| 41 | Nine hops scheduled, one not | bound_exists low |
| 42 | Same | first_unbounded_hop names it |
| 43 | worst_case_ns with no bound | zero — deliberately not published |
The distribution
| # | Scenario | Expected |
|---|---|---|
| 44 | 10⁶ frames, max 87 µs | a fact about 10⁶ frames |
| 45 | 10⁹ frames | max 94 µs — 8% higher |
| 46 | Extrapolating 44 to a bound | the rejected property |
| 47 | tail_still_growing after 10⁶ | high — uncharacterised |
| 48 | After 10⁹ on a scheduled path | low |
| 49 | 99.999th percentile at 1000 frames/s | fires about once a minute |
| 50 | 99th percentile instead | the wrong question for a hard deadline |
| 51 | c_missed_deadline = 0 over a month | no failure occurred; not a bound |
| 52 | Observed max exceeds the computed bound | measurement_consistent low — the model is wrong |
| 53 | Observed max below it | proves nothing |
Telemetry and preconditions
| # | Scenario | Expected |
|---|---|---|
| 54 | No bound | action = 1 — bound the hop |
| 55 | Measurement exceeds the bound | action = 2 — the model is wrong |
| 56 | Bound exceeds the deadline | action = 3 — reduce the bound |
| 57 | A miss with a valid bound | action = 4 |
| 58 | Tail growing, few samples | action = 5 — keep measuring |
| 59 | margin_pct from the computed bound | usable in a safety argument |
| 60 | Same from the observed maximum | generous and meaningless |
| 61 | MTU raised to 9000 after commissioning | mtu_larger_than_modelled |
| 62 | Queue depth raised | queue_deeper_than_modelled |
| 63 | Clock unsynchronised | the gates disagree — clock_unsynchronised |
The guard band
| # | Scenario | Expected |
|---|---|---|
| 64 | 1 Gb/s, sync 24.2 ns | 12.19 µs — 12.19% of a 100 µs window |
| 65 | 10 Gb/s | 1.26 µs — 1.26% |
| 66 | 100 Gb/s | 0.17 µs — 0.17% |
| 67 | 100 Gb/s, sync 1 µs | 2.12 µs — 2.12%, a factor of 12 |
| 68 | 1 Gb/s, sync 1 µs | 14.14 µs — 14.14%, a factor of 1.16 |
| 69 | The clock's share at 1 Gb/s | 0.4% |
| 70 | The clock's share at 100 Gb/s | 28% |
The directed test random stimulus will not produce
Random traffic will not exercise the unbounded terms' tails, and that is the whole finding. The conditions that produce them — a higher-priority class near saturation, a queue filled by a burst, a topology where interferers join mid-path — are configurations, not traffic distributions, and a random generator producing uniform load visits them with probability that falls exponentially in their depth. And the point to demonstrate is that a network passing every deadline has no bound, which requires running a benign case and an adversarial one and comparing what the instruments say rather than what the latencies were.
Setup: a five-hop path at 1 Gb/s, 100 m per hop, store-and-forward, Chapter 13.4 §11's strict-priority scheduler with eight classes, Chapter 14.1's 4096-cell queues. A measured flow in class 2 with a 1 ms deadline. Full attribution per hop via Section 3.
Stimulus, four runs. Run A — benign: background load 10%, spread across classes 0 and 1. 10⁷ frames. Run B — long benign: the same, 10⁹ frames. Run C — adversarial: class 7 driven to 99% utilisation for 200 ms once per hour. Run D — scheduled: Run C's traffic with a gate schedule at every hop, 1000 µs cycle, 100 µs window for class 2.
Oracle:
| # | Observable | A — 10⁷ benign | B — 10⁹ benign | C — adversarial | D — scheduled |
|---|---|---|---|---|---|
| 1 | p50_ns | ≈190 µs | ≈190 µs | ≈190 µs | ≈190 µs |
| 2 | p999_ns | ≈260 µs | ≈260 µs | ≈280 µs | ≈260 µs |
| 3 | worst_seen_ns | ≈310 µs | ≈420 µs | ≈6100 µs | ≈900 µs |
| 4 | c_missed_deadline | 0 | 0 | ≈200 per hour | 0 |
| 5 | tail_still_growing | high | high | high | low |
| 6 | bound_exists | low | low | low | high |
| 7 | first_unbounded_hop | 0 | 0 | 0 | — |
| 8 | worst_case_ns | 0 — not published | 0 | 0 | ≈1085 µs |
| 9 | bound_meets_deadline | low | low | low | low — 1085 > 1000 |
| 10 | measurement_consistent | vacuous | vacuous | vacuous | high |
| 11 | unbounded_dominates, typical frame | low | low | high | low |
| 12 | action | 1 | 1 | 1 | 3 |
| 13 | conformant | low | low | low | low |
| 14 | rerun D with a 250 µs cycle | — | — | — | worst_case_ns ≈385 µs, meets it |
| 15 | rerun D with jumbo frames | — | — | — | mtu_larger_than_modelled |
Rows 3, 4 and 6 together are the finding. Runs A and B miss no deadline at all — ten million and a billion frames, zero failures — and bound_exists is low in both, because no hop bounds its arrivals. Run C is the same network on a bad hour: 6.1 ms worst case and two hundred misses. Nothing about the network changed.
Row 3's A-against-B comparison is Section 13's table measured: a thousand times the samples raised the observed maximum from 310 to 420 µs — 35% — and row 5 says the tail was still growing in both.
Row 8 is Section 10's refusal doing its job. worst_case_ns is published only in Run D, so a consumer cannot read a partial sum as a guarantee — and row 9 shows the published bound failing its deadline, which is a much more useful outcome than a passing measurement: row 14 fixes it by shortening the cycle, which is a configuration change with a computable effect.
And row 12's action is 1 in three runs and 3 in the fourth — bound the hop against reduce the bound — which is the difference between a network that cannot be analysed and one that can.
21. Debugging a Latency Bound
Five questions, and the first is the one that decides whether the other four are worth asking.
Step 1 — does a bound exist at all? bound_exists and first_unbounded_hop. A path with one unscheduled, unshaped hop has no bound, and every latency measurement taken on it is a sample from a distribution with no upper support. This is a configuration question, answerable without traffic, and it must come first — measuring an unbounded quantity produces numbers that look like answers.
Step 2 — does the bound meet the deadline? computed_bound_ns against deadline_ns, and margin_pct. If the bound exists and exceeds the deadline, the fix is structural — a shorter schedule cycle, fewer hops, a higher line rate or a smaller MTU — and Section 20's row 14 shows a cycle change moving a 1085 µs bound to 385 µs.
Step 3 — has a measurement falsified the model? measurement_consistent. An observed latency above the computed bound proves the construction wrong — a hop whose schedule is not what the model assumed, a queue deeper than configured, an MTU raised after commissioning. This is the only direction in which measurement informs a bound, and it is a strong signal when it fires.
Step 4 — where is the time going? Section 3's per-term attribution and unbounded_dominates. A frame whose delay is mostly bounded terms is repeatable; one dominated by interference or queueing is not — and the attribution says which of the two, which decides between a shaper and a schedule.
Step 5 — is the evidence strong enough to report? tail_still_growing and the sample count. A month with no misses and a growing tail is not a characterisation, and quoting a 99.999th percentile from 10⁴ samples is quoting noise. Section 20's rows 3 and 5: a thousand times the samples raised the maximum by 35% and the tail was still growing.
And the finding that ends an investigation: bound_exists high, bound_meets_deadline high, measurement_consistent high, tail_still_growing low, and c_missed_deadline zero. That is a path whose bound is constructed, whose construction has not been falsified, and whose measurements agree — which is as strong a statement as this subject admits.
22. Common Misconceptions
1 — "Our latency is 50 µs, so we are deterministic."
The wrong model: a small latency is a bounded one.
What it costs: Section 20's Runs A and C — the same network, ten million frames with zero misses, then two hundred misses in one bad hour. Nothing changed except the background traffic.
The corrected model: determinism is about the support of a distribution, not its centre. A network delivering 50 µs on 99.999% of frames and 4 ms on the rest is low-latency, low-jitter and useless to a controller — because the deadline is missed on the frames that matter and there is no way to know which those will be.
2 — "Cut-through gives us determinism."
The wrong model: the store-and-forward delay is the problem.
What it costs: a real 24× reduction on one bounded term and no change to the two unbounded ones. Chapter 12.6's cut-through takes the store-and-forward term from 12.14 µs to 0.51 µs at 1 Gb/s — worth having — and has_a_bound stays low.
The corrected model: cut-through buys latency, not a bound. And it is not always available: Chapter 12.6 §10's rate-matching requirement means a 10 Gb/s to 1 Gb/s hop must store and forward, so a mixed-rate path has the full term at exactly the hops where the frame is slowest.
3 — "Bigger buffers make the network better."
The wrong model: depth is always good.
What it costs: 4194 µs of worst-case latency at 1 Gb/s from a 4096-cell queue — Chapter 14.1 §5's queue, read as a delay. A design that added buffer to reduce drops added four milliseconds to its bound, and the two decisions were made by different people for opposite reasons.
The corrected model: buffer depth trades drops against latency and there is no setting that satisfies both. Chapter 14.1 §14 priced depth as absorption time; this chapter prices the same number as delay — and a deterministic path wants shallow queues plus a mechanism that makes drops unnecessary, which is what a schedule provides.
4 — "We ran ten million frames and none exceeded 87 µs."
The wrong model: a large sample establishes a bound.
What it costs: an optimistic budget that fails on the frame nobody saw. Section 20's rows 3 and 5: a thousand times the samples raised the observed maximum by 35%, and the tail was still growing at both sizes. The interference term has no upper support, so there is no value the maximum converges to.
The corrected model: a bound is established by construction — a mechanism limiting arrivals — and verified by measurement, never the reverse. Measurement's only valid role is falsification: an observation above a computed bound proves the model wrong. One below it proves nothing.
5 — "Strict priority solves it — just mark the critical traffic highest."
The wrong model: the top class is never delayed.
What it costs: it works, and then it does not. The top class is delayed only by the blocking term — one maximum frame — so it is bounded, provided there is exactly one such class and nothing else shares it. Add a second flow to the top class and Chapter 14.1's queueing term is back; add a higher class and Section 8's interference term is back.
The corrected model: strict priority bounds one class if it is kept to one flow, and Chapter 13.4 §11's callout named starvation as the discipline's default — so everything below the top class is unbounded by construction. A schedule bounds every class, which is what a real system needs.
6 — "Better synchronisation is about knowing the time."
The wrong model: Module 16's accuracy matters to applications and not to the network.
What it costs: bandwidth, at high line rates. Section 17: a guard band is the MTU term plus twice the synchronisation error, and at 100 Gb/s the MTU term is 0.12 µs while the sync term at 1 µs of error is 2.0 µs — so a poorly synchronised 100 Gb/s network spends 2.12% of every window on a guard band and a well-synchronised one spends 0.17%.
The corrected model: synchronisation error is converted into wasted bandwidth by the guard band, at a rate set by the line rate. At 1 Gb/s the clock contributes 0.4% of the guard band and at 100 Gb/s it contributes 28% — so the case for Chapter 16.5's 24.2 ns is strongest exactly where the links are fastest.
23. Interview Reasoning
Q1 — Why can't standard switched Ethernet bound latency?
Because two of a hop's seven latency terms have no bound. Five are bounded — serialisation, propagation, lookup, Chapter 12.6's store-and-forward hold and the blocking from a frame already in flight — and they sum to 37.12 µs per hop at 1 Gb/s, which is comfortably inside a millisecond deadline over five hops. The other two are Chapter 13.4 §11's strict-priority interference, bounded by nothing, and Chapter 14.1's queueing, bounded only by the queue's depth — 4194 µs at 1 Gb/s. A bound is a conjunction over terms and fails on its worst member.
Q2 — Enumerate the terms and price them.
At 1 Gb/s over 100 m: serialisation 12.14 µs, propagation 0.50, lookup and fabric 0.03, store-and-forward 12.14, blocking 12.30 — subtotal 37.12 µs. Serialisation and store-and-forward are both L/R and both real: a store-and-forward switch receives the whole frame before forwarding any of it. The blocking term is a full maximum frame including preamble and interframe gap, because Chapter 12.6 §8 established that a frame in flight cannot be aborted. Then interference and queueing, which are accumulations rather than intervals and have no bound.
Q3 — The queueing term has a bound. Why call it unbounded?
Because the bound is four orders of magnitude too large, the mechanism refills, and the parameter will not be reduced. Chapter 14.1's 4096-cell queue drains in 4194 µs at 1 Gb/s against millisecond deadlines. The depth × drain bound assumes the frame waits only for what was there when it arrived — false under any scheduler that can serve a later arrival first. And the depth exists to avoid drops — Chapter 14.1 §8's one congested port starving twenty-three — so shrinking it undoes that chapter's work.
Q4 — Why is the interference term worse than that?
Because it has no bound of any size. A strict-priority scheduler serves a higher class whenever one is ready, so a lower-priority frame waits for every higher-priority arrival and nothing limits how many there are. The expected wait is L/(R(1−u)): 13.5 µs at 10% higher-priority utilisation, 121 µs at 90%, 1214 µs at 99%, and infinite at 100%. The curve is flat until it is not, which is why a network that behaved for years fails abruptly when a high-priority flow is added.
Q5 — What has to be added, and why three things?
A schedule, a clock and a guard band. The schedule bounds the arrivals by shutting every other class's gate during a window — so interference is zero inside the window and at most one cycle outside it, and 900 µs on a 1000 µs cycle is a bound because it is a configuration parameter. The clock is needed because a schedule is a statement about instants and every device must agree when the windows are — Module 16's 24.2 ns. And the guard band covers Chapter 12.6 §8's un-abortable frame: a gate must stop admitting early enough that nothing is still transmitting, which is the MTU term plus twice the synchronisation error.
Q6 — Why can't a measurement establish a latency bound?
Because a bound is a statement about every frame, including ones not yet sent, and a measurement is a statement about the ones that were. Ten million frames with a maximum of 87 µs and a billion with 94 µs are the same kind of statement at different sample sizes, and neither becomes a bound by growing. The interference term has no upper support, so there is no value the maximum converges to; the tail is exercised by rare, correlated conditions, so quiet operation is not evidence about them; and the error is one-sided, so the mistake is always optimistic. Measurement's valid role is falsification — an observation above a computed bound proves the construction wrong.
24. Understanding Check
25. What's Next
This chapter established a requirement and ruled out an answer.
The requirement: every term of a frame's latency must have a bound. Five of the seven do, and they sum to 37.12 µs per hop at 1 Gb/s — an entirely workable number. Two do not, and no queue sizing, line rate, buffer policy or forwarding discipline gives them one, because both are bounded only by limiting what arrives and no switch controls that.
What must be added is a schedule, a clock and a guard band, and Module 17's remaining chapters build the first and the third.
Chapter 17.2 — Time-Aware Shaping (802.1Qbv) builds the gate schedule: the gate-control list, the cycle and its windows, and the guard band this chapter derived. It is where Section 8's unbounded interference term becomes zero inside a window and at most one cycle outside it — a number set by configuration rather than by traffic.
And Chapter 17.3 — Frame Preemption (802.1Qbu / 802.3br) attacks the guard band's dominant component. Section 18's table: at 1 Gb/s the MTU term is 12.14 µs of a 12.19 µs guard band, which is 12.19% of a 100 µs window spent on the possibility of one maximum frame. Preemption interrupts that frame and the term becomes a fragment — an order of magnitude of reclaimed bandwidth, at the cost of fragment framing, a second MAC state machine, and a new class of partial frame for Chapter 6.3's checker to handle.
One thread runs from Module 16 straight through both. Chapter 16.5 assembled a 24.2 ns budget and called it the module's floor. Section 17 converted it into bandwidth — 0.048 µs of guard band, 0.4% of the total at 1 Gb/s and 28% at 100 — which is the form the next two chapters need it in.
And it inverts the usual reason for wanting a better clock. Module 16 pursued accuracy because applications need to know what time it is. Module 17 wants it because a poorly synchronised network must waste bandwidth to stay safe — and the waste is proportional to the disagreement.
Continue learning
Related tutorials
- Related topic
Time-Aware Shaping (802.1Qbv)
A schedule turns an unbounded interference term into a configuration parameter, and then spends up to 19.5% of the cycle on a guard band to stay safe.
- Related topic
One Frame, End to End
A frame's journey down the stack and back up the other side, stage by stage. The transmit path decides and the receive path must discover — at four layers, not one — and that asymmetry is why the receive half of every Ethernet design is the larger, later and buggier one.
- Related topic
Forward Error Correction
FEC converts a gradual degradation into a cliff and hides the gradient behind it. The pre-correction error rate is the link's health metric and gives months of warning; the corrected output reads zero until the moment it collapses, and can be silently wrong when a decoder miscorrects.
- Related topic
Serialization Delay
A frame's size divided by the line rate — trivial arithmetic whose significance changes by four orders of magnitude, inverting latency budgets so that at 100 Gb/s one metre of cable outweighs an entire minimum-size frame.
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.
