Skip to content
VLSI Mentor

Ethernet · Module 5

Preamble and Start Frame Delimiter

Bit synchronisation wants maximum regularity and octet alignment wants uniqueness, which are opposites. The preamble does the first for as long as it can and breaks it exactly once — and because it is consumed and regenerated at every hop, its received length measures the path.

Chapter 5.1 placed the preamble first because nothing else is parseable until the question where does this begin? has been answered. Chapter 4.3 §4 established where it lives — generated below the MAC and consumed below it, because a preamble failure is detectable only in the signal and the MAC has no access to the signal.

Neither said what it actually does, and the answer is more interesting than "it lets the receiver get ready".

It does two things, and they want opposite properties from a bit pattern.

Bit synchronisation — recovering the symbol clock, which Chapter 2.6 established is not transmitted and must be extracted from transitions in the data — wants a pattern that is maximally regular. Every bit a transition, so the recovery loop sees the most edges possible in the least time.

Octet alignment — finding where one octet ends and the next begins — wants a pattern that is unique. Something that occurs at exactly one position, so its location is unambiguous.

A preamble field feeds two consumers with conflicting requirements. Clock recovery wants maximum regularity, meaning a transition every bit period. Octet alignment wants uniqueness, meaning a pattern that occurs at exactly one position. Regularity destroys positional information, so the field satisfies them in sequence rather than simultaneously.The preamble field64 bit times, one budgetClock recovery wantsmaximum regularity — everybit an edgeAlternation56 bits of itAlignment wantsuniqueness — one positiononlyOne broken bitthe smallest unique marker12
Figure 2 — one field, two jobs, and requirements that pull in opposite directions.

Those requirements are in direct conflict. A perfectly regular pattern is maximally ambiguous about position: if every bit period looks identical to every other, the pattern tells you nothing about which bit you are on. Perfect regularity destroys positional information by construction.

How does one field satisfy two requirements that contradict each other?

1. Scope — What This Chapter Owns

This chapter owns: why bit synchronisation and octet alignment are different problems with contradictory requirements; the exact preamble and delimiter patterns and the bit-ordering convention that produces them; why the delimiter is one bit different rather than something more distinctive; the false-detection analysis that follows from such a small marker; and why a received preamble length is a property of the path.

This chapter does not own: where the preamble lives in the layering — Chapter 4.3 §4 owns that, and this chapter takes it as given. Nor clock recovery itself, which is Chapter 2.6's; nor block-level alignment on coded links, which is Chapter 3.5 §9's and is a different mechanism for a different layer. Nor the fields that follow — Chapter 5.1 located them and Chapters 5.3 onward open them.

The question this chapter answers that its neighbours do not: how does one field solve two problems whose requirements contradict each other?

2. Bit Synchronisation Wants Maximum Regularity

Chapter 2.6 established the constraint: no clock is transmitted alongside the data, so the receiver must extract symbol timing from transitions in the signal itself. A run with no transitions gives the recovery loop nothing to correct against, and its estimate drifts.

So the fastest possible way to acquire timing is a pattern with a transition every bit period, and that is exactly what alternating ones and zeros gives.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
preamble on the wire:  1010 1010  1010 1010  ...  (7 octets, 56 bits)
transitions:           every single bit period

Nothing carries more timing information per bit than this. A pattern with a transition every bit is the densest possible edge stream, and 56 of them is a substantial acquisition window — the loop has 56 independent corrections before any data arrives.

3. Octet Alignment Wants Uniqueness

The second problem is different in kind. Timing recovery tells the receiver when each bit is; it says nothing about which bit is the first of an octet.

And the preamble actively destroys that information. A perfectly alternating pattern looks identical at every offset — shift it by one bit and it is the same pattern inverted, shift by two and it is identical. There is no way to tell position from a regular pattern, and that is not a shortcoming of the receiver; it is what regularity means.

So something must break the pattern, and what breaks it must be unique: it must occur at exactly one place, or the receiver has several candidate boundaries and no way to choose.

The delimiter's final two ones are that break. In a stream of strict alternation, two consecutive identical bits occur nowhere — so the first time the receiver sees 11, it has found the delimiter's end, and the octet boundary is the bit immediately after.

Conceptual — the alternation and its single break

10 cycles
A conceptual bit-level view over ten bit periods. The preamble alternates between one and zero every bit, giving a transition each period, which the clock recovery uses. In the final delimiter octet the alternation breaks: two consecutive ones appear where a one then a zero was expected. The octet boundary is the bit immediately after that break.a transition every bit — timinga transition every bit —timingstill alternatingstill alternatingthe break: two ones in a rowthe break: two ones in arowthe octet boundary is herethe octet boundary is herebit_clkwire1010101101transitionalignedt0t1t2t3t4t5t6t7t8t9
Figure 1 — conceptual: alternation for timing, then one broken bit for position.

This figure is conceptual and labelled so. It compresses the 56-bit preamble to seven bits so the structure is visible; the relationship it shows — alternation, one break, boundary immediately after — is exact.

4. Why the Break Is as Small as Possible

A more distinctive marker would be easier to detect. The standard uses the smallest possible one, and the reason is a genuine trade rather than an accident.

Every bit spent on a distinctive pattern is a bit not spent on timing recovery. The preamble's budget is fixed at 64 bit times, and those bits do timing work only while they are alternating. A four-bit marker would leave 60 bits of alternation instead of 63; an eight-bit one would leave 56.

And one broken bit is already sufficient for uniqueness, because the pattern it breaks is strict alternation. Two consecutive identical bits cannot occur in an alternating stream, so a single break is unambiguous the moment it appears.

The trade, stated plainly: the smallest break that is unique is the one that costs the least timing information. Anything larger buys detection margin that is not needed and pays for it in acquisition time.

