Skip to content
VLSI Mentor

Ethernet · Module 17

Frame Preemption (802.1Qbu / 802.3br)

Interrupting a frame in flight takes the guard band from 12.192 to 0.560 microseconds, and gives Chapter 6.3's FCS checker a case it was never designed for.

Chapter 17.2 §18 ended on a table: at every line rate below 100 Gb/s the maximum transmission unit is more than 90% of the guard band, and at 1 Gb/s it is 99.6%.

The band exists because Chapter 12.6 §8 established that a frame in flight cannot be aborted. This chapter is what happens when that stops being true.

Preemption interrupts a transmission mid-frame, sends the urgent frame, and resumes. So the quantity a gate must reserve against is no longer a maximum frame — it is a minimum fragment, because that is the most that can still be in flight when the interruption is decided.

Line rateGuard band withoutWithFactor
1 Gb/s12.192 µs0.560 µs21.8×
10 Gb/s1.263 µs0.100 µs12.7×
25 Gb/s0.534 µs0.069 µs7.8×
100 Gb/s0.170 µs0.054 µs3.2×

And Chapter 17.2 §8's collapse disappears. That chapter's 62.5 µs cycle delivered 0.49% of the link because a 12.5 µs window was almost entirely guard band. With preemption the same window delivers 19.10% — and a 31.25 µs cycle, which was zero, delivers 18.21%.

The cost is a second MAC, a second framing, and a new kind of thing on the wire. A preempted frame arrives as fragments, each terminated by an mCRC that is the bitwise complement of an ordinary FCS — so Chapter 6.3's residue check on a fragment produces 0x38FB2284 instead of 0xC704DD7B, and a receiver must be able to tell a legitimate fragment from a corrupt frame.

1. Scope — What This Chapter Owns

This chapter owns the interruption: the two MACs, the SMD codes and fragment framing, the mCRC and how a receiver distinguishes a fragment from corruption, the preemption decision, reassembly, and the verify/response handshake.

It does not own the schedule. Chapter 17.2 built the gate-control list, the cycle and the guard band's formula. Section 16 recomputes that chapter's tables with the MTU term replaced.

It does not own the requirement. Chapter 17.1 established that two of a hop's seven latency terms are unbounded and that a schedule bounds them. Preemption reduces the price of the schedule and changes none of its guarantees.

And it does not own the CRC. Chapter 6.2 built the generator and Chapter 6.3 built the residue check. Sections 7 to 9 give that checker a second valid residue and a new failure to distinguish, which is the largest change this chapter makes outside its own module.

2. The Term That Dominates the Guard Band

Chapter 17.2 §7's band is MTU/R + 2 × sync_error, and the first term is there for one reason: a frame that has started cannot be stopped.

Chapter 12.6 §8 established it and every chapter since has taken it as given. Chapter 14.2 §5's dead time contains it — the sender finishes its current frame, up to 12.144 µs. Chapter 17.1 §4's blocking term is it. And Chapter 17.2's guard band reserves for it.

Three chapters, three appearances, one fact — and preemption is the first mechanism in the track that changes it.

The substitution is precise. A gate that is about to shut must reserve against whatever can still be transmitting when it does. Without preemption that is a whole maximum frame. With preemption, a transmission in progress can be interrupted, so what remains is the fragment that is already committed — and the standard bounds that at 64 octets, because a shorter one would be a runt.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
guard_band  =  L_uninterruptible / R  +  2 × sync_error
L_uninterruptibleAt 1 Gb/s
without preemption1518 octets12.144 µs
with preemption64 octets0.512 µs

And the sync term is unchanged at 0.048 µs, which changes its share dramatically:

Line rateWithout: MTU's shareWith: fragment's share
1 Gb/s99.6%91.4%
10 Gb/s96.2%51.6%
25 Gb/s90.9%30.1%
100 Gb/s71.5%10.6%

At 100 Gb/s with preemption the guard band is 89.4% synchronisation error, which is Chapter 16.5's budget becoming the dominant term — and the second reason that chapter's 24.2 ns was worth the work.

3. RTL 1 — Two MACs, One PHY

The mechanism's structure: two independent MAC state machines sharing one transmit path, and a multiplexer that can switch between them mid-frame.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// preempt_pkg -- shared types for 802.1Qbu / 802.3br frame preemption.
// -----------------------------------------------------------------------
package preempt_pkg;

  // The Start mData Delimiter replaces the last octet of the preamble,
  // where 5.2's SFD sits. Its value says what follows.
  localparam logic [7:0] SMD_E  = 8'hD5;   // express -- the ordinary SFD
  localparam logic [7:0] SMD_V  = 8'h07;   // verify
  localparam logic [7:0] SMD_R  = 8'h19;   // response

  // Four SMD-S codes and four SMD-C codes, cycled so a receiver can
  // detect a lost fragment rather than silently concatenating.
  localparam logic [7:0] SMD_S [4] = '{8'hE6, 8'h4C, 8'h7F, 8'hB3};
  localparam logic [7:0] SMD_C [4] = '{8'h61, 8'h52, 8'h9E, 8'h2A};

  localparam int MIN_FRAG_OCTETS = 64;    // 7.3's runt threshold
  localparam int MCRC_OCTETS     = 4;

  typedef enum logic [1:0] {
    MAC_EXPRESS,        // never preempted, never fragmented
    MAC_PREEMPTABLE     // may be interrupted at a legal boundary
  } mac_kind_e;

  typedef enum logic [2:0] {
    SMD_KIND_EXPRESS, SMD_KIND_START, SMD_KIND_CONT,
    SMD_KIND_VERIFY,  SMD_KIND_RESPONSE, SMD_KIND_INVALID
  } smd_kind_e;

  // 6.3's residue for a good frame, and its complement for a
  // fragment terminated by an mCRC. Section 8.
  localparam logic [31:0] FCS_RESIDUE  = 32'hC704_DD7B;
  localparam logic [31:0] MCRC_RESIDUE = 32'h38FB_2284;

endpackage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// dual_mac_mux -- arbitrates one PHY interface between an express MAC
// and a preemptable one, and performs the interruption itself.
//
// The express MAC is an ordinary 7.1 transmit path. The preemptable
// one must be able to STOP mid-frame and resume later with its state
// intact -- which is the whole difference.
// -----------------------------------------------------------------------
module dual_mac_mux
  import preempt_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,

  // Express MAC -- 7.1's transmit path, unchanged.
  input  logic       exp_valid,
  input  logic [7:0] exp_octet,
  input  logic       exp_sop,
  input  logic       exp_eop,
  output logic       exp_ready,

  // Preemptable MAC.
  input  logic       pre_valid,
  input  logic [7:0] pre_octet,
  input  logic       pre_sop,
  input  logic       pre_eop,
  output logic       pre_ready,

  // From section 10: may we cut here, and should we.
  input  logic       cut_legal,
  input  logic       cut_wanted,

  input  logic       preemption_enabled,   // section 13's handshake

  output logic       tx_valid,
  output logic [7:0] tx_octet,
  output logic       tx_sop,
  output logic       tx_eop,
  output logic       emit_mcrc,            // terminate this fragment
  output logic       resuming,             // the next SMD is a CONT

  output logic [31:0] c_preemptions,
  output logic [31:0] c_express_frames,
  output logic [31:0] c_fragments,
  output logic       express_waited_ns_valid,
  output logic [31:0] express_wait_ns
);

  typedef enum logic [2:0] {
    S_IDLE, S_EXPRESS, S_PREEMPTABLE, S_CUTTING, S_HELD
  } st_e;
  st_e st;

  logic [31:0] wait_cnt;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st <= S_IDLE; wait_cnt <= '0;
      emit_mcrc <= 1'b0; resuming <= 1'b0;
      c_preemptions <= '0; c_express_frames <= '0; c_fragments <= '0;
      express_waited_ns_valid <= 1'b0; express_wait_ns <= '0;
    end else begin
      emit_mcrc <= 1'b0;
      express_waited_ns_valid <= 1'b0;

      unique case (st)
        S_IDLE: begin
          if (exp_valid && exp_sop) begin
            st <= S_EXPRESS;
            c_express_frames <= c_express_frames + 1;
          end else if (pre_valid && pre_sop) begin
            st <= S_PREEMPTABLE;
          end
        end

        S_EXPRESS: begin
          // Express frames are NEVER preempted. An express frame
          // cannot be interrupted by another express frame either --
          // there is only one express MAC.
          if (exp_valid && exp_eop) st <= S_IDLE;
        end

        S_PREEMPTABLE: begin
          wait_cnt <= wait_cnt + 1;
          if (cut_wanted && preemption_enabled) begin
            if (cut_legal) begin
              // Terminate this fragment with an mCRC and yield.
              emit_mcrc     <= 1'b1;
              st            <= S_CUTTING;
              c_preemptions <= c_preemptions + 1;
              c_fragments   <= c_fragments + 1;
            end
            // If the cut is not legal here -- section 11's 64-octet
            // floor -- we keep transmitting and the express frame
            // waits. That wait is the residual guard band.
          end else if (pre_valid && pre_eop) begin
            st          <= S_IDLE;
            c_fragments <= c_fragments + 1;
          end
        end

        S_CUTTING: begin
          // One cycle to let the mCRC leave, then the express MAC has
          // the wire.
          st                      <= S_EXPRESS;
          express_wait_ns         <= wait_cnt;
          express_waited_ns_valid <= 1'b1;
          wait_cnt                <= '0;
          resuming                <= 1'b1;
        end

        S_HELD: begin
          if (!exp_valid) st <= S_PREEMPTABLE;
        end
      endcase

      if ((st == S_EXPRESS) && exp_valid && exp_eop && resuming) begin
        st       <= S_PREEMPTABLE;
        resuming <= 1'b0;
      end
    end
  end

  assign tx_valid  = (st == S_EXPRESS) ? exp_valid
                   : (st == S_PREEMPTABLE) ? pre_valid : 1'b0;
  assign tx_octet  = (st == S_EXPRESS) ? exp_octet : pre_octet;
  assign tx_sop    = (st == S_EXPRESS) ? exp_sop   : pre_sop;
  assign tx_eop    = (st == S_EXPRESS) ? exp_eop   : pre_eop;
  assign exp_ready = (st == S_EXPRESS) || (st == S_IDLE);
  assign pre_ready = (st == S_PREEMPTABLE);

endmodule

Classification: a five-state arbiter between two transmit sources, with a mid-frame yield. The yield is the novelty.

What it teaches: that express frames are never preempted and never fragmented, which is what makes the mechanism simple enough to build. There is exactly one express MAC, so an express frame cannot be interrupted by another; and a preemptable frame can only be interrupted by an express one. The relation is a strict two-level hierarchy rather than a general priority scheme, and Chapter 13.4 §11's eight classes are mapped onto it by configuration.

And it teaches that a cut which is wanted but not legal costs the express frame a wait, and that wait is the residual guard band. Section 11: a fragment must be at least 64 octets and so must the remainder. A preemptable frame in its first 64 octets cannot be cut, so an express frame arriving then waits up to 0.512 µs at 1 Gb/s — which is exactly the L_uninterruptible term Section 2 substituted.

Deliberately simplified: the preemptable MAC's state is assumed to survive the yield without being described. A real design must hold the frame's remaining octets, its running CRC state — Chapter 6.4's register — and its position, because on resumption the mCRC of the next fragment covers only that fragment while the final FCS covers the whole frame. Section 12's reassembly is the receiver's half of the same problem.

