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.
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.
preamble on the wire: 1010 1010 1010 1010 ... (7 octets, 56 bits)
transitions: every single bit periodNothing 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 cyclesThis 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
// 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
endmoduleClassification: 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
// 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;
endmoduleClassification: 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:
P(false alignment at a given bit) = (1/2)^MIN_RUN × (1/2)
= (1/2)^(MIN_RUN + 1)Worked for three settings:
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 billionThese 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 preamble | What it suggests |
|---|---|
| full length | a direct link, or a store-and-forward device that regenerated it |
| a few bits short | one regeneration in the path |
| substantially short | several, or a device with a slow acquisition |
shorter than MIN_RUN | alignment 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
// 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
endmoduleClassification: 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
// 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
endmoduleClassification: 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.
// 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
endmoduleClassification: 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.
// ─── 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);
endproperty13. Verification
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
- A full-length preamble. Verify 55 bits of alternation, alignment on the break,
run_at_alignreading 55, and exactly one static bit in the window. - A preamble at exactly
MIN_RUN. The lower boundary. Verify alignment succeeds andshortest_accepted_runrecords it. - A preamble one bit below
MIN_RUN. Verifybreak_rejectedfires and no alignment occurs — the detector correctly declining. - Alternation with no break at all. Verify no alignment, and that
align_stalleventually fires rather than the receiver waiting silently forever. - A break with no preceding alternation. Two identical bits immediately after carrier. Verify rejection — this is the noise case.
- Two breaks in quick succession. Verify the first with sufficient run aligns, and the second does not re-align mid-frame.
- Carrier lost mid-preamble. Verify the state returns to
IDLEand the run is discarded, not carried into the next frame. - Carrier lost while aligned. Verify alignment does not persist — it is per-frame, unlike Chapter 3.5's block lock.
- Back-to-back frames at the minimum gap. Verify each frame aligns independently and no run leaks across the boundary.
- A damaged preamble with extra static bits. Verify
window_static_bitsexceeds one andc_degraded_windowsadvances, while alignment may still succeed if enough alternation remains. - 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.
- 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.
- 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.
shortest_everandlongest_everacross a clear. Verify both survive while the bins zero.- Margin alarm. Drive run lengths creeping down toward
MIN_RUNand verifymargin_lowfires before any rejection occurs. Record the headroom — that is the warning the design actually gives. - No carrier at all. Verify no alignment, no stall attributed to the preamble, and that the failure is left to the layer below.
- Carrier with no alignment for a long period. Verify
align_stallandfirst_failurenaming it. - Alignment followed by a parse failure. Verify
suspected_false_align— the signature of aligning on something that was not a preamble. MIN_RUNswept 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.- Generation, regenerating mode. Verify a full-length preamble is emitted,
c_regenerated_fulladvances, andshortest_emittedstays at full length. - Generation, forwarding mode with a short inherited preamble. Verify the short length is passed on,
c_forwarded_shortadvances, andshortest_emittedrecords 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 events | Alignments | Frames OK | What it means |
|---|---|---|---|
| zero | zero | zero | nothing is arriving — below this layer, Chapter 3.8's ladder |
| rising | zero | zero | signal present, no delimiter found — Step 2 |
| rising | rising | zero | aligning on something that is not a frame — Step 4 |
| rising | rising | rising | this 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_rejectedrising — breaks are being seen and declined. The preamble is arriving shorter thanMIN_RUN. This is Section 8's mechanism, and it means the path has more regenerations than the setting allows for.c_break_rejectedat 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
Related tutorials
- Related topic
The Shared-Medium Problem
Why several independent transmitters on one medium is a distributed timing problem, not a formatting problem. Propagation delay makes every station's view of the medium stale, so two locally correct decisions can still collide — and that is the constraint the Ethernet MAC was built around.
- Related topic
CSMA/CD, Collision Domains and Slot Time
Slot time is the parameter the whole half-duplex MAC hangs on: it bounds medium acquisition, bounds a collision fragment, and is the retransmission quantum. Deriving it from round-trip propagation plus jam is what fixes Ethernet's minimum frame size — a timing constant wearing a frame-format costume.
- Related topic
Packet Switching
A circuit allocates capacity in advance and guarantees it; a packet network allocates on demand and guarantees nothing. The exchange is measurable in RTL — idle reserved slots against buffered, delayed and occasionally dropped packets — and it is why a packet must describe its own extent and destination.
- Related topic
From Coax to Twisted Pair to Switched Links
Coax, repeater, hub, bridge, switch — four steps, and only the last touched contention. A repeater reproduces a signal and cannot buffer, so it spends collision-domain budget and partitions nothing; a bridge holds the whole frame, and that buffer is what makes every other capability possible.
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.