5. RTL 1 — Bit Synchronisation, and What RTL Can Say About It

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Transition-density measurement over the acquisition window.
//
// THIS DOES NOT MODEL CLOCK RECOVERY. Chapter 2.6 owns the loop and Chapter
// 3.3 §15 established why RTL cannot represent one: it is an analog control
// system, not logic.
//
// What RTL CAN measure is the EVIDENCE the loop was given. A preamble is
// supposed to present a transition every bit period; if it does not, the
// loop had less to work with than the design assumed, and that is a
// measurable, actionable fact about the transmitter or the path.
module preamble_transition_monitor #(
  // Bit periods over which density is measured -- the preamble's own length.
  parameter int unsigned WINDOW = 56,
  parameter int unsigned WIN_W  = $clog2(WINDOW + 1),
  parameter int unsigned CNT_W  = 20
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic bit_valid,
  input  logic bit_in,
  input  logic window_active,      // asserted while a preamble is expected
 
  // Transitions observed in the window that just closed. A healthy preamble
  // gives WINDOW-1 of them -- one fewer than the bit count, because the
  // first bit has no predecessor.
  output logic [WIN_W-1:0] window_transitions,
  output logic             window_valid,
 
  // Bits seen with no transition. On strict alternation this is zero, so
  // ANY non-zero value means the pattern was not what it should be.
  output logic [WIN_W-1:0] window_static_bits,
 
  // The worst window since reset, held. Survives `clear`: it is a property
  // of the installation, not of a measurement interval someone chose.
  output logic [WIN_W-1:0] worst_transition_count,
 
  output logic [CNT_W-1:0] c_windows,
  output logic [CNT_W-1:0] c_degraded_windows
);
 
  logic             prev_bit_q;
  logic             have_prev_q;
  logic [WIN_W-1:0] trans_q, static_q, count_q;
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v,
                                            input logic             en);
    bump = (en && !(&v)) ? (v + 1'b1) : v;   // saturating
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      prev_bit_q             <= 1'b0;
      have_prev_q            <= 1'b0;
      trans_q                <= '0;
      static_q               <= '0;
      count_q                <= '0;
      window_transitions     <= '0;
      window_static_bits     <= '0;
      window_valid           <= 1'b0;
      worst_transition_count <= '1;
      c_windows              <= '0;
      c_degraded_windows     <= '0;
    end else begin
      window_valid <= 1'b0;
 
      if (clear) begin
        c_windows          <= '0;
        c_degraded_windows <= '0;
        // worst_transition_count deliberately survives.
      end
 
      if (!window_active) begin
        trans_q     <= '0;
        static_q    <= '0;
        count_q     <= '0;
        have_prev_q <= 1'b0;
      end else if (bit_valid) begin
        if (have_prev_q) begin
          if (bit_in != prev_bit_q) trans_q  <= trans_q  + 1'b1;
          else                      static_q <= static_q + 1'b1;
        end
        prev_bit_q  <= bit_in;
        have_prev_q <= 1'b1;
 
        if (count_q == WIN_W'(WINDOW - 1)) begin
          window_transitions <= trans_q;
          window_static_bits <= static_q;
          window_valid       <= 1'b1;
          count_q            <= '0;
          trans_q            <= '0;
          static_q           <= '0;
 
          c_windows <= bump(c_windows, 1'b1);
          // A healthy preamble has exactly one static bit -- the delimiter's
          // break. More than that means the alternation itself was damaged.
          if (static_q > WIN_W'(1)) c_degraded_windows <= bump(c_degraded_windows, 1'b1);
          if (trans_q < worst_transition_count) worst_transition_count <= trans_q;
        end else begin
          count_q <= count_q + 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that a preamble's transition count is checkable in logic even though the loop it feeds is not. A healthy preamble presents strict alternation, so exactly one static bit — the delimiter's break — should appear per frame. More than one means the alternation itself was damaged, and that is a property of the transmitter or the path rather than of the receiver's recovery loop.

Deliberately simplified: it measures over a fixed window rather than tracking where the delimiter actually landed. A production monitor correlates the two, since a short window and a damaged pattern are different faults.

Production implication: window_static_bits is a transmitter-side diagnostic read at the receiver. A receiver seeing extra static bits in the preamble is being given a pattern that is not alternating — which the far end generated or the path corrupted, and in either case not something the receiver can fix. Reporting it separately from a general error count is what makes the distinction visible.

Later ownership: the recovery loop is Chapter 2.6's; the channel effects that damage a pattern are Chapter 3.3's.

6. RTL 2 — Octet Alignment, With the Evidence Requirement

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Octet alignment from the delimiter, with evidence.
//
// The naive detector is one comparison: two consecutive identical bits means
// the delimiter. IT IS WRONG, because two identical bits occur in noise, in
// idle patterns, and in the tail of another station's activity.
//
// The EVIDENCE is the context, not the marker: a break at the end of a
// SUFFICIENT RUN of alternation. MIN_RUN is the whole trade --
//
//   larger  -> fewer false detections, but a shortened preamble is rejected
//   smaller -> tolerates a short preamble, but aligns on noise more often
//
// Section 12 shows why "shortened preamble" is the common case rather than
// the exceptional one, which is what makes this trade real.
package sfd_pkg;
  typedef enum logic [1:0] {
    A_IDLE,       // no run in progress
    A_RUNNING,    // accumulating alternation
    A_ALIGNED     // the break was seen with sufficient evidence
  } align_state_e;
endpackage
 
module octet_aligner
  import sfd_pkg::*;
#(
  // Bits of alternation required before a break is believed. The standard
  // preamble offers 55; a shortened one offers fewer, and Section 12
  // explains why that is normal rather than a fault.
  parameter int unsigned MIN_RUN = 16,
  parameter int unsigned RUN_W   = $clog2(MIN_RUN + 2),
  parameter int unsigned CNT_W   = 20
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic bit_valid,
  input  logic bit_in,
  input  logic carrier,           // the PHY reports signal present
 
  output align_state_e state,
  output logic         aligned,          // pulses on the octet boundary
  output logic [RUN_W-1:0] run_at_align,  // evidence behind this alignment
 
  // A break seen with INSUFFICIENT evidence. Not an error -- it is the
  // detector correctly declining -- but a rising count means the preamble
  // is arriving shorter than MIN_RUN, which Section 12 shows is a path
  // measurement rather than a fault.
  output logic             break_rejected,
  output logic [CNT_W-1:0] c_break_rejected,
 
  output logic [CNT_W-1:0] c_aligned,
  // The shortest run that was ever ACCEPTED. Trend it: a run that has
  // fallen from 55 to just above MIN_RUN means the path has gained
  // regenerations, and the next one will start rejecting frames.
  output logic [RUN_W-1:0] shortest_accepted_run
);
 
  align_state_e     state_q;
  logic             prev_q;
  logic             have_prev_q;
  logic [RUN_W-1:0] run_q;
 
  wire same_c = have_prev_q && (bit_in == prev_q);
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v,
                                            input logic             en);
    bump = (en && !(&v)) ? (v + 1'b1) : v;
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q               <= A_IDLE;
      prev_q                <= 1'b0;
      have_prev_q           <= 1'b0;
      run_q                 <= '0;
      aligned               <= 1'b0;
      break_rejected        <= 1'b0;
      run_at_align          <= '0;
      c_aligned             <= '0;
      c_break_rejected      <= '0;
      shortest_accepted_run <= '1;
    end else begin
      aligned        <= 1'b0;
      break_rejected <= 1'b0;
 
      if (clear) begin
        c_aligned        <= '0;
        c_break_rejected <= '0;
        // shortest_accepted_run deliberately survives.
      end
 
      // Carrier loss resets everything. Alignment is per-frame and nothing
      // carries over -- which is the difference from Chapter 3.5's block
      // lock, acquired once per link and then maintained.
      if (!carrier) begin
        state_q     <= A_IDLE;
        run_q       <= '0;
        have_prev_q <= 1'b0;
      end else if (bit_valid) begin
        prev_q      <= bit_in;
        have_prev_q <= 1'b1;
 
        unique case (state_q)
          A_IDLE, A_RUNNING: begin
            if (!have_prev_q) begin
              run_q   <= '0;
              state_q <= A_RUNNING;
            end else if (!same_c) begin
              // Alternation continues: evidence accumulates.
              if (run_q != '1) run_q <= run_q + 1'b1;
              state_q <= A_RUNNING;
            end else begin
              // A break. Believe it only with enough evidence behind it.
              if (run_q >= RUN_W'(MIN_RUN)) begin
                state_q      <= A_ALIGNED;
                aligned      <= 1'b1;
                run_at_align <= run_q;
                c_aligned    <= bump(c_aligned, 1'b1);
                if (run_q < shortest_accepted_run) shortest_accepted_run <= run_q;
              end else begin
                // Correctly declining. The run restarts from here.
                break_rejected   <= 1'b1;
                c_break_rejected <= bump(c_break_rejected, 1'b1);
                run_q            <= '0;
                state_q          <= A_RUNNING;
              end
            end
          end
 
          A_ALIGNED: begin
            // Aligned for this frame. Stay until carrier drops.
          end
 
          default: state_q <= A_IDLE;
        endcase
      end
    end
  end
 
  assign state = state_q;
 
endmodule

Classification: synthesizable.

What it teaches: that the marker is not the evidence — the run of alternation before it is. A detector that fires on the break alone aligns on noise, on idle, and on the tail of another station's activity, and then reads whatever follows as a destination address.

Deliberately simplified: alignment is declared on the break and held until carrier drops, with no re-validation. A production design cross-checks against the frame's structure, since a false alignment usually produces an immediately implausible frame.

Production implication: shortest_accepted_run is the number to trend, and it is the chapter's most useful output. A run that was 55 when the equipment was installed and is now just above MIN_RUN means the path has gained regenerations — Section 12's mechanism — and the next one added will start rejecting frames. That is months of warning, and it is invisible to any count of frames received.

And break_rejected is not an error. It is the detector correctly declining insufficient evidence. Counting it as an error produces alarms on a working link; counting it separately makes it the leading indicator of the same trend.

Later ownership: the frame structure that a false alignment would violate is Chapter 5.1's parser.

7. False Detection, Quantified

Section 4 claimed a one-bit marker is unique only within alternation. This puts a number on what that costs as the evidence requirement is relaxed.

The model, stated as illustrative: suppose the receiver is looking at a stream of independent random bits — noise, or an unrelated pattern — rather than a preamble. The probability that any given bit repeats its predecessor is one half. A false alignment requires a run of MIN_RUN alternating bits followed by a repeat:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
P(false alignment at a given bit) = (1/2)^MIN_RUN × (1/2)
                                  = (1/2)^(MIN_RUN + 1)

Worked for three settings:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
MIN_RUN =  4  ->  (1/2)^5   = 1 in 32
MIN_RUN = 16  ->  (1/2)^17  = 1 in 131,072
MIN_RUN = 32  ->  (1/2)^33  = 1 in about 8.6 billion

These are illustrative figures from a deliberately crude model — real line noise is not independent random bits, and idle patterns are structured rather than random, which changes the numbers in ways this model cannot predict. The relationship is what to carry: false-detection probability falls exponentially with the evidence requirement, so each additional bit of required run halves it.

8. Why the Preamble Arrives Shorter Than It Was Sent

Chapter 4.3 §4 established that the preamble is consumed below the MAC and regenerated at each transmitter. That has a consequence which turns out to be the most practically useful fact in the chapter.

Its length is not preserved end to end.

Every device that receives and retransmits — a repeater, a PHY with a retiming function, anything that recovers a signal and drives a fresh one — spends some of the preamble acquiring its own timing before it starts forwarding. What it forwards is what remains. The device then generates a fresh preamble of its own on transmit, but a device that regenerates while forwarding rather than storing the whole frame passes on a shortened one.

So the preamble length a receiver observes is a measurement of the path, not a property of the frame:

Received preambleWhat it suggests
full lengtha direct link, or a store-and-forward device that regenerated it
a few bits shortone regeneration in the path
substantially shortseveral, or a device with a slow acquisition
shorter than MIN_RUNalignment will start failing

That is a genuinely useful diagnostic, and it is available for free from Section 6's shortest_accepted_run.

9. RTL 3 — Preamble Length as a Path Measurement

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE INSTRUMENTATION.
//
// The preamble's length is a PATH MEASUREMENT, not a frame property. So
// this records a distribution rather than checking against a constant.
//
// What the distribution says:
//   tight at full length      -> a direct link, or store-and-forward hops
//   tight at a shorter value  -> a stable path with a known regeneration count
//   BROADENING over time      -> the path has changed, or a device is
//                                acquiring more slowly than it used to
//   creeping toward MIN_RUN   -> alignment will begin failing
//
// The last is the actionable one and it precedes any failure by months.
module preamble_length_histogram #(
  parameter int unsigned MAX_BITS = 64,
  parameter int unsigned BINS     = 8,          // 8 bits per bin
  parameter int unsigned BIN_W    = $clog2(BINS),
  parameter int unsigned CNT_W    = 20,
  parameter int unsigned MIN_RUN  = 16
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic                      align_event,
  input  logic [$clog2(MAX_BITS+1)-1:0] run_length,
 
  output logic [CNT_W-1:0] bin_count [BINS],
 
  // Shortest and longest ever accepted. Both survive `clear`: they describe
  // the installed path, not a measurement window.
  output logic [$clog2(MAX_BITS+1)-1:0] shortest_ever,
  output logic [$clog2(MAX_BITS+1)-1:0] longest_ever,
 
  // Margin between the shortest run seen and the requirement. THIS is the
  // number to alarm on -- it shrinks as the path gains regenerations, and it
  // reaches zero before any frame is rejected.
  output logic [$clog2(MAX_BITS+1)-1:0] run_margin,
  output logic                          margin_low
);
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v,
                                            input logic             en);
    bump = (en && !(&v)) ? (v + 1'b1) : v;
  endfunction
 
  wire [BIN_W-1:0] bin_c = BIN_W'(run_length >> 3);   // 8 bits per bin
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      for (int unsigned b = 0; b < BINS; b++) bin_count[b] <= '0;
      shortest_ever <= '1;
      longest_ever  <= '0;
    end else begin
      if (clear) begin
        for (int unsigned b = 0; b < BINS; b++) bin_count[b] <= '0;
        // shortest_ever and longest_ever deliberately survive.
      end else if (align_event) begin
        bin_count[bin_c] <= bump(bin_count[bin_c], 1'b1);
      end
 
      if (align_event) begin
        if (run_length < shortest_ever) shortest_ever <= run_length;
        if (run_length > longest_ever)  longest_ever  <= run_length;
      end
    end
  end
 
  always_comb begin
    run_margin = (shortest_ever > ($clog2(MAX_BITS+1))'(MIN_RUN))
               ? (shortest_ever - ($clog2(MAX_BITS+1))'(MIN_RUN))
               : '0;
    // Alarm well before zero. By the time margin reaches zero, frames are
    // already being rejected.
    margin_low = (run_margin < ($clog2(MAX_BITS+1))'(8));
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that a distribution carries information a threshold check destroys. A tight distribution at a shorter-than-full length is a healthy path with a known regeneration count. A broadening one means the path has changed. Neither is visible to a check that only asks whether each frame passed.

Deliberately simplified: eight bins of eight bits. A production histogram is finer near the requirement, where the interesting movement happens.

Production implication: run_margin is the alarm, and it must fire well above zero. When margin reaches zero, frames are already being rejected — so the useful alarm is at a margin of a few octets, which gives time to investigate a path change before it becomes an outage. This is the same slope-before-cliff discipline the track has applied at four other layers: eye margin, pre-correction error rate, skew margin, buffer drift, and now preamble margin.

10. RTL 4 — Telemetry That Separates Three Failures

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE INSTRUMENTATION.
//
// Three failures, one symptom ("no frames"), three different owners:
//
//   NO CARRIER          -> nothing is arriving. The PHY, the link, the far
//                          end. Chapter 3.8's ladder, below this layer.
//   CARRIER, NO ALIGN   -> a signal is present and no delimiter is found.
//                          A shortened preamble, a bit-order mismatch, or
//                          a MIN_RUN set too high for this path.
//   ALIGNED, NO FRAME   -> alignment succeeded and what followed was not a
//                          frame. Very often a FALSE alignment on noise.
//
// A single "no frames received" counter names none of them.
module preamble_telemetry #(
  parameter int unsigned CNT_W = 24,
  // Bit times of carrier without an alignment before it is called a stall.
  parameter int unsigned STALL_BITS = 4096,
  parameter int unsigned ST_W = $clog2(STALL_BITS + 1)
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic bit_valid,
  input  logic carrier,
  input  logic aligned,
  input  logic frame_parsed_ok,     // from Chapter 5.1's parser
  input  logic break_rejected,
 
  output logic [CNT_W-1:0] c_carrier_events,
  output logic [CNT_W-1:0] c_alignments,
  output logic [CNT_W-1:0] c_frames_ok,
  output logic [CNT_W-1:0] c_break_rejected,
 
  // Carrier present for a long time with no alignment. Points at the
  // preamble or the detector, not at the link.
  output logic             align_stall,
  output logic [CNT_W-1:0] c_align_stall,
 
  // Aligned, then the parser found no valid frame. The signature of a FALSE
  // alignment, and Section 7 explains why it happens.
  output logic             suspected_false_align,
  output logic [CNT_W-1:0] c_suspected_false_align,
 
  // First failure kind since reset, held. During an incident all three will
  // have counts and only ordering says which came first.
  output logic [1:0]       first_failure,
  output logic             first_failure_valid
);
 
  logic [ST_W-1:0] since_align_q;
  logic            carrier_q, aligned_q;
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v,
                                            input logic             en);
    bump = (en && !(&v)) ? (v + 1'b1) : v;
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      since_align_q           <= '0;
      carrier_q               <= 1'b0;
      aligned_q               <= 1'b0;
      c_carrier_events        <= '0;
      c_alignments            <= '0;
      c_frames_ok             <= '0;
      c_break_rejected        <= '0;
      c_align_stall           <= '0;
      c_suspected_false_align <= '0;
      align_stall             <= 1'b0;
      suspected_false_align   <= 1'b0;
      first_failure           <= '0;
      first_failure_valid     <= 1'b0;
    end else begin
      align_stall           <= 1'b0;
      suspected_false_align <= 1'b0;
 
      if (clear) begin
        c_carrier_events        <= '0;
        c_alignments            <= '0;
        c_frames_ok             <= '0;
        c_break_rejected        <= '0;
        c_align_stall           <= '0;
        c_suspected_false_align <= '0;
      end else begin
        c_carrier_events <= bump(c_carrier_events, carrier && !carrier_q);
        c_alignments     <= bump(c_alignments,     aligned);
        c_frames_ok      <= bump(c_frames_ok,      frame_parsed_ok);
        c_break_rejected <= bump(c_break_rejected, break_rejected);
      end
 
      carrier_q <= carrier;
      aligned_q <= aligned;
 
      // Carrier with no alignment for a long time.
      if (!carrier || aligned) begin
        since_align_q <= '0;
      end else if (bit_valid && (since_align_q != '1)) begin
        since_align_q <= since_align_q + 1'b1;
        if (since_align_q == ST_W'(STALL_BITS - 1)) begin
          align_stall   <= 1'b1;
          c_align_stall <= bump(c_align_stall, 1'b1);
          if (!first_failure_valid) begin
            first_failure       <= 2'd1;
            first_failure_valid <= 1'b1;
          end
        end
      end
 
      // Aligned and the parser then found nothing valid.
      if (aligned_q && !carrier && !frame_parsed_ok) begin
        suspected_false_align   <= 1'b1;
        c_suspected_false_align <= bump(c_suspected_false_align, 1'b1);
        if (!first_failure_valid) begin
          first_failure       <= 2'd2;
          first_failure_valid <= 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that "no frames received" is three different faults with three owners, and that the ratios between four counters separate them. Carrier events without alignments points at the preamble or the detector; alignments without parsed frames points at false alignment; neither moving points below this layer entirely.

Deliberately simplified: false alignment is inferred from a parse failure following an alignment, which also catches genuinely corrupted frames. A production design correlates with Chapter 5.1 §7's stopping field, since a false alignment typically stops very early.

Production implication: c_suspected_false_align rising alongside c_break_rejected falling is a specific and diagnostic combination: it means MIN_RUN has been lowered — or was set too low — and the detector is now accepting evidence it should decline. That pair of movements identifies a configuration change that neither counter alone would.

11. RTL 5 — The Transmit Side, Where the Shortening Originates

Everything above is receive-side. Section 8's mechanism has a transmitter at the other end of it, and the decision that shortens a preamble is made here.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Preamble generation, and the decision that shortens it.
//
// Emitting the pattern is trivial. The DECISION is not, and it is the
// origin of Section 8's mechanism:
//
//   REGENERATE -- emit a full-length preamble of our own. Downstream sees
//                 the full budget. Requires holding the frame until we are
//                 ready to transmit it, which is store-and-forward latency.
//
//   FORWARD    -- pass on what remains of the incoming preamble after we
//                 have taken what we needed for our own timing. Lower
//                 latency, and EVERY DOWNSTREAM DEVICE gets less budget.
//
// A cut-through device forwards, because it starts transmitting before the
// frame has finished arriving and therefore cannot have generated anything.
// That is Chapter 2.7 §5's latency-against-containment trade appearing again
// in a place it is rarely noticed -- it also trades away preamble budget.
module preamble_generator #(
  parameter int unsigned PREAMBLE_OCTETS = 7,
  parameter logic [7:0]  PREAMBLE_OCTET  = 8'h55,
  parameter logic [7:0]  SFD_OCTET       = 8'hD5,
  parameter int unsigned CNT_W           = 20,
  parameter int unsigned OCT_W           = $clog2(PREAMBLE_OCTETS + 2)
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic       frame_request,      // a frame is ready to send
  input  logic       cut_through,        // forwarding, not regenerating
  input  logic [OCT_W-1:0] inherited_octets,  // preamble left from ingress
  input  logic       out_ready,
 
  output logic       out_valid,
  output logic [7:0] out_octet,
  output logic       out_is_sfd,
  output logic       preamble_done,      // the delimiter has been sent
 
  // How many octets this transmission actually emitted. On a regenerating
  // path this is PREAMBLE_OCTETS; on a forwarding one it is whatever
  // survived, and that is what the next device downstream will measure.
  output logic [OCT_W-1:0] emitted_octets,
 
  // Frames forwarded with a preamble shorter than full length. THIS DEVICE
  // is the one shortening it, and this counter is the only place that fact
  // is visible -- the receiver downstream sees the effect and cannot see
  // the cause.
  output logic [CNT_W-1:0] c_forwarded_short,
  output logic [CNT_W-1:0] c_regenerated_full,
  // The shortest preamble this device has ever passed on. If a downstream
  // link is failing to align, this is the number that says whether this
  // device is responsible.
  output logic [OCT_W-1:0] shortest_emitted
);
 
  typedef enum logic [1:0] { G_IDLE, G_PREAMBLE, G_SFD, G_DONE } gen_state_e;
 
  gen_state_e      state_q;
  logic [OCT_W-1:0] count_q, target_q;
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v,
                                            input logic             en);
    bump = (en && !(&v)) ? (v + 1'b1) : v;   // saturating
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q            <= G_IDLE;
      count_q            <= '0;
      target_q           <= '0;
      out_valid          <= 1'b0;
      out_octet          <= '0;
      out_is_sfd         <= 1'b0;
      preamble_done      <= 1'b0;
      emitted_octets     <= '0;
      c_forwarded_short  <= '0;
      c_regenerated_full <= '0;
      shortest_emitted   <= '1;
    end else begin
      preamble_done <= 1'b0;
 
      if (clear) begin
        c_forwarded_short  <= '0;
        c_regenerated_full <= '0;
        // shortest_emitted deliberately survives: it is a property of what
        // this device does, not of a measurement window.
      end
 
      unique case (state_q)
        G_IDLE: begin
          out_valid <= 1'b0;
          if (frame_request) begin
            // THE DECISION. Regenerating gives downstream the full budget
            // and costs the latency of holding the frame. Forwarding is
            // cheaper and spends someone else's margin.
            target_q  <= cut_through ? inherited_octets
                                     : OCT_W'(PREAMBLE_OCTETS);
            count_q   <= '0;
            state_q   <= G_PREAMBLE;
          end
        end
 
        G_PREAMBLE: if (out_ready) begin
          if (count_q >= target_q) begin
            state_q    <= G_SFD;
            out_valid  <= 1'b1;
            out_octet  <= SFD_OCTET;
            out_is_sfd <= 1'b1;
          end else begin
            out_valid  <= 1'b1;
            out_octet  <= PREAMBLE_OCTET;
            out_is_sfd <= 1'b0;
            count_q    <= count_q + 1'b1;
          end
        end
 
        G_SFD: if (out_ready) begin
          out_valid      <= 1'b0;
          out_is_sfd     <= 1'b0;
          preamble_done  <= 1'b1;
          emitted_octets <= count_q;
          state_q        <= G_DONE;
 
          c_forwarded_short  <= bump(c_forwarded_short,
                                     count_q < OCT_W'(PREAMBLE_OCTETS));
          c_regenerated_full <= bump(c_regenerated_full,
                                     count_q >= OCT_W'(PREAMBLE_OCTETS));
          if (count_q < shortest_emitted) shortest_emitted <= count_q;
        end
 
        G_DONE: state_q <= G_IDLE;
 
        default: state_q <= G_IDLE;
      endcase
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that cut-through forwarding spends preamble budget, and that this is a cost of the latency trade that is almost never counted. Chapter 2.7 §5 framed cut-through as trading error containment for latency. It also trades away preamble budget for every device downstream — and unlike the containment cost, this one accumulates along the path.

Deliberately simplified: inherited_octets is an input rather than measured here. A real forwarding device knows it from its own receive-side alignment, which is Section 6's run_at_align divided by eight.

Production implication: shortest_emitted is the cause-side counterpart to Section 6's shortest_accepted_run, and having both is what makes the fault attributable. A downstream link failing to align sees the effect and has no way to identify which device in the path produced it. This counter, read on each device along the path, identifies the one responsible in a single sweep — and without it the investigation is a bisection of the physical topology.

And c_forwarded_short against c_regenerated_full is a configuration audit. A device believed to be storing and forwarding, whose forwarded-short counter is rising, is actually cutting through — which is a configuration fact that matters for Chapter 2.7 §5's error containment as well as for this chapter's budget.

12. Assertions

Some properties below rest on published values — the preamble and delimiter patterns and the least-significant-bit-first transmission order are defined by IEEE 802.3. The evidence requirement, the histogram and the telemetry are implementation choices, and each property says which it is.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─── FRAME PROPERTY: the delimiter differs from the preamble in one bit ────
// The wire patterns are 10101010 and 10101011. Catches a delimiter constant
// transcribed without the bit-order conversion, which emits a pattern no
// conformant receiver recognises -- the link appears dead with a healthy
// signal.
property p_delimiter_is_one_bit_from_preamble;
  @(posedge clk) disable iff (!rst_n)
  1'b1 |-> ($countones(8'h55 ^ 8'hD5) == 1);
endproperty
 
// ─── Causation: alignment requires sufficient evidence ─────────────────────
// THE property of this chapter. Catches a detector that fires on the break
// alone, which aligns on noise, on idle, and on another station's tail --
// and then reads whatever follows as a destination address.
property p_align_requires_run;
  @(posedge clk) disable iff (!rst_n)
  aligned |-> (run_at_align >= MIN_RUN);
endproperty
 
// ─── Safety: an insufficient break is rejected, not accepted late ──────────
// Catches a detector that remembers a rejected break and accepts it once
// more evidence arrives -- which aligns on the wrong bit.
property p_rejected_break_restarts_run;
  @(posedge clk) disable iff (!rst_n)
  break_rejected |=> (run_q == 0);
endproperty
 
// ─── Safety: alignment does not survive carrier loss ───────────────────────
// Alignment is PER FRAME. Catches a design that carries it over, which is
// the difference from Chapter 3.5's block lock and produces a receiver that
// mis-aligns the first frame after an idle period.
property p_align_clears_on_carrier_loss;
  @(posedge clk) disable iff (!rst_n)
  !carrier |=> (state == A_IDLE);
endproperty
 
// ─── Conservation: a healthy preamble has exactly one static bit ───────────
// Strict alternation plus the delimiter's single break. Catches a damaged
// pattern, which is a TRANSMITTER or PATH fault observed at the receiver.
property p_one_static_bit_per_preamble;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> (window_static_bits <= 1);
endproperty
 
// ─── Conservation: transitions and static bits account for the window ──────
// Catches a bit counted as neither, which biases the density measurement in
// a way no downstream analysis can detect.
property p_window_accounting_closes;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> ((window_transitions + window_static_bits) == WINDOW - 1);
endproperty
 
// ─── Stability: the shortest accepted run never increases ──────────────────
// It is a historical minimum. Catches it being recomputed per window, which
// destroys the trend that is this chapter's early warning.
property p_shortest_run_monotone;
  @(posedge clk) disable iff (!rst_n)
  1'b1 |=> (shortest_accepted_run <= $past(shortest_accepted_run));
endproperty
 
// ─── Stability: historical extremes survive a clear ────────────────────────
// Catches them folded into the clear branch, destroying the record of what
// this path has ever delivered.
property p_extremes_survive_clear;
  @(posedge clk) disable iff (!rst_n)
  clear |=> ((shortest_ever <= $past(shortest_ever))
          && (longest_ever  >= $past(longest_ever)));
endproperty
 
// ─── Causation: the margin alarm precedes rejection ────────────────────────
// Catches an alarm set at zero margin, which fires only once frames are
// already being rejected -- an obituary rather than a warning.
property p_margin_alarm_has_headroom;
  @(posedge clk) disable iff (!rst_n)
  (run_margin == 0) |-> margin_low;
endproperty
 
// ─── Conservation: every alignment lands in exactly one histogram bin ──────
// Catches a binning path that drops entries, biasing the distribution that
// the trend depends on.
property p_alignment_binned_once;
  @(posedge clk) disable iff (!rst_n)
  align_event |=> (bin_count[$past(bin_c)] == $past(bin_count[$past(bin_c)]) + 1);
endproperty
 
// ─── Mutual exclusion: the three failure kinds are distinguished ───────────
// Catches a stall and a false alignment being counted together, which
// conflates two failures with different owners.
property p_failures_distinguished;
  @(posedge clk) disable iff (!rst_n)
  !(align_stall && suspected_false_align);
endproperty
 
// ─── Stability: the first failure kind is held ─────────────────────────────
// Catches it being overwritten by consequences of the first failure, which
// names an effect as the cause.
property p_first_failure_stable;
  @(posedge clk) disable iff (!rst_n)
  first_failure_valid |=> $stable(first_failure);
endproperty
 
// ─── Bounded response: a stall is eventually reported ──────────────────────
// Catches a stall detector that can be starved, leaving carrier present and
// no alignment with nothing recording it.
property p_stall_is_reported;
  @(posedge clk) disable iff (!rst_n)
  (carrier && !aligned && bit_valid) |-> ##[1:STALL_BITS+1] (align_stall || aligned || !carrier);
endproperty
 
// ─── Safety: the delimiter always follows the preamble ─────────────────────
// Catches a generator that can emit the delimiter early or omit it, either
// of which produces a stream no receiver aligns to.
property p_sfd_follows_preamble;
  @(posedge clk) disable iff (!rst_n)
  out_is_sfd |-> ($past(state_q) == G_PREAMBLE);
endproperty
 
// ─── Causation: forwarding mode inherits, regenerating mode does not ───────
// Catches a device configured to regenerate that forwards anyway, which
// spends downstream preamble budget while its configuration says otherwise.
property p_regenerate_emits_full;
  @(posedge clk) disable iff (!rst_n)
  (preamble_done && !$past(cut_through)) |-> (emitted_octets >= PREAMBLE_OCTETS);
endproperty

13. Verification

A three-state alignment machine. From IDLE, carrier and a first bit move to RUNNING, where alternating bits accumulate evidence. A break with enough evidence behind it moves to ALIGNED. A break with insufficient evidence returns to RUNNING with the run restarted. Losing carrier from any state returns to IDLE, because alignment is re-established for every frame.IDLERUNNINGALIGNEDcarrier, first bitcarrier, first bitbreak with enough runbreak with enough runcarrier lostcarrier lostcarrier lost — every framecarrier lost— every frame
Figure 3 — evidence accumulates, a break is judged against it, and carrier loss resets everything.

One transition is deliberately absent from the figure and present in the RTL. A break with insufficient evidence is a self-transition on RUNNING — the run restarts and the machine stays where it is. Drawing it would add a loop that says less than this sentence does, and Section 6's code shows it directly.

Scenarios

  1. A full-length preamble. Verify 55 bits of alternation, alignment on the break, run_at_align reading 55, and exactly one static bit in the window.
  2. A preamble at exactly MIN_RUN. The lower boundary. Verify alignment succeeds and shortest_accepted_run records it.
  3. A preamble one bit below MIN_RUN. Verify break_rejected fires and no alignment occurs — the detector correctly declining.
  4. Alternation with no break at all. Verify no alignment, and that align_stall eventually fires rather than the receiver waiting silently forever.
  5. A break with no preceding alternation. Two identical bits immediately after carrier. Verify rejection — this is the noise case.
  6. Two breaks in quick succession. Verify the first with sufficient run aligns, and the second does not re-align mid-frame.
  7. Carrier lost mid-preamble. Verify the state returns to IDLE and the run is discarded, not carried into the next frame.
  8. Carrier lost while aligned. Verify alignment does not persist — it is per-frame, unlike Chapter 3.5's block lock.
  9. Back-to-back frames at the minimum gap. Verify each frame aligns independently and no run leaks across the boundary.
  10. A damaged preamble with extra static bits. Verify window_static_bits exceeds one and c_degraded_windows advances, while alignment may still succeed if enough alternation remains.
  11. The delimiter pattern, checked against the preamble pattern. Verify they differ in exactly one bit position — a static check that catches a bit-order transcription error at elaboration.
  12. Bit-order inversion injected. Transmit the delimiter most-significant-bit-first and verify no alignment ever occurs despite a healthy signal. This is the failure that looks like a dead link.
  13. Histogram binning. Drive a known distribution of run lengths and verify each lands in the right bin and the bins sum to the alignment count.
  14. shortest_ever and longest_ever across a clear. Verify both survive while the bins zero.
  15. Margin alarm. Drive run lengths creeping down toward MIN_RUN and verify margin_low fires before any rejection occurs. Record the headroom — that is the warning the design actually gives.
  16. No carrier at all. Verify no alignment, no stall attributed to the preamble, and that the failure is left to the layer below.
  17. Carrier with no alignment for a long period. Verify align_stall and first_failure naming it.
  18. Alignment followed by a parse failure. Verify suspected_false_align — the signature of aligning on something that was not a preamble.
  19. MIN_RUN swept across three values. Verify the false-alignment rate falls as it rises and the rejection rate rises with it. A parameter whose trade has never been measured has not been chosen.
  20. Generation, regenerating mode. Verify a full-length preamble is emitted, c_regenerated_full advances, and shortest_emitted stays at full length.
  21. Generation, forwarding mode with a short inherited preamble. Verify the short length is passed on, c_forwarded_short advances, and shortest_emitted records it — this device is the cause a downstream receiver will see as an effect.

What the checker must own

  • A preamble generator with configurable length, not a fixed one. Scenarios 2, 3, 15 and 20 all require it, and a testbench that always emits seven octets cannot test the field's most important property.
  • A bit-order inversion mode, because Scenario 12's failure is invisible to a testbench that generates and checks with the same convention — the classic self-consistency trap.
  • A noise generator for the pre-carrier window, since Scenario 5's false-detection case needs plausible junk rather than silence.
  • Coverage crosses of run length against alignment outcome. The bin (run just below MIN_RUN, rejected) must be well populated — it is correct behaviour and a run that never reaches it has not tested the evidence requirement at all.

14. Debugging — Carrier, Alignment, Then Margin

The symptom: no frames are being received, and the link reports a signal.

Step 1 — read the four counters together. Their pattern names the owner, and no single one does:

Carrier eventsAlignmentsFrames OKWhat it means
zerozerozeronothing is arriving — below this layer, Chapter 3.8's ladder
risingzerozerosignal present, no delimiter found — Step 2
risingrisingzeroaligning on something that is not a frame — Step 4
risingrisingrisingthis layer is healthy; look above it

Step 2 — carrier but no alignment: read run_margin and c_break_rejected. Two causes, and they are distinguishable:

  • c_break_rejected rising — breaks are being seen and declined. The preamble is arriving shorter than MIN_RUN. This is Section 8's mechanism, and it means the path has more regenerations than the setting allows for.
  • c_break_rejected at zero — no break is being seen at all. Either the far end is not sending a delimiter, or it is sending one with the wrong bit order, which produces a pattern no receiver recognises while the signal looks perfect.

Step 3 — read the histogram, not the current value. A distribution tight at a shorter-than-full length is a stable path with a known regeneration count and is fine. A broadening distribution means the path has changed, and that is the finding — often a device replaced with one that acquires more slowly.

Step 4 — alignments without frames means false alignment. Section 7's analysis applies: the evidence requirement is too low for the noise on this path. Cross-check with Chapter 5.1 §7's stopping field — a false alignment stops very early, usually inside the destination address, because what follows the fake delimiter is not a frame.

Step 5 — if everything is working, read margin_low anyway. This is the step people skip because nothing is wrong. A link with zero margin works perfectly until the path gains one regeneration and then fails completely — Scenario 22's cliff. The alarm during the working phase is the entire warning.

The method stated once: the counter pattern names the layer, the rejection count separates a short preamble from a missing delimiter, the histogram distinguishes a stable path from a changing one, and the margin alarm is worth reading on a link that is working — because this failure has no slope.

15. Common Misconceptions

"The preamble is padding that gives the receiver time to wake up."

The wrong model: dead time before the real data.

What it costs: you cannot explain why its pattern is specified rather than arbitrary, why the delimiter differs by one bit rather than being something distinctive, or why shortening it matters. You treat its length as slack rather than as evidence.

The corrected model: it does two jobs with opposite requirements. Alternation gives clock recovery the densest possible transition stream — a transition every bit period. The delimiter's single broken bit gives octet alignment a unique marker. Regularity and uniqueness are opposites, so the field does the first for as long as it can and then breaks it exactly once.

"The delimiter is 0xD5, so the wire carries 11010101."

The wrong model: the hex value is the bit pattern.

What it costs: a design that transmits most-significant-bit-first emits a delimiter no conformant receiver recognises. The signal is healthy, timing recovery works, and the link appears dead — one of the hardest failures in the chapter to diagnose because every physical indicator is fine.

The corrected model: Ethernet transmits least-significant bit first. 0x55 produces 10101010 on the wire and 0xD5 produces 10101011. The hex values are the octet values that produce those patterns under that convention, and they look arbitrary only until the convention is applied.

"Detecting the delimiter means looking for two identical bits."

The wrong model: the marker is the evidence.

What it costs: the receiver aligns on noise, on idle patterns, and on the tail of another station's activity — and then reads whatever follows as a destination address. The frames it produces are garbage that occasionally passes structural checks.

The corrected model: the marker is unique only within alternation, so the evidence is the run of alternation before it. MIN_RUN is the trade, false-detection probability falls exponentially with it, and Section 7's arithmetic shows sixteen bits already gives roughly one in a hundred thousand.

"The preamble arrives as it was sent."

The wrong model: seven octets in, seven octets out.

What it costs: Section 12's rejected property. A receiver requiring full length rejects frames every other receiver accepts, on a path that has done nothing wrong — and the symptom points at intermediate equipment that is behaving correctly.

The corrected model: it is consumed and regenerated at every hop, and a device that regenerates while forwarding passes on what remains after acquiring its own timing. The received length is a measurement of the path, which is why the useful check is sufficiency and the useful metric is the trend.

"Octet alignment is the same problem as block alignment."

The wrong model: both find a boundary in a bit stream, so the same search applies.

What it costs: you build an evidence-accumulating search that is far too slow, because it is affordable once per link and this happens once per frame. Or you carry alignment across frames and mis-align the first frame after an idle period.

The corrected model: Chapter 3.5 §9's block search accumulates evidence over many blocks and is acquired once per link, then maintained. Octet alignment is re-established every frame, so it cannot search — it pays a fixed 64-bit cost to make alignment a single unambiguous detection instead.

16. Interview Reasoning

"What is the preamble for?"

The weak answer is "synchronisation", which is true and undifferentiated. The answer that ends the topic names two jobs with opposite requirements — bit timing wants maximum regularity, octet alignment wants uniqueness, and a perfectly regular pattern is maximally ambiguous about position. Then it gives the resolution: be regular for as long as possible, break it exactly once, and make the break as small as possible because every bit spent on distinctiveness is a bit not spent on timing.

"Why is the delimiter only one bit different from the preamble?"

Because one bit is already sufficient. Two consecutive identical bits cannot occur in strict alternation, so a single break is unambiguous the moment it appears — and anything larger buys detection margin that is not needed while costing acquisition time from a fixed 64-bit budget. The strong follow-up is what the small marker costs: it is unique only within alternation, so detection must require a run of alternation as evidence rather than firing on the marker alone.

"A link works back-to-back and fails through one switch. Where do you look?"

Not at the switch. A very likely cause is a receiver requiring a full-length preamble — which is not preserved, because every regenerating device consumes some of it acquiring its own timing. The switch is behaving correctly. Naming the preamble's length as a path measurement rather than a frame property, and pointing at run_margin as the reading that would have shown it coming, is what separates a complete answer.

17. Understanding Check

Because it solves two problems whose requirements are opposites.

Bit synchronisation wants maximum regularity. Chapter 2.6 established that no clock is transmitted, so timing is extracted from transitions — and a pattern with a transition every bit period gives the recovery loop the densest possible edge stream. Fifty-six bits of alternation is fifty-six independent corrections before any data arrives.

Octet alignment wants uniqueness. The receiver must know which bit begins an octet, and that requires a pattern occurring at exactly one position.

These conflict directly. A perfectly alternating pattern looks identical at every offset — shift it by two bits and it is unchanged. Perfect regularity destroys positional information by construction, so the same pattern cannot do both jobs.

The resolution is sequence, not compromise: be maximally regular for as long as possible, then break the regularity exactly once. The delimiter is that break.

The follow-up to be ready for: why not a more distinctive marker? Because the budget is fixed at 64 bit times and every bit spent on distinctiveness is a bit not spent on timing. One broken bit is already unique within alternation, so it is the minimum — and the minimum is what the standard uses.

18. What's Next

The claim this chapter defended: the preamble solves two problems whose requirements are opposites, by doing the first for as long as it can and then breaking it exactly once.

Alternation gives clock recovery a transition every bit period — the densest timing evidence a pattern can carry. The delimiter's single broken bit gives octet alignment a unique marker, and it is one bit because one bit is already sufficient within alternation and every additional bit would be taken from the timing budget. The marker's smallness is what forces the evidence requirement: it is unique only within alternation, so a detector must judge a break against the run that preceded it.

And because the field is consumed and regenerated at every hop, its received length is a measurement of the path rather than a property of the frame — which makes it a free diagnostic and makes the obvious assertion a category error.

Chapter 5.3 — The 48-Bit MAC Address opens the next field group. Chapter 5.1 placed the destination address second because it is the earliest point a station can abandon a frame, and Chapter 2.7 §4 built the filter that acts there — branching on a bit in the first octet without saying why that bit is in that position.

5.3 opens the address as a structured value: an organisationally assigned prefix, and two flag bits placed where they are so that receive hardware can branch on them before the address has finished arriving. That placement is not a convention, and the reason connects directly to this chapter's bit-ordering discipline.

The full path is on the Ethernet curriculum index.

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.