Skip to content
VLSI Mentor

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.

PropertyStatementIs standard Ethernet this?
low latencythe typical delay is smallyes — microseconds
low jitterthe delay varies littleusually
bounded latencythe delay never exceeds Dno

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:

ApplicationDeadlineConsequence of a miss
a motion controller's update~1 ms, harda machined part out of tolerance
a protective relay's trip~4 ms, hardequipment damage
an automotive brake-by-wire command~10 ms, hard
an audio stream's sample~2 ms, hardan audible dropout
a video frame~33 ms, softa visible glitch
a file transfernone

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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.

TermFormulaAt 1 Gb/sBounded by
serialisationChapter 8.1L / R12.14 µsthe MTU
propagationChapter 8.2d / c0.50 µsthe cable
lookup and fabricChapter 12.1 §12fixed0.03 µsthe design
store-and-forwardChapter 12.6L / R12.14 µsthe MTU
blocking — a frame already transmittingL_max / R12.30 µsthe MTU
interferenceChapter 13.4 §11higher-priority arrivalsnothing
queueingChapter 14.1same-priority backlognothing
bounded subtotal37.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 rateBounded per hop
1 Gb/s37.12 µs
10 Gb/s3.71 µs
100 Gb/s0.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.

==

A hop's latency decomposes into seven terms. Five are bounded: serialisation and store-and-forward, each bounded by the maximum transmission unit at 12.14 microseconds per gigabit; propagation, bounded by the cable at 0.5 microseconds for 100 metres; lookup and fabric, bounded by the design at 28 nanoseconds; and blocking by a frame already transmitting, bounded by one maximum frame at 12.30 microseconds because Ethernet cannot abort a frame in progress. Together they sum to 37.12 microseconds per hop, which is comfortably inside a millisecond deadline over five hops. The other two terms have no useful bound: strict-priority interference is limited only by how much higher-priority traffic arrives, and same-priority queueing is limited only by the queue's depth, which is 4194 microseconds for a 4096-cell queue at one gigabit. A latency bound is a conjunction over all seven terms and fails on its worst member.One hop's latencyseven termsFive boundedMTU, cable, design37.12 us per hopat 1 Gb/sFive hops: 185.6 usinside a 1 ms deadlineTwo unboundedinterference and queueingQueue: 4194 usfinite and uselessInterference: nonediverges as u to 112
Figure 1 — seven latency terms, five bounded and two not, and a bound fails on its worst member.

5. RTL 2 — The Queueing Term

The first of the two unbounded terms, modelled so its unboundedness is visible rather than asserted.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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);

endmodule

Classification: 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 onesChapter 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 rateFull-queue wait
1 Gb/s4194 µs
10 Gb/s419 µs
100 Gb/s41.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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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 configurationChapter 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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 utilisationExpected 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.

==

Under a strict-priority scheduler a lower-priority frame waits for every higher-priority arrival, and the expected wait follows L over R times one minus u, where u is the higher-priority utilisation. At ten percent utilisation the wait is 13.5 microseconds, at fifty percent 24.3, at ninety percent 121, at ninety-nine percent 1214, and at one hundred percent it is infinite. The curve is nearly flat across the first half of its range and then rises without limit, which is why a network that behaved acceptably for years fails abruptly when one new high-priority flow is added. Nothing in the switch, the standard or the configuration limits how much higher-priority traffic arrives, so the term has no bound of any size — unlike the queueing term, which is bounded by the queue depth at a uselessly large value.u = 10%13.5 usu = 50%24.3 usu = 90%121 usu = 99%1214 usu = 100%infiniteFlat hereyears of goodbehaviourThen notone new flow12
Figure 2 — strict priority's interference diverges as the higher classes approach saturation, and the curve is flat until it is not.

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 hopN hops
interfering flowsthose sharing this egressthe union across every egress
bound on the countnonenone
correlation between hopsnone — 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:

HopsBounded total at 1 Gb/sat 10 Gb/sat 100 Gb/s
137.1 µs3.71 µs0.87 µs
3111.4 µs11.1 µs2.61 µs
5185.6 µs18.6 µs4.35 µs
7259.8 µs26.0 µs6.09 µs
10371.2 µs37.1 µs8.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 deadlineand 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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.

QueueingChapter 14.1InterferenceChapter 13.4 §11
the wait isbehind same-priority framesbehind higher-priority frames
bounded bythe queue's depthnothing
that bound is4194 µs at 1 Gb/s
so it isfinite and uselessgenuinely unbounded
shrinking the queuehelps, and costs Chapter 14.1 §6's absorptiondoes nothing
what bounds ita limit on same-class arrivalsa 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.

shaperschedule
interference boundrate × intervalzero, inside the window
needs a synchronised clocknoyes
needs the senders to cooperateno — the switch shapesyes — they must transmit in their windows
bound qualitya numberthe 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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.

EvidenceEstablishes
10⁶ frames, max 87 µsthe max of those 10⁶ frames was 87 µs
10⁹ frames, max 94 µsthe max of those 10⁹ was 94 µs
a schedule that admits nothing during the windowa bound
a shaper limiting a class to X bit/sa 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.