Production implication: express_wait_ns is the measurement that says whether preemption is delivering what it promised. On a working port it is bounded by the minimum-fragment time — 0.512 µs at 1 Gb/s — and a value approaching a full frame time means cuts are being wanted and refused: either preemption is disabled (Section 13's handshake never completed) or the preemptable traffic is all short frames that cannot be cut.

==

A preemption-capable port has two independent MAC state machines sharing one PHY interface. The express MAC is an ordinary Chapter 7.1 transmit path and is never interrupted and never fragmented; there is only one of it, so two express classes contend and the loser waits a full frame. The preemptable MAC can stop mid-frame at a legal boundary, emit an mCRC to terminate the current fragment, yield the wire to the express MAC, and resume afterwards with its remaining octets and running CRC state intact. A cut is legal only when at least 64 octets have been sent and at least 64 remain, so a frame in its first 64 octets cannot be cut and an express frame arriving then waits up to 0.512 microseconds at 1 gigabit — which is exactly the L-uninterruptible term in the guard band formula.Express MACnever interruptedPreemptable MACmay yield mid-frameThe arbiterfive statesCut: emit mCRConly if legalOne PHY4.2's xMII64 octets each side7.3's runt checkExpress waits 0.512uswhen a cut is illegal12
Figure 1 — two MACs share one PHY, and the preemptable one can yield mid-frame.

4. Express and Preemptable

Two MACs, and which class goes to which is a configuration with consequences the configuration does not state.

ExpressPreemptable
may be interruptednoyes
may interruptyesno
fragmented on the wireneverwhen interrupted
terminating CRCFCSmCRC, then FCS on the last fragment
latency, worst caseone fragment's wait — 0.512 µsplus every express frame
reassembly needed at the receivernoyes

The assignment is per traffic class, and Chapter 13.2's PCP is what selects it — so preemption reuses the same three bits Chapter 13.4 §9 mapped to queues and Chapter 14.4 §2 mapped to pause classes. Those three bits now carry four independent meanings, which Chapter 14.4 §22's last callout observed is a shared namespace allocated once and argued about for ever.

And the assignment has one property that is easy to get backwards.

Making a class express does not make it fast. It makes it uninterruptible — so a class marked express is one that cannot be cut, and if it is a bulk class it now blocks every other express class for a full frame time. The express MAC is a single resource with no internal priority, so two express classes contend and the loser waits a whole frame.

ConfigurationExpress classesResult
control only1correct — 0.512 µs worst wait
control and audio2they block each other for a full frame
everything marked express8preemption is disabled in effect
nothing express0nothing to preempt for

Row three is the configuration a cautious operator produces — mark everything important as express — and it removes the mechanism entirely, because preemption requires something preemptable to interrupt.

The right assignment is narrow: one class express, everything else preemptable, and the gain is that the one express class's worst wait falls from 12.144 µs to 0.512.

5. RTL 2 — The SMD Codes and Fragment Framing

A fragment must announce itself, and the announcement has to occupy a position that already exists in the frame.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// smd_framer -- replaces 5.2's SFD with a Start mData Delimiter that
// says what kind of thing follows, and adds a fragment counter to
// continuation fragments.
//
// The SMD occupies the SFD's octet. Nothing is added to the frame's
// length -- which is why preemption does not change 8.3's efficiency.
// -----------------------------------------------------------------------
module smd_framer
  import preempt_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,

  input  logic       start,
  input  smd_kind_e  kind,
  input  logic [1:0] seq,              // cycles 0..3 across fragments
  input  logic [7:0] frag_count,       // continuation fragments only

  output logic       tx_valid,
  output logic [7:0] tx_octet,
  output logic       tx_sop,
  output logic       preamble_done,
  output logic [31:0] c_by_kind [6]
);

  logic [3:0] idx;
  logic       busy;

  // 5.2's preamble is seven 0x55 octets then the SFD. The SMD takes
  // the SFD's place, so the preamble's length is unchanged.
  localparam logic [7:0] PREAMBLE = 8'h55;

  function automatic logic [7:0] smd_of(input smd_kind_e k, input logic [1:0] s);
    case (k)
      SMD_KIND_EXPRESS:  smd_of = SMD_E;
      SMD_KIND_START:    smd_of = SMD_S[s];
      SMD_KIND_CONT:     smd_of = SMD_C[s];
      SMD_KIND_VERIFY:   smd_of = SMD_V;
      SMD_KIND_RESPONSE: smd_of = SMD_R;
      default:           smd_of = SMD_E;
    endcase
  endfunction

  always_ff @(posedge clk or negedge rst_n) begin
    int i;
    if (!rst_n) begin
      idx <= '0; busy <= 1'b0; preamble_done <= 1'b0;
      for (i = 0; i < 6; i++) c_by_kind[i] <= '0;
    end else begin
      preamble_done <= 1'b0;

      if (start && !busy) begin
        busy <= 1'b1; idx <= '0;
        c_by_kind[kind] <= c_by_kind[kind] + 1;
      end else if (busy) begin
        // A continuation fragment carries one extra octet after the
        // SMD: the fragment count, so a receiver can detect a LOST
        // fragment rather than concatenating across a gap.
        if (idx == ((kind == SMD_KIND_CONT) ? 4'd8 : 4'd7)) begin
          busy          <= 1'b0;
          preamble_done <= 1'b1;
          idx           <= '0;
        end else begin
          idx <= idx + 1'b1;
        end
      end
    end
  end

  assign tx_valid = busy;
  assign tx_sop   = busy && (idx == 4'd0);
  assign tx_octet = (idx <  4'd7) ? PREAMBLE
                  : (idx == 4'd7) ? smd_of(kind, seq)
                  :                 frag_count;

endmodule

Classification: a preamble generator with a substituted last octet. No datapath, and its entire content is where the SMD sits.

What it teaches: that the SMD occupies Chapter 5.2's SFD octet rather than being added to the frame, and that is what makes preemption cost nothing in bandwidth. A mechanism that prefixed a new header would add octets to every frame and would show up in Chapter 8.3's efficiency; replacing an octet that was already a constant costs nothing at all. The only addition is the continuation fragment's one-octet count.

And it teaches why there are four SMD-S and four SMD-C codes rather than one of each. The codes cycle across successive fragments, so a receiver that sees C1 followed by C3 knows C2 was lost — where a single code would let it concatenate two fragments from different frames into one plausible-looking frame with a valid FCS. Four values give a detection window of three lost fragments, which is ample for a link whose frame loss is Chapter 6.1's residual error rate.

Deliberately simplified: the SMD values here are illustrative rather than the standard's exact encodings, and a real implementation must use the specified values because they are chosen for Hamming distance from one another and from 0xD5. A code one bit from another is a code that a single-bit error turns into a different valid delimiter — which Section 8's probability calculation assumes does not happen.

Production implication: c_by_kind is the histogram that says what a link is actually carrying, and the ratio of SMD_KIND_CONT to SMD_KIND_START is the average number of times a frame was preempted. A value near zero means preemption is configured and never used — Section 4's "everything express" configuration — and a value above two or three means express traffic is arriving faster than preemptable frames can be sent, which is a traffic-mix problem rather than a preemption problem.

6. Why a Fragment Needs a Different CRC

This is the chapter's central complication and it exists for a reason that is not obvious until it is stated.

A receiver sees octets. When they stop, it must decide what it received.

What arrivedWithout preemptionWith preemption
1518 octets, FCS valida good framea good frame
300 octets, FCS invalida corrupt frame — discarda corrupt frame, or a fragment
40 octetsa runt — Chapter 7.3a runt

Row two is the problem. A fragment is a prefix of a frame, so its last four octets are ordinary frame content rather than an FCS — and Chapter 6.3's residue check on it fails, exactly as it would on a corrupt frame. A receiver applying the ordinary check discards every fragment.

So a fragment must carry its own CRC, and that CRC must be distinguishable from an FCS.

The standard's answer is elegant: the mCRC is the ordinary CRC-32 of the fragment, bitwise complemented.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
FCS   = CRC32(frame)
mCRC  = ~CRC32(fragment)

And Chapter 6.3's residue check — running the CRC over the data and its trailing check value — then produces two different constants:

Terminated byResidue
an FCS0xC704DD7B
an mCRC0x38FB2284

The two are complements of each other, which is not a coincidence: complementing the check value complements the residue. So the same Chapter 6.4 engine, run unchanged, produces a value that says which of the two terminations it saw — and a third value that says neither, which is corruption.

Which gives the receiver a three-way decision where it previously had two:

ResidueMeans
0xC704DD7Ba complete frame, or a final fragment
0x38FB2284a non-final fragment — more is coming
anything elsecorruption — discard

And the cost of that third case is one comparator. The CRC engine is unchanged, the polynomial is unchanged, and Chapter 6.3's checker gains one constant and one output bit.

7. RTL 3 — The mCRC Generator

The transmit half of Section 6, and its subtlety is that a fragment's CRC and the whole frame's CRC are both being computed at once.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// mcrc_generator -- produces both the per-fragment mCRC and the
// whole-frame FCS from one datapath.
//
// The two registers run over different spans:
//   frame_crc    covers every octet of the frame, across all fragments
//   fragment_crc covers only the octets of the CURRENT fragment
// The first terminates the LAST fragment; the second terminates every
// earlier one, complemented.
// -----------------------------------------------------------------------
module mcrc_generator
  import preempt_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,

  input  logic       frame_start,        // a new preemptable frame
  input  logic       fragment_start,     // a new fragment of it
  input  logic       octet_valid,
  input  logic [7:0] octet,

  input  logic       terminate_fragment, // section 10 decided to cut
  input  logic       terminate_frame,    // the frame's last octet

  output logic [31:0] mcrc_out,
  output logic [31:0] fcs_out,
  output logic        emit_valid,
  output logic        emit_is_mcrc,
  output logic [31:0] c_mcrc_emitted,
  output logic [31:0] c_fcs_emitted
);

  // 6.2's CRC-32: polynomial 0x04C11DB7, initial value all ones,
  // reflected input and output, final complement. Written here as a
  // bit-serial update for clarity; 6.4 unrolls it.
  localparam logic [31:0] POLY = 32'h04C1_1DB7;

  logic [31:0] frame_crc, frag_crc;

  function automatic logic [31:0] crc_step(input logic [31:0] c, input logic [7:0] d);
    logic [31:0] r;
    int i;
    begin
      r = c;
      for (i = 7; i >= 0; i--) begin
        if (r[31] ^ d[i]) r = {r[30:0], 1'b0} ^ POLY;
        else              r = {r[30:0], 1'b0};
      end
      crc_step = r;
    end
  endfunction

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      frame_crc <= 32'hFFFF_FFFF;
      frag_crc  <= 32'hFFFF_FFFF;
      emit_valid <= 1'b0; emit_is_mcrc <= 1'b0;
      mcrc_out <= '0; fcs_out <= '0;
      c_mcrc_emitted <= '0; c_fcs_emitted <= '0;
    end else begin
      emit_valid <= 1'b0;

      // The FRAME register is initialised once per frame and runs
      // across every fragment. The FRAGMENT register is reinitialised
      // at every fragment boundary.
      if (frame_start)    frame_crc <= 32'hFFFF_FFFF;
      if (fragment_start) frag_crc  <= 32'hFFFF_FFFF;

      if (octet_valid) begin
        frame_crc <= crc_step(frame_crc, octet);
        frag_crc  <= crc_step(frag_crc,  octet);
      end

      if (terminate_fragment) begin
        // The mCRC is the ordinary CRC of the fragment, COMPLEMENTED
        // a second time -- once by 6.2's final complement and once by
        // the mCRC rule. Section 6's two residues follow from this.
        mcrc_out       <= ~(~frag_crc);
        emit_valid     <= 1'b1;
        emit_is_mcrc   <= 1'b1;
        c_mcrc_emitted <= c_mcrc_emitted + 1;
      end else if (terminate_frame) begin
        fcs_out       <= ~frame_crc;
        emit_valid    <= 1'b1;
        emit_is_mcrc  <= 1'b0;
        c_fcs_emitted <= c_fcs_emitted + 1;
      end
    end
  end

endmodule

Classification: two CRC registers over the same octet stream with different reset points. The duplication is the design.

What it teaches: that a preempted frame needs two CRC accumulations simultaneously, and they cannot be derived from one another. The frame's FCS must cover every octet of the frame across all its fragments; each non-final fragment's mCRC covers only that fragment. Chapter 6.2's CRC is linear, so the frame's CRC is not simply a function of the fragments' CRCs without knowing their lengths — and carrying both registers is cheaper than reconstructing one.

And it teaches that the double complement is deliberate rather than a typo. Chapter 6.2's CRC-32 applies a final complement; the mCRC rule applies another. The two cancel for the value and do not cancel for the residue, because the residue is computed by running the CRC over the data plus the check value — and a complemented check value complements the residue. Which is exactly what Section 6's table needs.

Deliberately simplified: the CRC is bit-serial for readability and a real design uses Chapter 6.4's parallel form. Doubling it doubles that logic — two 32 × 8 GF(2) matrices on a byte-wide datapath, or two 32 × 512 matrices at 100 Gb/s — which is the single largest gate cost this chapter adds.

Production implication: c_mcrc_emitted against c_fcs_emitted is the fragmentation ratio in its most direct form. One FCS per frame and one mCRC per interruption, so the ratio is the average number of cuts per preempted frame. On a healthy link it is well under one — most frames are never interrupted — and a ratio above two means express traffic is arriving faster than preemptable frames complete.

8. RTL 4 — Distinguishing a Fragment From a Corrupt Frame

The receive half, and it is where Chapter 6.3's checker gains a case.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// fragment_classifier -- decides what a received unit is.
//
// Three-way, where 6.3 had two. The SMD says what was CLAIMED and the
// residue says what was DELIVERED, and the classification is the
// conjunction -- which is what keeps a corrupt frame from being taken
// for a fragment.
// -----------------------------------------------------------------------
module fragment_classifier
  import preempt_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        unit_end,
  input  logic [31:0] running_residue,    // 6.3's engine, unchanged
  input  smd_kind_e   smd_kind,
  input  logic [1:0]  smd_seq,
  input  logic [13:0] unit_octets,

  output logic        is_complete_frame,
  output logic        is_nonfinal_fragment,
  output logic        is_corrupt,
  output logic        is_runt,

  output logic [31:0] c_frames,
  output logic [31:0] c_fragments,
  output logic [31:0] c_corrupt,
  output logic [31:0] c_runts,
  output logic [31:0] c_smd_mismatch      // SMD and residue disagree
);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      is_complete_frame <= 1'b0; is_nonfinal_fragment <= 1'b0;
      is_corrupt <= 1'b0; is_runt <= 1'b0;
      c_frames <= '0; c_fragments <= '0; c_corrupt <= '0;
      c_runts <= '0; c_smd_mismatch <= '0;
    end else begin
      is_complete_frame    <= 1'b0;
      is_nonfinal_fragment <= 1'b0;
      is_corrupt           <= 1'b0;
      is_runt              <= 1'b0;

      if (unit_end) begin
        // Gate 0 -- 7.3's runt check, before anything else. A unit
        // shorter than 64 octets is not a fragment however its CRC
        // comes out, because the standard forbids emitting one.
        if (unit_octets < MIN_FRAG_OCTETS) begin
          is_runt <= 1'b1;
          c_runts <= c_runts + 1;
        end
        // A frame or a FINAL fragment: the FCS residue.
        else if (running_residue == FCS_RESIDUE) begin
          if ((smd_kind == SMD_KIND_EXPRESS) ||
              (smd_kind == SMD_KIND_START)   ||
              (smd_kind == SMD_KIND_CONT)) begin
            is_complete_frame <= 1'b1;
            c_frames          <= c_frames + 1;
          end else begin
            // A valid FCS on a VERIFY or RESPONSE unit is a protocol
            // error, not a frame.
            c_smd_mismatch <= c_smd_mismatch + 1;
          end
        end
        // A NON-FINAL fragment: the complemented residue.
        else if (running_residue == MCRC_RESIDUE) begin
          // And the SMD must agree that more is coming. A unit with
          // an mCRC residue and an EXPRESS delimiter is corrupt, not
          // a fragment -- section 9.
          if ((smd_kind == SMD_KIND_START) || (smd_kind == SMD_KIND_CONT)) begin
            is_nonfinal_fragment <= 1'b1;
            c_fragments          <= c_fragments + 1;
          end else begin
            is_corrupt     <= 1'b1;
            c_corrupt      <= c_corrupt + 1;
            c_smd_mismatch <= c_smd_mismatch + 1;
          end
        end
        else begin
          is_corrupt <= 1'b1;
          c_corrupt  <= c_corrupt + 1;
        end
      end
    end
  end

endmodule

Classification: a three-way classifier over one residue and one delimiter. Two comparators where Chapter 6.3 had one.

What it teaches: that the classification is a conjunction of the SMD and the residue, and requiring both is what keeps the error rate where Chapter 6.1 put it. The SMD says what was claimed; the residue says what was delivered. A corrupt frame that happens to produce the mCRC residue is still rejected unless its delimiter also says a fragment is coming — and c_smd_mismatch counts exactly those disagreements.

And it teaches that the runt check must come first. Chapter 7.3's 64-octet minimum applies to fragments too — Section 11's floor exists so that it does — so a 40-octet unit is a runt whatever its residue says. A classifier that checked the residue first would accept a 40-octet corrupt frame that happened to produce the mCRC residue as a legitimate fragment.

Deliberately simplified: the SMD sequence number is captured and not checked. Section 12's reassembly is where it is used — a receiver that sees C1 then C3 has lost C2 — and the classifier deliberately does not do that, because a lost fragment is a reassembly failure rather than a unit failure and the two counters must stay separate.

Production implication: c_smd_mismatch is the counter that catches a link where the two ends disagree about whether preemption is in use. A transmitter sending fragments to a receiver that did not negotiate them produces units whose SMD is unrecognised — and a receiver whose classifier treats an unknown SMD as express sees a stream of corrupt frames. Section 13's handshake exists to prevent exactly this, and this counter is how a design notices that it failed.

==

A fragment is a prefix of a frame, so its last four octets are ordinary content and Chapter 6.3's residue check fails on it exactly as it would on corruption. The standard's answer is that a non-final fragment carries an mCRC, which is the ordinary CRC-32 of the fragment bitwise complemented. Because the residue is computed by running the CRC over the data and its trailing check value, complementing the check value complements the residue: an FCS-terminated unit gives C704DD7B and an mCRC-terminated fragment gives 38FB2284, which are complements of one another. So the same engine, unchanged in polynomial, reflection and final complement, produces a value that says which of the two terminations it saw, and a third value that says neither, which is corruption. The hardware cost is one 32-bit comparator and one extra output bit.6.4's CRC engineunchangedResidue C704DD7Ba frame or final fragmentResidue 38FB2284a non-final fragmentAnything elsecorruptionAND the SMD agreesconjunction, not either7.3e-12corrupt taken forfragmentOne comparatorthe whole hardware cost12
Figure 2 — one CRC engine, two residues, three answers: Chapter 6.3's checker gains a case.

9. What Preemption Costs Chapter 6.3's Checker

Chapter 6.3 built a checker with one constant and a binary answer. Preemption gives it a second constant, a third answer, and an error case it did not have — and the cost is smaller than that sounds.

Chapter 6.3's checkerWith preemption
CRC engineChapter 6.4's, unchangedunchanged
polynomial0x04C11DB7unchanged
residue constantsone — 0xC704DD7Btwo
outputsgood / badframe / fragment / corrupt
additional logicone 32-bit comparator
new failure modea corrupt frame taken for a fragment

Row five is the whole hardware cost: one comparator. The engine, the polynomial, the reflection and the final complement are all identical — because complementing the check value complements the residue, and nothing else about the computation changes.

Row six is the interesting cost, and it is worth pricing rather than worrying about.

For a corrupt frame to be accepted as a fragment, two independent things must happen:

Its residue must equal 0x38FB2284 — a 32-bit coincidence, probability 2⁻³²2.33 × 10⁻¹⁰ for a random corruption.

And its SMD must decode as SMD-S or SMD-C — eight valid codes out of 256 octet values, probability 1/32.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
P(corrupt frame accepted as a fragment)  ≈  (8/256) × 2⁻³²  ≈  7.3 × 10⁻¹²

Against Chapter 6.1's undetected-error requirement that is comfortably inside the budget — the ordinary FCS already admits 2⁻³² of corrupt frames as good, and this adds a case thirty-two times rarer than that.

And there is a third guard that the arithmetic does not capture: the SMD codes are chosen for Hamming distance. A single bit error in 0xD5 — the express delimiter — does not produce a valid SMD-C, so the common single-bit case is excluded by construction rather than by probability.

Which gives the honest summary: preemption costs Chapter 6.3's checker one comparator and one output bit, and costs its error model a term that is two orders of magnitude below the one already there.

The larger cost is elsewhere and Section 7 already paid it: the generator now needs two CRC registers, because a fragment's mCRC and the frame's FCS cover different spans and neither can be derived from the other. On a byte-wide datapath that is two 32 × 8 matrices; at 100 Gb/s it is two 32 × 512 ones, and it is the single largest gate addition in the chapter.

10. RTL 5 — The Preemption Decision

Section 3's arbiter asked whether a cut was legal and wanted. This is what answers it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// preemption_decider -- decides whether the preemptable frame in
// flight may be, and should be, interrupted right now.
//
// Two independent questions with different answers:
//   LEGAL  -- section 11's 64-octet floor, on both sides of the cut
//   WANTED -- an express frame is waiting, or a gate is about to shut
// -----------------------------------------------------------------------
module preemption_decider
  import preempt_pkg::*;
#(
  parameter int LINE_RATE_MBPS = 1000
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        pre_in_flight,
  input  logic [13:0] octets_sent,        // of this fragment
  input  logic [13:0] octets_remaining,   // of the frame

  input  logic        express_pending,
  input  logic [31:0] time_to_gate_close_ns,   // 17.2 section 7
  input  logic        preemption_enabled,

  output logic        cut_legal,
  output logic        cut_wanted,
  output logic [13:0] octets_until_legal,
  output logic [31:0] c_wanted_but_illegal,
  output logic [31:0] c_cut,
  output logic [31:0] worst_illegal_wait_ns
);

  logic [31:0] illegal_wait;

  always_comb begin
    // A cut is legal only if BOTH pieces will be at least 64 octets.
    // The fragment being closed needs 64 including its 4-octet mCRC;
    // the remainder needs 64 including its FCS. Section 11.
    cut_legal = pre_in_flight &&
                (octets_sent      >= 14'(MIN_FRAG_OCTETS)) &&
                (octets_remaining >= 14'(MIN_FRAG_OCTETS));

    octets_until_legal = (octets_sent >= 14'(MIN_FRAG_OCTETS))
                       ? 14'd0
                       : (14'(MIN_FRAG_OCTETS) - octets_sent);

    // Two reasons to want a cut, and the second is why this module
    // belongs to Module 17 rather than to Module 13.
    cut_wanted = preemption_enabled &&
                 (express_pending ||
                  (time_to_gate_close_ns <
                   ((32'(octets_remaining) * 8000) / LINE_RATE_MBPS)));
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_wanted_but_illegal <= '0; c_cut <= '0;
      worst_illegal_wait_ns <= '0; illegal_wait <= '0;
    end else begin
      if (cut_wanted && !cut_legal) begin
        c_wanted_but_illegal <= c_wanted_but_illegal + 1;
        illegal_wait         <= illegal_wait + 1;
        if (illegal_wait > worst_illegal_wait_ns)
          worst_illegal_wait_ns <= illegal_wait;
      end else begin
        illegal_wait <= '0;
      end

      if (cut_wanted && cut_legal) c_cut <= c_cut + 1;
    end
  end

endmodule

Classification: two combinational predicates with a divergence counter. No state machine, and the two predicates are independent.

What it teaches: that the second reason to want a cut has nothing to do with an express frame, and it is the one that ties this chapter to Chapter 17.2. A preemptable frame whose remaining octets will not fit before the gate shuts must be cut anyway — otherwise it overruns the window, which is exactly what Chapter 17.2 §16's overran_the_window fires on. So preemption serves the schedule directly and not only the express class.

And it teaches that octets_until_legal is the residual guard band, expressed in octets. A frame in its first 64 octets cannot be cut, so an express frame arriving then waits — up to 64 octets, which is 0.512 µs at 1 Gb/s, and that is Section 2's L_uninterruptible. The number in the guard-band formula and the number in this comparator are the same number, and a design that used a different minimum in the two places has a guard band that does not cover its own decider.

Deliberately simplified: cut_wanted is evaluated combinationally from express_pending, which assumes the express MAC's readiness is visible without latency. In a real pipeline there are cycles between an express frame becoming ready and the decider seeing it, and those cycles add to the express frame's wait — so a production guard band uses MIN_FRAG + pipeline_depth rather than MIN_FRAG alone.

Production implication: worst_illegal_wait_ns is the measurement that validates the guard band empirically. It should never exceed the minimum-fragment time plus the pipeline depth — 0.512 µs at 1 Gb/s plus a few tens of nanoseconds — and a larger value means either the decider is seeing cut_wanted late or the preemptable traffic is short frames that are never legal to cut. Section 11's table says which.

11. The Minimum Fragment, and Why It Exists

Section 10's cut_legal has a 64-octet floor on both sides of the cut, and both halves of that are forced by chapters written long before preemption.

The fragment being closed must be at least 64 octets because Chapter 7.3's receive path discards anything shorter as a runt — and a runt is discarded silently, so a shorter fragment would vanish and its frame would never reassemble.

And the remainder must be at least 64 octets for the same reason: the continuation fragment is itself a unit on the wire and is subject to the same check.

Which means a frame is preemptible only in a window, and the window closes on short frames:

FrameEarliest cutLatest cutLegal cut points
64 octets640none — not preemptible
128 octets6464exactly one
256 octets64192129
512 octets64448385
1024 octets64960897
1518 octets6414541391

The first two rows are the ones that matter operationally. A link carrying minimum-size preemptable frames cannot be preempted at all — every frame is 64 octets and none has a legal cut point — so an express frame waits a full 0.672 µs including preamble and interframe gap, and the guard band must still cover it.

And that is a genuinely awkward case, because it is the traffic pattern a congested link produces. Chapter 8.3's efficiency argument says small frames are inefficient; a link carrying many of them is a link where preemption buys nothing, and the guard band's saving evaporates.

The honest formula, then, is not MIN_FRAG / R but:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
L_uninterruptible = max(MIN_FRAG, shortest_preemptable_frame_that_cannot_be_cut)

which for a link carrying frames of at least 128 octets is 64, and for one carrying 64-octet frames is 64 anywaybecause a 64-octet frame is itself only 64 octets long. The two cases coincide numerically and differ in cause: one is a fragment's floor and the other is a whole frame that fits under it.

Which is a small mercy: the guard-band formula is correct either way, and the mechanism simply does nothing on short-frame traffic rather than doing something wrong.

==

A cut is legal only if both resulting pieces are at least 64 octets, because Chapter 7.3's runt check applies to fragments as units on the wire and a shorter one is silently discarded, so its frame never reassembles. The remainder is itself a unit and is subject to the same check. Consequently a 64-octet frame has no legal cut point at all, a 128-octet frame has exactly one at octet 64, a 256-octet frame has 129, and a 1518-octet frame has 1391. The uncomfortable consequence is that a link carrying short preemptable frames gains nothing from preemption, and short frames are what a congested link produces — so the guard band's saving is smallest precisely when the schedule is under most pressure, with c_wanted_but_illegal rising and c_cut at zero as the only evidence.64 octetsno cut point128 octetsexactly one256 octets129 points512 octets385 points1518 octets1391 pointsBoth sides >= 647.3's runt checkCongested links send short framesCongested linkssend short…the savingevaporates12
Figure 3 — the 64-octet floor applies to both sides of a cut, and it closes the window on short frames.

12. RTL 6 — The Reassembly State Machine

The receiver's half. It must hold a partial frame across an arbitrary interruption and detect every way the sequence can go wrong.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// reassembly_fsm -- rebuilds a preempted frame from its fragments.
//
// Three failure modes, each with its own counter, and all three
// produce a discarded frame rather than a corrupt one:
//   a lost fragment       -- the SMD sequence skips
//   an interleaved frame  -- an SMD-S arrives mid-reassembly
//   an abandoned frame    -- no continuation ever arrives
// -----------------------------------------------------------------------
module reassembly_fsm
  import preempt_pkg::*;
#(
  parameter int MAX_FRAME   = 1518,
  parameter int TIMEOUT_CYC = 500_000       // 1 ms at 500 MHz
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        unit_valid,
  input  smd_kind_e   smd_kind,
  input  logic [1:0]  smd_seq,
  input  logic [7:0]  frag_count,
  input  logic        is_nonfinal_fragment,
  input  logic        is_complete_frame,
  input  logic [13:0] unit_octets,

  output logic        frame_out_valid,
  output logic [13:0] frame_out_octets,
  output logic        reassembling,

  output logic [31:0] c_reassembled,
  output logic [31:0] c_lost_fragment,
  output logic [31:0] c_interleaved,
  output logic [31:0] c_timed_out,
  output logic [31:0] c_oversize,
  output logic [7:0]  worst_fragments_seen
);

  logic [1:0]  expect_seq;
  logic [7:0]  expect_count;
  logic [13:0] accumulated;
  logic [31:0] age;
  logic [7:0]  frags_this_frame;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      reassembling <= 1'b0; expect_seq <= '0; expect_count <= '0;
      accumulated <= '0; age <= '0; frags_this_frame <= '0;
      frame_out_valid <= 1'b0; frame_out_octets <= '0;
      c_reassembled <= '0; c_lost_fragment <= '0;
      c_interleaved <= '0; c_timed_out <= '0; c_oversize <= '0;
      worst_fragments_seen <= '0;
    end else begin
      frame_out_valid <= 1'b0;

      // A reassembly that never completes must be abandoned, or one
      // lost final fragment stalls the receiver for ever.
      if (reassembling) begin
        age <= age + 1;
        if (age == TIMEOUT_CYC) begin
          reassembling <= 1'b0;
          accumulated  <= '0;
          c_timed_out  <= c_timed_out + 1;
        end
      end

      if (unit_valid) begin
        unique case (smd_kind)
          SMD_KIND_EXPRESS: begin
            // An express frame passes through untouched and does NOT
            // disturb an in-progress reassembly -- the two MACs are
            // independent all the way to the receiver.
            if (is_complete_frame) begin
              frame_out_valid  <= 1'b1;
              frame_out_octets <= unit_octets;
            end
          end

          SMD_KIND_START: begin
            // An SMD-S while already reassembling means the previous
            // frame's continuation never arrived. Discard it.
            if (reassembling) c_interleaved <= c_interleaved + 1;

            reassembling     <= is_nonfinal_fragment;
            accumulated      <= unit_octets;
            expect_seq       <= smd_seq + 2'd1;
            expect_count     <= 8'd1;
            age              <= '0;
            frags_this_frame <= 8'd1;

            // A START that is already complete is an unpreempted
            // frame that happened to be announced as preemptable.
            if (is_complete_frame) begin
              frame_out_valid  <= 1'b1;
              frame_out_octets <= unit_octets;
              c_reassembled    <= c_reassembled + 1;
            end
          end

          SMD_KIND_CONT: begin
            if (!reassembling) begin
              // A continuation with nothing to continue: its START
              // was lost.
              c_lost_fragment <= c_lost_fragment + 1;
            end else if ((smd_seq != expect_seq) ||
                         (frag_count != expect_count)) begin
              // The sequence skipped. Four SMD-C codes give a
              // detection window of three lost fragments -- section 5.
              c_lost_fragment <= c_lost_fragment + 1;
              reassembling    <= 1'b0;
              accumulated     <= '0;
            end else if ((accumulated + unit_octets) > 14'(MAX_FRAME)) begin
              c_oversize   <= c_oversize + 1;
              reassembling <= 1'b0;
              accumulated  <= '0;
            end else begin
              accumulated      <= accumulated + unit_octets;
              expect_seq       <= smd_seq + 2'd1;
              expect_count     <= expect_count + 8'd1;
              age              <= '0;
              frags_this_frame <= frags_this_frame + 8'd1;

              if (is_complete_frame) begin
                reassembling     <= 1'b0;
                frame_out_valid  <= 1'b1;
                frame_out_octets <= accumulated + unit_octets;
                c_reassembled    <= c_reassembled + 1;
                if (frags_this_frame + 8'd1 > worst_fragments_seen)
                  worst_fragments_seen <= frags_this_frame + 8'd1;
              end
            end
          end

          default: ;
        endcase
      end
    end
  end

endmodule

Classification: a sequence-checked accumulator with a timeout. Three distinct failure paths, all leading to a discard.

What it teaches: that an express frame arriving mid-reassembly must not disturb it, and that is what makes the two MACs genuinely independent. The whole point of preemption is that an express frame arrives between two fragments; a receiver that treated any new SMD as ending the current reassembly would discard every preempted frame. The classification in Section 8 keeps them apart, and this module acts on that.

And it teaches why the timeout exists and what it costs. A lost final fragment leaves the receiver reassembling for ever — holding a partial frame and refusing every subsequent continuation — so the state must age out. One millisecond at 1 Gb/s is 125 000 octets of subsequent traffic, all of which reassembles correctly once the stale state clears; without the timeout, one lost fragment stops the preemptable path permanently.

Deliberately simplified: the accumulated octets are counted and not stored, so this module verifies the sequence rather than rebuilding the frame. A real receiver holds the fragments in a buffer of at least one maximum frame — 1518 octets — and the buffer is per port, because two ports can be reassembling simultaneously.

Production implication: worst_fragments_seen is the number that sizes the sequence space. Four SMD-C codes detect up to three consecutive lost fragments; a frame that is legitimately cut more than four times wraps the sequence, and a subsequent loss becomes undetectable. A value approaching four means express traffic is interrupting preemptable frames repeatedly, which is Section 5's ratio seen from the receiving end — and it is a traffic-engineering finding rather than a hardware one.

13. RTL 7 — The Verify/Response Handshake

Preemption cannot be switched on unilaterally, and the reason is that a receiver which does not understand fragments sees corruption.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// verify_response_fsm -- establishes that the link partner understands
// preemption before any fragment is transmitted.
//
// The shape is 15.3's LACP in miniature: a periodic assertion, a
// timeout, and a capability that stays DISABLED until confirmed.
// -----------------------------------------------------------------------
module verify_response_fsm
  import preempt_pkg::*;
#(
  parameter int VERIFY_PERIOD_CYC = 64_000_000,   // 128 ms at 500 MHz
  parameter int VERIFY_LIMIT      = 3
)(
  input  logic       clk,
  input  logic       rst_n,

  input  logic       cfg_preemption_requested,
  input  logic       link_up,

  input  logic       rx_verify,           // an SMD-V arrived
  input  logic       rx_response,         // an SMD-R arrived

  output logic       tx_verify,
  output logic       tx_response,
  output logic       preemption_enabled,
  output logic [2:0] state_out,
  output logic [31:0] c_verify_sent,
  output logic [31:0] c_verify_failed,
  output logic [31:0] c_responses_sent
);

  typedef enum logic [2:0] {
    V_DISABLED, V_INIT, V_VERIFYING, V_SUCCEEDED, V_FAILED
  } vstate_e;
  vstate_e st;

  logic [31:0] timer;
  logic [2:0]  attempts;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st <= V_DISABLED; timer <= '0; attempts <= '0;
      tx_verify <= 1'b0; tx_response <= 1'b0;
      preemption_enabled <= 1'b0;
      c_verify_sent <= '0; c_verify_failed <= '0; c_responses_sent <= '0;
    end else begin
      tx_verify   <= 1'b0;
      tx_response <= 1'b0;

      // Responding is UNCONDITIONAL: a device that understands
      // preemption answers a verify even if it is not requesting it
      // itself. 15.3's passive/passive deadlock is avoided this way.
      if (rx_verify) begin
        tx_response      <= 1'b1;
        c_responses_sent <= c_responses_sent + 1;
      end

      if (!link_up || !cfg_preemption_requested) begin
        st                 <= V_DISABLED;
        preemption_enabled <= 1'b0;
        attempts           <= '0;
      end else begin
        unique case (st)
          V_DISABLED: begin
            st       <= V_INIT;
            timer    <= '0;
            attempts <= '0;
          end

          V_INIT: begin
            st            <= V_VERIFYING;
            tx_verify     <= 1'b1;
            timer         <= '0;
            attempts      <= 3'd1;
            c_verify_sent <= c_verify_sent + 1;
          end

          V_VERIFYING: begin
            timer <= timer + 1;
            if (rx_response) begin
              st                 <= V_SUCCEEDED;
              preemption_enabled <= 1'b1;
            end else if (timer == VERIFY_PERIOD_CYC) begin
              timer <= '0;
              if (attempts == VERIFY_LIMIT) begin
                // The partner does not answer. Preemption stays OFF,
                // which is the safe state: whole frames always work.
                st              <= V_FAILED;
                c_verify_failed <= c_verify_failed + 1;
              end else begin
                attempts      <= attempts + 3'd1;
                tx_verify     <= 1'b1;
                c_verify_sent <= c_verify_sent + 1;
              end
            end
          end

          V_SUCCEEDED: begin
            preemption_enabled <= 1'b1;
          end

          V_FAILED: begin
            preemption_enabled <= 1'b0;
          end
        endcase
      end
    end
  end

  assign state_out = st;

endmodule

Classification: a five-state negotiation with a bounded retry. The unconditional response is the design decision.

What it teaches: that responding is unconditional and requesting is not, which avoids Chapter 15.3 §13's passive/passive deadlock by construction. A device that understands preemption answers every verify whether or not it wants preemption itself; only the requesting side needs configuration. Chapter 15.3's LACP made both sides' activity configurable and produced a legal configuration that never forms an aggregate — this protocol removed that possibility.

And it teaches that failure leaves preemption off, which is the safe direction. A link whose partner does not answer transmits whole frames, which always work — the guard band reverts to 12.192 µs and the schedule's usable fraction drops, but nothing is lost. The opposite default would send fragments to a receiver that classifies them as corruption, producing a link that is up and discards a large fraction of its traffic.

Deliberately simplified: the verify period is a single constant at 128 ms. The standard allows it to be configured, and the trade is the usual one: a shorter period re-establishes preemption faster after a link event and puts more verify frames on a link that may not support them. At 128 ms the cost is 5250 bit/s — 5.25 × 10⁻⁴% of a gigabit link.

Production implication: c_verify_failed is the counter that explains a schedule whose usable fraction is unexpectedly low. A port whose preemption never enabled has Chapter 17.2's full 12.192 µs guard band, so a 12.5 µs window delivers 0.49% instead of 19.10% — a factor of 39 — and the cause is a handshake that never completed on a link that looks perfectly healthy.

14. Why Preemption Must Be Verified Before Use

Section 13's handshake looks like caution. It prevents a specific failure whose symptom points nowhere useful.

A preempting transmitter sends units whose delimiter is SMD-S or SMD-C and whose non-final fragments end in an mCRC. A receiver that does not implement preemption:

What the receiver doesResult
sees an unrecognised SMDmost implementations treat it as no SFD at all
or accepts it as an SFDthen the mCRC fails Chapter 6.3's residue check
either waythe fragment is discarded
and the final fragmenthas a valid FCS over the whole frame, not over itself

The last row is the one that produces the confusing symptom. A final fragment's FCS covers every octet of the original frame, but the receiver only saw the last fragment — so the residue is computed over a prefix of what the FCS covers and fails. Every fragment of every preempted frame is discarded, and the counter that increments is Chapter 6.3's FCS error count.

Which means the symptom of unnegotiated preemption is a link with a high CRC error rate — and Chapter 6.1's whole framing says a CRC error is a physical-layer problem. So the investigation goes to the cable, the optics and Chapter 3.7's FEC, and none of them is at fault.

And the rate is not small. A link where 10% of frames are preempted once discards 10% of its preemptable traffic, with FCS errors at a rate that on any other link would mean a failing transceiver.

Which is why the handshake is mandatory rather than advisory, and why it fails closed:

Handshake outcomePreemptionGuard band at 1 Gb/s
response receivedenabled0.560 µs
no response after 3 attemptsdisabled12.192 µs
link downdisabled

Row two is a working link with a worse guard band, which is a performance loss. The alternative — enabling optimistically — is a working link that silently discards a tenth of its traffic and blames the cabling.

And one more consideration makes the handshake worth its 5250 bit/s: it must be redone after every link event. A link that flaps has a new partner as far as this protocol is concerned — possibly literally, if somebody re-patched it — and a device that remembered "preemption worked last time" is exactly the optimistic enable that row two exists to prevent.

15. RTL 8 — Conformance for a Preemptable Port

The monitor checks the framing, the floors and the handshake. It cannot check that the far end reassembled correctly.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// preempt_conformance_monitor -- one bit.
//
// It asserts the local invariants: fragments meet the 64-octet floor,
// express frames are never fragmented, the mCRC is emitted on every
// non-final fragment, and nothing was preempted before the handshake
// completed.
// -----------------------------------------------------------------------
module preempt_conformance_monitor
  import preempt_pkg::*;
(
  input  logic  clk,
  input  logic  rst_n,

  input  logic  fragment_below_floor,
  input  logic  remainder_below_floor,
  input  logic  express_was_fragmented,
  input  logic  mcrc_missing_on_fragment,
  input  logic  fcs_on_nonfinal_fragment,
  input  logic  preempted_before_verify,
  input  logic  smd_sequence_reused,       // same code twice running
  input  logic  cfg_all_classes_express,   // section 4's row three
  input  logic  cfg_no_class_express,      // nothing to preempt for

  output logic  conformant,
  output logic [15:0] fault_vector,
  output logic [31:0] c_violations
);

  logic v_frag, v_rem, v_exp, v_mcrc, v_fcs, v_early, v_seq;
  logic v_allexp, v_noexp;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      v_frag <= 1'b0; v_rem <= 1'b0; v_exp <= 1'b0; v_mcrc <= 1'b0;
      v_fcs <= 1'b0; v_early <= 1'b0; v_seq <= 1'b0;
      v_allexp <= 1'b0; v_noexp <= 1'b0; c_violations <= '0;
    end else begin
      // A fragment below the floor is discarded as a runt by every
      // receiver -- 7.3 -- so its frame never reassembles.
      if (fragment_below_floor)     begin v_frag  <= 1'b1; c_violations <= c_violations + 1; end
      if (remainder_below_floor)    begin v_rem   <= 1'b1; c_violations <= c_violations + 1; end
      if (express_was_fragmented)   begin v_exp   <= 1'b1; c_violations <= c_violations + 1; end
      if (mcrc_missing_on_fragment) begin v_mcrc  <= 1'b1; c_violations <= c_violations + 1; end
      if (fcs_on_nonfinal_fragment) begin v_fcs   <= 1'b1; c_violations <= c_violations + 1; end
      // Section 14: fragments to a partner that never answered.
      if (preempted_before_verify)  begin v_early <= 1'b1; c_violations <= c_violations + 1; end
      if (smd_sequence_reused)      begin v_seq   <= 1'b1; c_violations <= c_violations + 1; end

      // Standing configuration properties -- section 4's table.
      v_allexp <= cfg_all_classes_express;
      v_noexp  <= cfg_no_class_express;
    end
  end

  assign conformant = !(v_frag || v_rem || v_exp || v_mcrc || v_fcs ||
                        v_early || v_seq || v_allexp || v_noexp);
  assign fault_vector = {7'b0, v_noexp, v_allexp, v_seq, v_early,
                         v_fcs, v_mcrc, v_exp, v_rem, v_frag};

endmodule

Classification: a sticky aggregator with seven framing violations and two standing configuration terms.

What it teaches: that fcs_on_nonfinal_fragment and mcrc_missing_on_fragment are the same bug with opposite signs and both must be checked. A non-final fragment terminated by an ordinary FCS is classified by Section 8 as a complete frame — so the receiver delivers a truncated frame upward with a valid checksum, which is worse than discarding it. The complement is a final fragment terminated by an mCRC, which leaves the receiver reassembling until Section 12's timeout.

And it teaches that cfg_all_classes_express and cfg_no_class_express are opposite configurations with the same effect and different causes. All-express is a cautious operator marking everything important; none-express is a port where preemption is enabled and there is nothing to preempt for. Both leave the guard band at 12.192 µs, and both look like a working configuration.

Deliberately simplified: smd_sequence_reused checks only for an immediately repeated code. The four-code cycle detects up to three losses, so the meaningful check is that consecutive fragments advance by exactly one — which requires remembering the previous code and is one more comparator.

Production implication: conformant here means this port framed and floored its fragments correctly and did not preempt before it was allowed to. It does not mean the frames reassembled — that happens at the far end, and Section 12's c_lost_fragment, c_interleaved and c_timed_out are the far end's counters. A port with conformant high whose partner reports lost fragments has a link-layer problem between them, which is the honest division and the same one Chapter 15.3 §15 drew.

16. The Guard Band, Recomputed With Preemption

Chapter 17.2 §8 built a table whose last row delivered 0.49% of the link. This is that table with L_uninterruptible changed from 1518 octets to 64.

The band first:

Line rateWithoutWithFactorSync's share, with
1 Gb/s12.192 µs0.560 µs21.8×8.6%
10 Gb/s1.263 µs0.100 µs12.7×48.4%
25 Gb/s0.534 µs0.069 µs7.8×69.9%
100 Gb/s0.170 µs0.054 µs3.2×89.4%

The factor falls with line rate because the sync term does not shrinkChapter 17.2 §18's observation, now on the other side of the substitution. At 100 Gb/s with preemption the guard band is 89.4% synchronisation error, so the next improvement is Chapter 16.5's and not this chapter's.

And the usable fraction, which is what a deployment feels:

CycleWindowUsable withoutUsable with
2000 µs200 µs9.39%9.97%
1000 µs100 µs8.78%9.94%
500 µs75 µs12.56%14.89%
250 µs50 µs15.12%19.78%
125 µs25 µs10.25%19.55%
62.5 µs12.5 µs0.49%19.10%
31.25 µs6.25 µs0.00%18.21%

Chapter 17.2 §8's collapse is gone. Without preemption the usable fraction peaked at a 250 µs cycle and fell off a cliff below it; with preemption it is flat from 250 µs down to 31.25 µs at around 19% — and the cliff has moved to a cycle whose window approaches 0.560 µs, which is fifty times shorter.

Which changes the sizing rule Chapter 17.2 §12 gave. That chapter said choose the longest cycle that meets the deadline, because shortening cost bandwidth. With preemption, shortening costs almost nothing down to 31 µs — so the rule becomes choose the cycle that meets the deadline, and the bandwidth is nearly indifferent.

And the latency that buys, using Chapter 17.1 §4's 37.12 µs per hop over five hops:

CycleSchedule waitFive-hop worst caseUsable
1000 µs900 µs1085.6 µs9.94%
250 µs200 µs385.6 µs19.78%
62.5 µs50 µs235.6 µs19.10%
31.25 µs25 µs210.6 µs18.21%

A 31.25 µs cycle delivers a 210 µs worst case and 18.21% of the link. Without preemption the same cycle delivers zero.

==

Chapter 17.2's usable-fraction curve peaked at a 250 microsecond cycle with 15.12 percent of the link and then collapsed, reaching 0.49 percent at a 62.5 microsecond cycle and zero at 31.25, because the guard band of 12.192 microseconds approached and then exceeded the window. With preemption the band falls to 0.560 microseconds and the collapse disappears: the usable fraction is 19.78 percent at a 250 microsecond cycle, 19.55 at 125, 19.10 at 62.5 and 18.21 at 31.25 — nearly flat across a factor of eight in cycle length. This changes Chapter 17.2's sizing rule from choose the longest cycle that meets the deadline to the bandwidth is nearly indifferent to the cycle, so a 31.25 microsecond cycle delivers a 210.6 microsecond five-hop worst case and 18.21 percent of the link, where without preemption the same cycle delivers nothing at all.250 us cycle15.12% -> 19.78%125 us10.25% -> 19.55%62.5 us0.49% -> 19.10%31.25 us0.00% -> 18.21%Nearly flatthe collapse is gone12.192 -> 0.560us21.8xCycle is nearlyfree17.2's rule changes12
Figure 4 — Chapter 17.2's collapse, removed: the usable fraction stays near 19% down to a 31 microsecond cycle.

17. What Preemption Can and Cannot Promise

ClaimStatus
an express frame waits at most one minimum fragmentguaranteed — 0.512 µs at 1 Gb/s
the guard band falls 21.8× at 1 Gb/sguaranteed
a fragment is distinguishable from corruptionguaranteed to 7.3 × 10⁻¹²
a frame under 128 octets is preemptibleno — Section 11
a link of 64-octet frames benefitsno — nothing can be cut
fragments cost bandwidthno — the SMD replaces the SFD
the far end reassembled itnot checkable from here
preemption is ononly after Section 13's handshake

Rows four and five are the mechanism's real limitation and they coincide in an unfortunate way. A link carrying short preemptable frames cannot be preempted, and short frames are what a congested link produces — so the guard band's saving is smallest exactly when the schedule is under most pressure.

Row six is worth stating because it is the one thing preemption does not cost. The SMD occupies Chapter 5.2's SFD octet; the only addition is one octet of fragment count on continuation fragments. A frame cut twice carries two extra octets in total, which against 1518 is 0.13% — invisible in Chapter 8.3's efficiency.

And row eight is the one that produces a silent shortfall. A port whose handshake failed runs Chapter 17.2's full guard band, so a 12.5 µs window delivers 0.49% instead of 19.10% — a factor of 39 — with a link that is up, a schedule that is valid and a conformance bit that is high.

18. The Cost of Preemption, Accounted

ComponentCostNote
a second MAC state machine≈200 flopsexpress, and it is an ordinary Chapter 7.1 path
Section 3's arbiter5 states, ≈40 flopsthe mid-frame yield
preemptable MAC state held across a yielda frame buffer + CRC state1518 octets
Section 7's second CRC enginetwo 32 × 8 GF(2) matricesat 100 Gb/s, two 32 × 512
Section 8's classifierone 32-bit comparatorChapter 6.3 gains one constant
Section 12's reassembly buffer1518 octets, per portreceive side
Section 13's handshake5 states, two timers
total logic≈4000 gate-equivalents + two 1518-octet buffers
bandwidth costone octet per continuation fragment0.13% on a twice-cut frame
verify traffic5250 bit/s at a 128 ms period5.25 × 10⁻⁴% of 1 Gb/s
what it buysthe guard band, 21.8× smallerand Chapter 17.2 §8's collapse removed

Rows three and six are the real cost and they are memory rather than logic. A preempted frame must be held at both ends — the transmitter holds the remainder and the receiver holds the fragments — so a preemptable port carries two maximum-frame buffers it did not need before. At 1518 octets each that is 3 KiB per port, which against Chapter 14.1's 12 MiB pool is 0.024%.

Row four is the largest logic addition and it is the one Section 9 identified. Two CRC accumulations over the same octet stream, because a fragment's mCRC and the frame's FCS cover different spans.

And the comparison across Module 17 puts it in proportion:

MechanismCostWhat it buys
Chapter 17.2's schedule≈700 octetsa bounded interference term
this chapter's preemption≈3 KiB + 4000 gatesthe guard band, 21.8× smaller

Four times the state and an order of magnitude more logic than the schedule, for a mechanism that adds no guarantee at all — it only makes the schedule's guarantee affordable at short cycles. Which is the honest characterisation: preemption is not a determinism feature; it is a bandwidth-recovery feature for a determinism feature.

19. Properties Worth Asserting, and One Worth Refusing

The properties divide by object: the arbiter, the framing, the CRCs, the classifier, reassembly, and the handshake.

Group 1 — the arbiter.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P1. An express frame is never fragmented. There is one express MAC
// and nothing can interrupt it.
property p_express_never_fragmented;
  @(posedge clk) disable iff (!rst_n)
  (st == S_EXPRESS) |-> !emit_mcrc;
endproperty

// P2. A preemptable frame yields only at a LEGAL cut.
property p_cut_only_when_legal;
  @(posedge clk) disable iff (!rst_n)
  emit_mcrc |-> $past(cut_legal);
endproperty

// P3. And only when preemption has been verified -- section 13.
property p_cut_only_when_enabled;
  @(posedge clk) disable iff (!rst_n)
  emit_mcrc |-> $past(preemption_enabled);
endproperty

// P4. After a cut the express MAC gets the wire.
property p_cut_yields_to_express;
  @(posedge clk) disable iff (!rst_n)
  (st == S_CUTTING) |=> (st == S_EXPRESS);
endproperty

// P5. And the preemptable frame resumes after the express one ends.
property p_preemptable_resumes;
  @(posedge clk) disable iff (!rst_n)
  (resuming && (st == S_EXPRESS) && exp_eop) |=> (st == S_PREEMPTABLE);
endproperty

Group 2 — framing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P6. The SMD occupies the SFD's octet. Nothing is added to the
// frame's length -- which is why preemption costs no bandwidth.
property p_smd_replaces_sfd;
  @(posedge clk) disable iff (!rst_n)
  (tx_valid && (idx == 4'd7)) |-> (tx_octet == smd_of(kind, seq));
endproperty

// P7. The preamble is otherwise unchanged -- seven 0x55 octets.
property p_preamble_unchanged;
  @(posedge clk) disable iff (!rst_n)
  (tx_valid && (idx < 4'd7)) |-> (tx_octet == 8'h55);
endproperty

// P8. Only continuation fragments carry a fragment count.
property p_count_only_on_cont;
  @(posedge clk) disable iff (!rst_n)
  (tx_valid && (idx == 4'd8)) |-> (kind == SMD_KIND_CONT);
endproperty

// P9. The SMD sequence advances by one across fragments, so a loss
// is detectable rather than silently concatenated.
property p_seq_advances;
  @(posedge clk) disable iff (!rst_n)
  (start && (kind == SMD_KIND_CONT)) |-> (seq == ($past(seq) + 2'd1));
endproperty

Group 3 — the CRCs.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P10. The frame CRC is initialised once per FRAME and the fragment
// CRC once per FRAGMENT. They cover different spans.
property p_two_spans;
  @(posedge clk) disable iff (!rst_n)
  fragment_start |=> (frag_crc == 32'hFFFF_FFFF);
endproperty

property p_frame_span_survives_fragments;
  @(posedge clk) disable iff (!rst_n)
  (fragment_start && !frame_start) |=> $stable(frame_crc);
endproperty

// P11. A non-final fragment is terminated by an mCRC and never by
// an FCS. The opposite delivers a truncated frame that CHECKS.
property p_nonfinal_gets_mcrc;
  @(posedge clk) disable iff (!rst_n)
  terminate_fragment |=> (emit_valid && emit_is_mcrc);
endproperty

// P12. And the final one by an FCS over the WHOLE frame.
property p_final_gets_fcs;
  @(posedge clk) disable iff (!rst_n)
  terminate_frame |=> (emit_valid && !emit_is_mcrc && (fcs_out == ~frame_crc));
endproperty

// P13. The two residues are complements -- which is why one engine
// distinguishes them. Section 6.
property p_residues_are_complements;
  @(posedge clk) disable iff (!rst_n)
  (MCRC_RESIDUE == ~FCS_RESIDUE);
endproperty

Group 4 — classification.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P14. The runt check comes FIRST. A 40-octet unit is a runt whatever
// its residue -- 7.3, and section 11's floor exists so that it is.
property p_runt_check_is_first;
  @(posedge clk) disable iff (!rst_n)
  (unit_end && (unit_octets < MIN_FRAG_OCTETS))
    |=> (is_runt && !is_nonfinal_fragment && !is_complete_frame);
endproperty

// P15. A fragment requires BOTH the mCRC residue and a fragment SMD.
// Requiring both is what keeps 6.1's error budget.
property p_fragment_needs_both;
  @(posedge clk) disable iff (!rst_n)
  is_nonfinal_fragment |-> (($past(running_residue) == MCRC_RESIDUE) &&
                            (($past(smd_kind) == SMD_KIND_START) ||
                             ($past(smd_kind) == SMD_KIND_CONT)));
endproperty

// P16. A disagreement between the SMD and the residue is corruption,
// and it is counted.
property p_mismatch_is_corrupt;
  @(posedge clk) disable iff (!rst_n)
  (unit_end && (running_residue == MCRC_RESIDUE) &&
   (smd_kind == SMD_KIND_EXPRESS)) |=> (is_corrupt && $changed(c_smd_mismatch));
endproperty

// P17. Exactly one classification per unit.
property p_one_classification;
  @(posedge clk) disable iff (!rst_n)
  unit_end |=> ($countones({is_complete_frame, is_nonfinal_fragment,
                            is_corrupt, is_runt}) == 1);
endproperty

Group 5 — reassembly.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P18. An express frame passes through without disturbing an
// in-progress reassembly. The two MACs are independent to the end.
property p_express_does_not_disturb;
  @(posedge clk) disable iff (!rst_n)
  (unit_valid && (smd_kind == SMD_KIND_EXPRESS) && reassembling)
    |=> $stable(accumulated);
endproperty

// P19. A sequence skip discards rather than concatenating.
property p_skip_discards;
  @(posedge clk) disable iff (!rst_n)
  (unit_valid && (smd_kind == SMD_KIND_CONT) && reassembling &&
   (smd_seq != expect_seq)) |=> (!reassembling && $changed(c_lost_fragment));
endproperty

// P20. A reassembly always terminates -- completed or timed out.
property p_reassembly_terminates;
  @(posedge clk) disable iff (!rst_n)
  $rose(reassembling) |-> ##[1:TIMEOUT_CYC+1] !reassembling;
endproperty

// P21. The reassembled length never exceeds the MTU.
property p_no_oversize;
  @(posedge clk) disable iff (!rst_n)
  frame_out_valid |-> (frame_out_octets <= 14'(MAX_FRAME));
endproperty

// P22. An SMD-S mid-reassembly abandons the old frame and starts a
// new one -- it never merges them.
property p_start_abandons;
  @(posedge clk) disable iff (!rst_n)
  (unit_valid && (smd_kind == SMD_KIND_START) && reassembling)
    |=> ($changed(c_interleaved) && (accumulated == $past(unit_octets)));
endproperty

Group 6 — the floor and the handshake.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P23. Both sides of a cut meet the 64-octet floor.
property p_both_sides_meet_the_floor;
  @(posedge clk) disable iff (!rst_n)
  cut_legal |-> ((octets_sent >= 14'(MIN_FRAG_OCTETS)) &&
                 (octets_remaining >= 14'(MIN_FRAG_OCTETS)));
endproperty

// P24. A frame under 128 octets has no legal cut point.
property p_short_frames_not_preemptible;
  @(posedge clk) disable iff (!rst_n)
  ((octets_sent + octets_remaining) < 14'd128) |-> !cut_legal;
endproperty

// P25. The guard band's L_uninterruptible and the decider's floor are
// the SAME number. A design using two has a band that does not cover
// its own decider.
property p_band_matches_decider;
  @(posedge clk) disable iff (!rst_n)
  (GUARD_L_UNINTERRUPTIBLE == MIN_FRAG_OCTETS);
endproperty

// P26. Preemption stays disabled until a response arrives.
property p_disabled_until_response;
  @(posedge clk) disable iff (!rst_n)
  preemption_enabled |-> ($past(st) inside {V_SUCCEEDED, V_VERIFYING});
endproperty

// P27. A verify is answered unconditionally -- 15.3's passive/passive
// deadlock avoided by construction.
property p_verify_always_answered;
  @(posedge clk) disable iff (!rst_n)
  rx_verify |=> tx_response;
endproperty

// P28. Failure leaves preemption OFF, which is the safe direction:
// whole frames always work.
property p_fails_closed;
  @(posedge clk) disable iff (!rst_n)
  (st == V_FAILED) |-> !preemption_enabled;
endproperty

// P29. A link event re-runs the handshake -- a remembered capability
// is the optimistic enable section 14 exists to prevent.
property p_link_event_reverifies;
  @(posedge clk) disable iff (!rst_n)
  $fell(link_up) |=> !preemption_enabled;
endproperty

// P30. Standing property: at least one class is express and not all
// of them are. Both extremes disable the mechanism.
property p_sensible_class_split;
  @(posedge clk) disable iff (!rst_n)
  (!cfg_all_classes_express && !cfg_no_class_express);
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

P11 and P12 are the framing's central pair, P15 keeps Chapter 6.1's error budget, P25 ties the guard band to the decider, and P28 makes the handshake fail closed. Every one of them is about a fragment, a unit or a configurationand none of them is about the frame as a thing transmitted without interruption, which is the property this chapter refuses.

20. Verification Scenarios

Seventy-four scenarios. The ones that matter are on the wire, because a testbench that checks reassembled frames never sees a fragment.

The arbiter

#ScenarioExpected
1Express frame in flight, another express arriveswaits — one express MAC
2Preemptable in flight, express arrives at octet 200cut, mCRC, yield
3Same at octet 30not legal — express waits
4Same, wait measuredup to 0.512 µs at 1 Gb/s
5Preemption disabledexpress waits a full frame — 12.144 µs
6After the express frame endspreemptable resumes
7Express frame arrives during S_CUTTINGqueued behind the first
8c_preemptions vs c_fragmentsfragments = preemptions + frames

Class assignment

#ScenarioExpected
9One class express0.512 µs worst express wait
10Two classes expressthey block each other a full frame
11All eight expresspreemption disabled in effect
12No class expressnothing to preempt for
1311 or 12 configuredconformant low
14PCP reused for queue, pause and preemptionfour meanings, three bits

Framing

#ScenarioExpected
15SMD in the SFD octetframe length unchanged
16Preamble octets 0–60x55, unchanged
17Continuation fragmentone extra octet: the count
18Frame cut twicetwo extra octets — 0.13% of 1518
19SMD-C codes across fragmentscycle 0,1,2,3
20C1 then C3 receivedC2 lost — detected
21One SMD-C code onlytwo frames concatenate into one plausible frame
22Four lost fragmentsthe sequence wraps — undetectable

The CRCs

#ScenarioExpected
23Frame CRC across fragmentscovers every octet of the frame
24Fragment CRCcovers only this fragment
25Frame CRC derived from fragment CRCsnot possible without the lengths
26mCRC = complement of the ordinary FCS
27Residue on an FCS-terminated unit0xC704DD7B
28Residue on an mCRC-terminated fragment0x38FB2284
29The two residuescomplements of each other
30Chapter 6.4's engineunchanged — one comparator added
31Two CRC engines at 100 Gb/stwo 32 × 512 matrices — the largest gate cost

Classification

#ScenarioExpected
3240-octet unit, mCRC residuerunt — the check comes first
33300 octets, FCS residue, SMD-Ea complete frame
34300 octets, mCRC residue, SMD-Ca non-final fragment
35300 octets, mCRC residue, SMD-Ecorrupt — c_smd_mismatch
36300 octets, neither residuecorrupt
37P(corrupt taken for a fragment)≈7.3 × 10⁻¹²
38Against Chapter 6.1's 2⁻³²32× rarer than the existing case
39Single-bit error in 0xD5not a valid SMD-C — excluded by Hamming distance
40Exactly one classification per unitalways

Reassembly

#ScenarioExpected
41Express frame mid-reassemblyreassembly undisturbed
42A receiver that ended reassembly on any SMDevery preempted frame discarded
43Sequence skipc_lost_fragment, discard
44SMD-S mid-reassemblyc_interleaved, old frame abandoned
45Final fragment lostreassembling for ever without a timeout
46Same, with the timeoutc_timed_out after 1 ms
47Same, traffic during the stall125 000 octets at 1 Gb/s
48Accumulated exceeds the MTUc_oversize, discard
49worst_fragments_seen near 4sequence space nearly exhausted
50Reassembly buffer1518 octets, per port

The floor

#ScenarioExpected
5164-octet framenot preemptible — no cut point
52128-octet frameexactly one cut point
53256-octet frame129 cut points
541518-octet frame1391
55Cut at octet 30fragment_below_floor
56Cut leaving 30 octetsremainder_below_floor
57A link of 64-octet preemptable framespreemption buys nothing
58Same, guard bandstill 0.560 µs — the formula holds
59Guard band's floor ≠ decider's floorthe band does not cover the decider

The handshake

#ScenarioExpected
60Verify sent, response receivedpreemption enabled
61Three verifies, no responseV_FAILED, preemption off
62Fragments to a non-preemption receiverevery fragment discarded
63Same, symptoma high FCS error rate
64Same, investigationgoes to the cable and the optics
6510% of frames preempted10% of preemptable traffic lost
66Verify received while not requestinganswered unconditionally
67Both ends passiveno deadlock — response is unconditional
68Link flapshandshake re-runs
69Capability remembered across a flapthe optimistic enable §14 prevents
70Verify at 128 ms5250 bit/s — 5.25 × 10⁻⁴%

The guard band

#ScenarioExpected
711 Gb/s, without / with12.192 / 0.560 µs — 21.8×
72100 Gb/s, with0.054 µs — 89.4% is sync error
7362.5 µs cycle, 12.5 µs window0.49% → 19.10%
7431.25 µs cycle, 6.25 µs window0.00% → 18.21%

The directed test random stimulus will not produce

A fragment is not a frame, so a testbench that checks reassembled frames never observes one — and every failure in Sections 6 to 9 is a property of what is on the wire between the two ends. Random stimulus varies frame contents and arrival times; it does not vary whether the far end understands preemption, which is Section 14's failure and the one whose symptom points at the cabling.

Setup: one preemptable port at 1 Gb/s with a wire-level monitor that classifies every unit by SMD and residue independently of the DUT. An express class carrying 64-octet frames at random intervals, a preemptable class carrying a fixed frame-size distribution. Chapter 6.4's engine in the monitor, run twice — once expecting 0xC704DD7B and once 0x38FB2284.

Stimulus, five runs of 10⁶ preemptable frames. Run A — verified, 1518-octet preemptable frames. Run B — verified, 64-octet preemptable frames. Run C — verified, 128-octet preemptable frames. Run D — handshake never completed, preemption requested and enabled optimistically. Run E — Run A with the receiver's classifier using only Chapter 6.3's single residue.

Oracle:

#ObservableA — 1518B — 64C — 128D — unverifiedE — single residue
1c_preemptionshigh0lowhighhigh
2cut_legal ever trueyesneverat octet 64 onlyyesyes
3express worst wait0.512 µs0.672 µs0.512 µs0.512 µs0.512 µs
4effective guard band needed0.560 µs0.560 µs0.560 µs12.192 µs0.560 µs
5fragments on the wiremanynonefewmanymany
6fragments classified correctlyallallnone — receiver has no classifiernone
7frames reassembledalln/aallnonenone
8FCS error rate at the receiver000≈ the preemption rate≈ the preemption rate
9c_smd_mismatch000n/a — no classifiern/a
10conformant at the transmitterhighhighhighlow — preempted_before_verifyhigh
11where an operator would lookthe cable and the opticsthe cable and the optics
12c_verify_failed000>0 — the only true signal0
13usable fraction, 62.5 µs cycle19.10%0.49%19.10%0.49%19.10%
14rerun E with two residuesall reassembled
15rerun B with 128-octet framesbecomes Run C

Rows 6 to 11 together are the finding. Runs D and E produce identical symptoms at the receiver — every fragment discarded, an FCS error rate equal to the preemption rate — from two entirely different causes, one a handshake that never completed and one a classifier missing a constant. And both send an operator to the physical layer, because Chapter 6.1's framing says a CRC error is a cable problem.

Row 12 is the only signal that separates them, and it exists at the transmitter rather than where the errors are counted.

Rows 1, 2 and 13 are the second finding: Run B is a correctly configured, fully conformant port on which preemption does nothing at all. Every frame is 64 octets, no frame has a legal cut point, and the usable fraction at a 62.5 µs cycle is Chapter 17.2's 0.49% rather than this chapter's 19.10%with conformant high and no counter indicating anything. Row 15 is the remedy and it is a traffic-engineering change, not a configuration one.

21. Debugging Preemption

Five questions, and the first two explain most of the cases where preemption appears not to work.

Step 1 — did the handshake complete? preemption_enabled, c_verify_sent, c_verify_failed. A port that never enabled runs Chapter 17.2's full guard band, so a 12.5 µs window delivers 0.49% instead of 19.10% — a factor of 39 — with a healthy link and a valid schedule. This is checkable with no traffic.

Step 2 — is anything preemptible? c_preemptions against c_fragments, and the preemptable traffic's frame-size distribution. A link carrying 64-octet preemptable frames has no legal cut point anywhere — Section 11's first row — so preemption is enabled, correct, and inert. c_wanted_but_illegal rising with c_cut at zero is exactly this.

Step 3 — are fragments surviving? The far end's c_lost_fragment, c_interleaved, c_timed_out and its FCS error rate. A receiver discarding fragments produces an FCS error rate equal to the preemption rate, and Chapter 6.1's framing sends that investigation to the cable. Two causes — an incomplete handshake and a classifier with one residue — produce identical symptoms, and Step 1's counter separates them.

Step 4 — is the class split sensible? cfg_all_classes_express and cfg_no_class_express. Both extremes disable the mechanism and both look like complete configurations: all-express is a cautious operator and none-express is a port with nothing to preempt for.

Step 5 — is the guard band matched to the decider? The band's L_uninterruptible against the decider's MIN_FRAG_OCTETS, and the pipeline depth between an express frame becoming ready and cut_wanted asserting. A band computed from 64 octets protecting a decider that sees express traffic ten cycles late is a band that does not cover its own mechanism, and worst_illegal_wait_ns exceeding 0.512 µs is the evidence.

And the finding that ends an investigation: handshake complete, cuts happening, no lost fragments at the far end, a sensible class split, and worst_illegal_wait_ns at the minimum-fragment time. That is preemption delivering its 21.8× — and any remaining shortfall is Chapter 17.2's cycle or Chapter 16.5's clock.

22. Common Misconceptions

1 — "Preemption makes the network faster."

The wrong model: interrupting a frame reduces latency.

What it costs: a mechanism deployed for the wrong measurement. Preemption adds no guarantee at allChapter 17.1's bound and Chapter 17.2's schedule provide it. What it changes is the guard band: 12.192 µs to 0.560 at 1 Gb/s, which is bandwidth rather than latency.

The corrected model: preemption is a bandwidth-recovery feature for a determinism feature. It makes Chapter 17.2 §8's short cycles affordable — a 62.5 µs cycle goes from delivering 0.49% of the link to 19.10% — and an express frame's worst wait falls from 12.144 µs to 0.512, which is a latency gain for one class and not for the network.

2 — "Mark everything important as express."

The wrong model: express is a priority level.

What it costs: the mechanism, entirely. Express means uninterruptible, not fast — and there is one express MAC with no internal priority, so two express classes block each other for a full frame. All eight classes express means nothing is preemptable and preemption is disabled in effect.

The corrected model: one class express, everything else preemptable. The gain is that the one express class's worst wait falls 23.7×; adding a second express class restores a full-frame wait between them and removes most of the benefit.

3 — "A fragment is just a short frame."

The wrong model: the receiver will work it out.

What it costs: every fragment discarded. A fragment is a prefix of a frame, so its last four octets are ordinary contentChapter 6.3's residue check fails exactly as it would on corruption. And a final fragment's FCS covers the whole frame, not the fragment, so it fails too.

The corrected model: a non-final fragment carries an mCRC — the ordinary CRC-32, complemented — so the residue is 0x38FB2284 instead of 0xC704DD7B, and a receiver with two constants and one comparator distinguishes a fragment from corruption to 7.3 × 10⁻¹².

4 — "Enable preemption on both ends and it works."

The wrong model: configuration is agreement.

What it costs: if one end does not implement it, every fragment is discarded and the FCS error rate equals the preemption rate — which Chapter 6.1's framing says is a physical-layer fault. The investigation goes to the cable, the optics and Chapter 3.7's FEC, and none of them is at fault.

The corrected model: preemption is verified before use — SMD-V until an SMD-R arrives, three attempts, and it fails closed. A partner that does not answer leaves preemption disabled and the guard band large, which is a performance loss rather than a traffic loss. And the handshake re-runs after every link event, because a flapped link may have a different partner.

5 — "Any frame can be preempted."

The wrong model: the cut point is free.

What it costs: a design whose guard band assumes a 64-octet floor and whose traffic never reaches it. Both sides of a cut must be at least 64 octetsChapter 7.3's runt check applies to fragments — so a 64-octet frame has no legal cut point and a 128-octet one has exactly one.

The corrected model: preemptibility is a window that closes on short frames, and short frames are what a congested link produces. A link of 64-octet preemptable frames is fully conformant, correctly configured, and preemption does nothing on it — with c_wanted_but_illegal rising and c_cut at zero as the only evidence.

6 — "Fragments cost bandwidth."

The wrong model: extra framing means extra octets.

What it costs: a mechanism rejected for an overhead that is not there. The SMD occupies Chapter 5.2's SFD octet — a position that already existed and held a constant — so the only addition is one octet of fragment count per continuation.

The corrected model: a frame cut twice carries two extra octets against 1518 — 0.13%, invisible in Chapter 8.3's efficiency. The real costs are logic and memory: two CRC engines, and a maximum-frame buffer at each end — about 3 KiB per port, 0.024% of Chapter 14.1's pool.

23. Interview Reasoning

Q1 — What does preemption change, and what does it not?

It changes what a gate must reserve against, and nothing else. Chapter 17.2's guard band is L_uninterruptible / R + 2 × sync_error, and preemption takes L_uninterruptible from a maximum frame (1518 octets) to a minimum fragment (64) — 12.192 µs to 0.560 at 1 Gb/s, a 21.8× reduction. It adds no guarantee: Chapter 17.1's bound and Chapter 17.2's schedule provide those. What it buys is that Chapter 17.2 §8's collapse disappears — a 62.5 µs cycle goes from 0.49% of the link to 19.10%.

Q2 — Why does a fragment need a different CRC, and what is it?

Because a fragment is a prefix, so its last four octets are ordinary content and Chapter 6.3's residue check fails on it exactly as on corruption. The mCRC is the ordinary CRC-32 of the fragment, bitwise complemented — and since complementing the check value complements the residue, an mCRC-terminated unit gives 0x38FB2284 where an FCS gives 0xC704DD7B. The two are complements. Chapter 6.4's engine is unchanged; the checker gains one constant, one comparator and a third output.

Q3 — How does a receiver avoid mistaking corruption for a fragment?

By requiring both the residue and the delimiter to agree. A corrupt frame must produce the mCRC residue — 2⁻³² ≈ 2.33 × 10⁻¹⁰and its SMD must decode as SMD-S or SMD-C — eight codes of 256, so 1/32. The product is ≈7.3 × 10⁻¹², which is 32× rarer than the 2⁻³² of corrupt frames the ordinary FCS already admits. And the SMD codes are chosen for Hamming distance, so a single-bit error in 0xD5 does not produce a valid SMD-C — the common case is excluded by construction.

Q4 — Why is there a 64-octet floor on both sides of a cut?

Because Chapter 7.3's runt check applies to fragments as units on the wire. A fragment shorter than 64 octets is silently discarded, so its frame never reassembles — and the same applies to the remainder, which is itself a unit. So a 64-octet frame has no legal cut point and a 128-octet frame has exactly one. The consequence is that a link carrying short preemptable frames gains nothing, and short frames are what a congested link produces.

Q5 — Why must preemption be verified before use?

Because a receiver that does not implement it discards every fragment, and the symptom points at the physical layer. An unrecognised SMD or a failed residue increments the FCS error counter, so a link where 10% of frames are preempted shows a 10% CRC error rate — and Chapter 6.1's framing sends that investigation to the cable and the optics. The handshake — SMD-V until SMD-R, three attempts — fails closed: no response means preemption stays off, the guard band stays large, and whole frames always work. And it re-runs after every link event, because a flapped link may be a different partner.

Q6 — Why can't you assert that a frame's octets are contiguous on the wire?

Because the mechanism's entire purpose is to make that false. Every chapter before this one assumed it, usually without writing it down, and Chapter 12.6 §8's un-abortable frame is the fact three chapters relied on. The property is not merely false — it is false by design, with no window to except, because a preempted frame is never contiguous at any instant. The fix is to re-scope rather than to re-bound: assert contiguity of a fragment and of an express frame, both of which have it, and assert content preservation of the reassembled frame, which is the guarantee anything actually depended on.

24. Understanding Check

25. What's Next

Module 17 is complete, and its three chapters are one argument.

Chapter 17.1 established that five of a hop's seven latency terms are bounded and sum to 37.12 µs, and that two have no bound at all. Chapter 17.2 bounded them with a schedule and paid a guard band of 12.192 µs at 1 Gb/s — which at short cycles consumed the window entirely. And this chapter took the band to 0.560 µs by making Chapter 12.6 §8's un-abortable frame abortable.

The result is a network that can promise a deadline. Five hops at 1 Gb/s with a 31.25 µs cycle: a 210.6 µs worst case and 18.21% of the link delivered — against an unbounded worst case and no promise at all before Module 17.

And the cost, assembled across the three chapters: a synchronised clock (Chapter 16.5's 24.2 ns), a schedule installed atomically across every hop, a guard band, two MACs, two CRC engines and two maximum-frame buffers per port — and the requirement that every device in the path participates, which Chapter 17.1 §22's last callout identified as the condition six mechanisms in this track now share.

Module 18 — Ethernet in SoCs — changes the subject entirely.

Everything from Module 2 to here has been about what happens on the wire and inside a switch. Module 18 is about what happens where the MAC meets the rest of a chip: the register interface the CPU drives, the memory the MAC masters, the clock domains between them, and the descriptor rings and DMA engines that move frames into and out of host memory.

Chapter 18.1The Ethernet MAC as an SoC IP Block frames the four interfaces and derives the memory bandwidth a MAC must sustain at each line rate. Then Chapter 18.2 builds the descriptor ring and its ownership model — and finds that the ownership bit is a memory location written by two agents in different clock and coherency domains, so the order of the writes is the entire correctness argument.

One thread carries across, and it is this chapter's. Preemption's whole content was that a unit which looked atomic — a frame on the wire — is not, and every property attached to it had to be re-scoped. Module 18's descriptor is the same discovery in memory: a structure that looks like one object is a set of writes a memory system may reorder, and the barrier that fixes it is the guard band of that chapter.

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.