==

Two routes to a latency claim. The construction route starts from a mechanism that limits arrivals — a gate schedule or a credit-based shaper at every hop — and deduces a bound that holds for frames not yet sent; it is published only when every hop qualifies, so a partial sum cannot be mistaken for a guarantee. The measurement route observes frames and records a maximum, which is a fact about those frames alone: ten million gave 87 microseconds and a billion gave 94, eight percent higher, because the interference term has no upper support and nothing converges. The measurement's only valid role is falsification — an observation above the computed bound proves the construction wrong, while an observation below it proves nothing at any sample size.Constructiona mechanism limitsarrivalsA deductionholds for unsent framesA boundpublished only if everyhopCompared to thedeadlinea safety argumentMeasurement10^7 frames, max 87 us10^9 frames: 94 usthe tail was stillgrowingCan only falsifyabove the bound proves itwrong12
Figure 3 — a bound comes from construction and a measurement can only falsify it.

14. RTL 7 — Determinism Telemetry

Six numbers, and the useful ones report whether a bound exists rather than what the latency was.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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

endmodule

Classification: 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// 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};

endmodule

Classification: 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.

AddedClosesBecause
a schedulethe interference termnothing else transmits during the window
a clockthe schedule's meaningevery device must agree when the window is
a guard bandthe blocking terma 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:

CycleWindowWorst wait for the next window
1000 µs100 µs900 µs — bounded
250 µs50 µs200 µs
125 µs20 µs105 µ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

ClaimStatus
low typical latencyyes — 37.12 µs per hop at 1 Gb/s, bounded terms
low jitter, most of the timeusually
a bound on the serialisation, propagation and lookup termsyes
a bound on the blocking termyes — one maximum frame
a bound on the queueing termonly the queue's depth — 4194 µs
a bound on the interference termnone, at any configuration
a latency boundno
the bound with a schedule and a clockyes — 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 rateMTU term2 × sync at 24.2 nsGuard bandOf a 100 µs window
1 Gb/s12.14 µs0.048 µs12.19 µs12.19%
10 Gb/s1.21 µs0.048 µs1.26 µs1.26%
100 Gb/s0.12 µs0.048 µs0.17 µs0.17%

And the same table with a poorly synchronised network — 1 µs instead of 24.2 ns:

Line rateMTU term2 × sync at 1 µsGuard bandOf a 100 µs window
1 Gb/s12.14 µs2.00 µs14.14 µs14.14%
10 Gb/s1.21 µs2.00 µs3.21 µs3.21%
100 Gb/s0.12 µs2.00 µs2.12 µs2.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.

==

A gate must stop admitting frames early enough that none is still transmitting when its window closes, because a frame in flight cannot be aborted. That guard band has two components: the longest frame that could be in flight, and twice the devices' disagreement about the time. At one gigabit the MTU term is 12.14 microseconds and the synchronisation term at Chapter 16.5's 24.2 nanosecond budget is 0.048, giving a guard band of 12.19 microseconds — 12.19 percent of a 100 microsecond window, of which the clock contributes 0.4 percent. At one hundred gigabit the MTU term has shrunk a hundredfold to 0.12 microseconds while the synchronisation term has not shrunk at all, so the guard band is 0.17 microseconds and the clock contributes 28 percent of it. A poorly synchronised fast network with one microsecond of error spends 2.12 percent of every window on a guard band instead of 0.17.The guard bandstop admitting earlyMTU terma frame in flight2 x sync error16.5's 24.2 ns1 Gb/s: 12.19 usclock is 0.4% of it100 Gb/s: 0.17 usclock is 28% of it17.3 shrinks theMTU termpreemption16.5 shrinks thesync termand it matters at 100G12
Figure 4 — a guard band is an MTU term plus twice the clock's error, and which one dominates depends on the line rate.

18. The Cost of a Bound, Accounted

ComponentCostNote
Section 3's accountant7 × 32-bit accumulators28 octets, per port
Section 5's queue modeltwo counters and a multiply
Section 8's interference modelan accumulator and a divideevaluated per window
Section 10's calculator16 × 32 bits64 octets
Section 12's histogram20 × 32 bits80 octets
Section 14's telemetry≈40 flops
total, per port≈200 octetsmeasurement only
what a bound actually coststhe guard band12.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 bandByChapter
shrink the MTU termpreemption — interrupt the frame in flightChapter 17.3
shrink the MTU terma smaller MTU — and lose Chapter 8.3's efficiency
shrink the sync terma better clockChapter 16.5Chapter 16.5
widen the windowa longer cycle — and a worse boundChapter 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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));
endproperty

Group 2 — the unbounded terms.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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);
endproperty

Group 3 — attribution.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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));
endproperty

Group 4 — the path calculation.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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));
endproperty

Group 5 — the distribution.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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;
endproperty

Group 6 — the preconditions.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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);
endproperty

P7 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

#ScenarioExpected
11518-octet frame, 1 Gb/sserialisation 12.14 µs
264-octet frameserialisation 0.51 µs
3Worst case for a budgetthe MTU, not the typical size
4100 m copperpropagation 0.50 µs
52 km fibre10 µs
6Lookup and fabric0.028 µsChapter 12.1 §12
7Store-and-forward, 1518 octets12.14 µs
8Cut-through, 64-octet commit0.51 µs — a 24× reduction
9Same, on the two unbounded termsno change
1010 Gb/s → 1 Gb/s hop, cut-through enabledforced store-and-forward
11Blocking, max frame in flight12.30 µs including preamble and IFG
12Bounded subtotal, 1 Gb/s37.12 µs
1310 Gb/s3.71 µs
14100 Gb/s0.87 µs
15Jumbo frames, 9000 octets219 µs per hop — the bound is gone

The queueing term

#ScenarioExpected
164096-cell queue at 1 Gb/sfull-queue wait 4194 µs
17Same at 10 Gb/s419 µs
18Same at 100 Gb/s41.9 µs
19Against a 1 ms deadlinemissed by a factor of four
20Shrink the queue tenfold419 µs — and Chapter 14.1 §6's absorption cut by ten
21Five hops, full queues21 ms
22The queue refills while a frame waitsthe depth bound does not hold
23Observed wait on a quiet networka few hundred µs — not a bound

The interference term

#ScenarioExpected
24Higher-priority utilisation 10%expected wait 13.5 µs
2550%24.3 µs
2690%121 µs
2799%1214 µs
28100%infinite — utilisation_is_one
29has_a_bound at any configurationlow
30A new high-priority flow addedthe curve is flat until it is not
31Strict priority with no shaperstrict_priority_unshaped
32A credit-based shaper addedbounded by rate × interval
33A gate schedule addedzero inside the window

Across N hops

#ScenarioExpected
341 hop, bounded terms, 1 Gb/s37.1 µs
355 hops185.6 µs
3610 hops371.2 µs
375 hops at 100 Gb/s4.35 µs
385 hops plus one full queue4380 µs — misses a 1 ms deadline
39Interferers differ at each hopdelays accumulate — no correlation
40A frame delayed at hop 1arrives at hop 2 at a different time
41Nine hops scheduled, one notbound_exists low
42Samefirst_unbounded_hop names it
43worst_case_ns with no boundzero — deliberately not published

The distribution

#ScenarioExpected
4410⁶ frames, max 87 µsa fact about 10⁶ frames
4510⁹ framesmax 94 µs — 8% higher
46Extrapolating 44 to a boundthe rejected property
47tail_still_growing after 10⁶high — uncharacterised
48After 10⁹ on a scheduled pathlow
4999.999th percentile at 1000 frames/sfires about once a minute
5099th percentile insteadthe wrong question for a hard deadline
51c_missed_deadline = 0 over a monthno failure occurred; not a bound
52Observed max exceeds the computed boundmeasurement_consistent low — the model is wrong
53Observed max below itproves nothing

Telemetry and preconditions

#ScenarioExpected
54No boundaction = 1 — bound the hop
55Measurement exceeds the boundaction = 2 — the model is wrong
56Bound exceeds the deadlineaction = 3 — reduce the bound
57A miss with a valid boundaction = 4
58Tail growing, few samplesaction = 5 — keep measuring
59margin_pct from the computed boundusable in a safety argument
60Same from the observed maximumgenerous and meaningless
61MTU raised to 9000 after commissioningmtu_larger_than_modelled
62Queue depth raisedqueue_deeper_than_modelled
63Clock unsynchronisedthe gates disagree — clock_unsynchronised

The guard band

#ScenarioExpected
641 Gb/s, sync 24.2 ns12.19 µs — 12.19% of a 100 µs window
6510 Gb/s1.26 µs — 1.26%
66100 Gb/s0.17 µs — 0.17%
67100 Gb/s, sync 1 µs2.12 µs — 2.12%, a factor of 12
681 Gb/s, sync 1 µs14.14 µs — 14.14%, a factor of 1.16
69The clock's share at 1 Gb/s0.4%
70The clock's share at 100 Gb/s28%

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:

#ObservableA — 10⁷ benignB — 10⁹ benignC — adversarialD — scheduled
1p50_ns≈190 µs≈190 µs≈190 µs≈190 µs
2p999_ns≈260 µs≈260 µs≈280 µs≈260 µs
3worst_seen_ns≈310 µs≈420 µs≈6100 µs≈900 µs
4c_missed_deadline00≈200 per hour0
5tail_still_growinghighhighhighlow
6bound_existslowlowlowhigh
7first_unbounded_hop000
8worst_case_ns0 — not published00≈1085 µs
9bound_meets_deadlinelowlowlowlow — 1085 > 1000
10measurement_consistentvacuousvacuousvacuoushigh
11unbounded_dominates, typical framelowlowhighlow
12action1113
13conformantlowlowlowlow
14rerun D with a 250 µs cycleworst_case_ns ≈385 µs, meets it
15rerun D with jumbo framesmtu_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 fourthbound the hop against reduce the boundwhich 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 queueChapter 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 µsso 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 dropsChapter 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

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.