Ethernet · Module 5
Interframe Gap and Idle
Idle is not an absence — it keeps clock recovery locked, supplies the symbols rate compensation spends, and holds the line code's invariants between frames. And on a wide interface the minimum gap is an average held by a bounded deficit, not a floor honoured every time.
Chapter 5.8 excluded the interframe gap from the check sequence's coverage in a single line: it is not data, so there is nothing there to protect.
That line is true about the check sequence and misleading about the gap.
Twelve octet times separate one frame from the next — 96 bit times, at whatever rate the link runs. It is described as idle, and idle sounds like an absence: the transmitter has stopped, nothing is happening, the wire is waiting.
Nothing is waiting. The wire is carrying a signal for the entire gap, that signal is doing three separate jobs, and a link on which the gap were genuinely empty would fail within microseconds.
And there is a second surprise, which is the one with consequences for RTL. On a wide interface a frame cannot start at an arbitrary octet — it must begin on a lane boundary. So the gap cannot be exactly twelve octets whenever the previous frame's length is not a multiple of the interface width. The gap must be rounded, and rounding it up every time would slowly lose throughput.
So the standard does something more interesting than fixing a minimum: it fixes an average, and lets individual gaps fall below it, provided a bounded deficit is carried and repaid.
1. Scope — What This Chapter Owns
Chapter 3.5 owns the encoding — what an idle symbol actually is in a given line code, and why the code must keep transitions alive whether or not data is present. This chapter takes those symbols as given and asks what the gap made of them is for.
Chapter 4.4 owns clock compensation — why two ends of a link run at slightly different rates and how an elastic buffer absorbs the difference. It established that the buffer needs deletable symbols; this chapter is where those symbols live.
Chapter 5.8 owns what the check sequence covers, and excluded the gap.
This chapter owns the gap as a quantity: why its minimum is 96 bit times, what occupies it, why it is measured in octets on one interface and in time on another, how a bounded deficit lets individual gaps fall below the minimum while the average holds, and what a receiver may and may not conclude from a gap it measured.
It does not own: the transmit arbitration that decides which frame goes next, which is a queueing subject; nor flow control, which suspends transmission for far longer than a gap and belongs to a later module.
The question this chapter answers that its neighbours do not: what is happening on the wire when nothing is being sent, and why is the requirement an average rather than a minimum?
2. Why 96 Bit Times
The number has the same shape as Chapter 5.6's 64 octets — it is a recovery time expressed in bits — but it survives for better reasons.
A receiver has work to do at the end of a frame. The check sequence must be verified, the frame's disposition decided, the receive buffer's pointers advanced, and the physical layer's state returned to a condition from which it can detect the next preamble. On the original shared medium there was more: the medium itself had to settle, and every station had to observe the channel as idle before contending for it.
96 bit times was enough for all of that on the slowest hardware that had to do it, and — like the frame floor — it was expressed in bits rather than in seconds so that it would scale with the signalling rate.
Which produces a consequence worth pausing on. Twelve octet times is a very different amount of time at different rates:
| Rate | 96 bit times |
|---|---|
| 10 Mb/s | 9.6 µs |
| 100 Mb/s | 0.96 µs |
| 1 Gb/s | 96 ns |
| 10 Gb/s | 9.6 ns |
| 100 Gb/s | 0.96 ns |
Illustrative arithmetic, and read the last row. Under a nanosecond of recovery time — for a receiver whose per-frame work has not become a thousand times cheaper. The gap did not stay adequate because it was generous; it stayed adequate because the work moved into deeper pipelines that no longer need to complete before the next frame arrives.
3. What Occupies the Gap
Take the middle column, because it is the one that connects this chapter to a mechanism already built.
Chapter 4.4 showed that the two ends of a link run from independent references, so the receiver's recovered clock is slightly faster or slower than its own transmit clock. An elastic buffer absorbs the difference — and it absorbs it by deleting a symbol when it is filling and inserting one when it is emptying.
Those symbols have to be ones nobody needs, which means they have to be idle. The gap is the only place they exist. A link whose gap were reduced to nothing would leave the elastic buffer with no adjustment to make, and the drift would accumulate until the buffer overflowed or underflowed — which is a frame lost or corrupted, on a link where nothing is wrong with the signal.
Which reframes the minimum entirely. The gap is not slack the transmitter is being asked to leave for politeness. It is a budget the physical layer spends, and the amount it spends depends on how far apart the two ends' frequencies are — a quantity neither end controls and both must tolerate.
And the third column has the same character. Chapter 3.5's codes maintain transition density and DC balance so that a receiver's clock recovery and its slicer keep working. Those invariants must hold between frames as well as within them, so the gap carries encoded symbols chosen to satisfy them — not an absence of signal, which would satisfy none of them.
4. Octets on One Interface, Time on Another
The gap is specified in bit times, and that is not the same as being specified in octets — a distinction that is invisible at 10 Mb/s and unavoidable at 10 Gb/s.
On a narrow interface the two coincide. One octet per clock, so twelve octet times is twelve clocks, and a counter that counts to twelve is an exact implementation.
On a wide interface they do not. An interface that transfers four octets per clock can only start a frame at an octet position that is a multiple of four — a lane boundary — because the control character that marks the start of a frame occupies a specific lane. So the gap cannot be twelve octets unless the previous frame's length happens to leave the alignment right.
Work an example. Suppose a frame ends leaving the next transmission opportunity two lanes into a four-lane group. A twelve-octet gap would place the next frame's start on lane 2, which is not permitted. The transmitter must round: either up to fourteen octets, or down to ten.
Rounding always up is correct and wasteful. Every frame whose length is not a multiple of the interface width pays extra idle, and on a link saturated with small frames that is a measurable throughput loss for no benefit.
Rounding down is what actually happens, under a bound, and Section 5 is the mechanism that makes it safe.
5. RTL 1 — The Deficit, and Why It Must Be Bounded
// SYNTHESIZABLE.
//
// Enforces the interframe gap on a wide interface, where the gap is
// quantised to the interface width and cannot always be the nominal
// value.
//
// The mechanism is a DEFICIT: octets of gap this transmitter owes.
//
// gap shorter than nominal -> deficit increases by the shortfall
// gap longer than nominal -> deficit decreases by the excess
//
// Bounded at both ends. The upper bound is what makes the guarantee an
// average rather than a hope: with a deficit that can never exceed
// MAX_DEFICIT, the shortfall over ANY interval is at most MAX_DEFICIT
// octets, so the mean gap over a run of frames converges to the nominal
// from above.
package ifg_pkg;
// NORMATIVE. 96 bit times.
localparam int unsigned IFG_OCTETS = 12;
// Deficit bound. With a four-octet interface the worst rounding is
// three octets, so three is the smallest bound that never forces a
// round-up -- and a larger bound would permit gaps shorter than any
// alignment requires.
localparam int unsigned MAX_DEFICIT = 3;
// Shortest single gap this permits: nominal minus the bound.
localparam int unsigned MIN_SINGLE_GAP = IFG_OCTETS - MAX_DEFICIT; // 9
endpackage
module ifg_enforcer
import ifg_pkg::*;
#(
// Octets per transfer. A frame may only start on a boundary of this.
parameter int unsigned LANES = 4,
parameter int unsigned DEF_W = $clog2(MAX_DEFICIT + 1)
) (
input logic clk,
input logic rst_n,
input logic frame_end, // last octet of a frame went out
input logic [2:0] end_lane, // lane the frame ended on
input logic want_start, // a frame is queued and ready
output logic start_permitted,
output logic [7:0] gap_emitted, // octets of gap in the last interval
output logic gap_valid,
output logic [DEF_W-1:0] deficit,
// High when this gap was shortened. Not an error -- a permitted use of
// the deficit, counted so the rate is visible.
output logic gap_shortened
);
logic [7:0] count_q;
logic in_gap_q;
// The gap this alignment would produce if the frame started at the next
// permitted boundary at or after the nominal gap.
wire [7:0] nominal_end = 8'(IFG_OCTETS);
wire [7:0] aligned_up = ((count_q + 8'(LANES) - 8'd1) / 8'(LANES)) * 8'(LANES);
wire [7:0] aligned_down = (count_q / 8'(LANES)) * 8'(LANES);
// Shortening is permitted only when the deficit can absorb it. This one
// condition is the entire safety argument.
wire can_shorten = (count_q >= 8'(MIN_SINGLE_GAP)) &&
((8'(IFG_OCTETS) - count_q) <= 8'(MAX_DEFICIT)) &&
((deficit + DEF_W'(8'(IFG_OCTETS) - count_q)) <= DEF_W'(MAX_DEFICIT));
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count_q <= '0;
in_gap_q <= 1'b0;
start_permitted <= 1'b0;
gap_emitted <= '0;
gap_valid <= 1'b0;
deficit <= '0;
gap_shortened <= 1'b0;
end else begin
start_permitted <= 1'b0;
gap_valid <= 1'b0;
gap_shortened <= 1'b0;
if (frame_end) begin
in_gap_q <= 1'b1;
count_q <= '0;
end else if (in_gap_q) begin
count_q <= count_q + 8'(LANES);
if (want_start) begin
// Two candidate start points exist because of the alignment.
// Taking the earlier one BORROWS from the deficit; taking the
// later one REPAYS it.
if ((count_q >= nominal_end) || can_shorten) begin
start_permitted <= 1'b1;
in_gap_q <= 1'b0;
gap_emitted <= count_q;
gap_valid <= 1'b1;
if (count_q < 8'(IFG_OCTETS)) begin
deficit <= deficit + DEF_W'(8'(IFG_OCTETS) - count_q);
gap_shortened <= 1'b1;
end else if (deficit != '0) begin
// Repay, saturating at zero. Credit does NOT accumulate: a
// long idle period must not buy the right to a run of short
// gaps later, which is what an unsaturated counter would do.
if (DEF_W'(count_q - 8'(IFG_OCTETS)) >= deficit) deficit <= '0;
else deficit <= deficit - DEF_W'(count_q - 8'(IFG_OCTETS));
end
end
end
end
end
end
endmoduleClassification: synthesizable.
What it teaches: that the repayment must saturate at zero and the borrowing must not. Letting the deficit go negative — accumulating credit during an idle link — would permit a long quiet period to buy a run of short gaps afterwards, which is exactly the burst a receiver cannot absorb. The guarantee is about every interval, not about the long run, and an unsaturated counter silently converts the first into the second.
Deliberately simplified: a single lane width and a single nominal value. Real interfaces differ in width across Chapter 4.5's family of xMII variants, and the module is parameterised so the alignment arithmetic does not have to be rewritten per interface.
Production implication: gap_shortened is not an error output, and naming it as one is the mistake this module exists to prevent. A shortened gap is a permitted, deliberate use of the mechanism; a design that counts it as a fault reports a healthy transmitter as defective, and — worse — invites somebody to "fix" it by forcing every gap to the nominal, which surrenders the throughput the mechanism was built to recover.
6. RTL 2 — The Gap Distribution, and What Its Shape Says
// SYNTHESIZABLE INSTRUMENTATION.
//
// Measures the gap DISTRIBUTION and its running mean, because the
// requirement is a mean and a minimum-only check cannot express it.
//
// The bucket edges are placed to make the three conclusions of Figure 2
// separable in one read:
//
// below MIN_SINGLE_GAP -> non-conforming, whatever the mean
// MIN_SINGLE_GAP..11 -> shortened, legitimate if the mean holds
// exactly 12 -> nominal
// 13 and above -> repayment, or simply a quiet link
module gap_histogram
import ifg_pkg::*;
#(
parameter int unsigned CNT_W = 40,
parameter int unsigned NBUCKET = 6,
parameter int unsigned WINDOW = 65_536,
parameter int unsigned WIN_W = $clog2(WINDOW + 1)
) (
input logic clk,
input logic rst_n,
input logic clear,
input logic gap_valid,
input logic [7:0] gap_octets,
output logic [CNT_W-1:0] bucket [NBUCKET],
output logic [CNT_W-1:0] gaps_total,
// The number the requirement is actually about. Reported per window,
// in tenths of an octet, because the mean of a conforming transmitter
// sits just above twelve and an integer mean cannot show the margin.
output logic [CNT_W-1:0] mean_tenths,
output logic mean_valid,
// Smallest gap ever observed. Survives `clear`: one gap below the
// permitted minimum is a finding, and a count of them is not.
output logic [7:0] smallest_gap,
output logic smallest_valid
);
logic [WIN_W-1:0] win_q;
logic [CNT_W-1:0] sum_q, n_q;
function automatic int unsigned bucket_of(input logic [7:0] g);
if (g < 8'(MIN_SINGLE_GAP)) bucket_of = 0; // non-conforming
else if (g < 8'd11) bucket_of = 1; // 9..10 shortened
else if (g == 8'd11) bucket_of = 2; // one short
else if (g == 8'(IFG_OCTETS)) bucket_of = 3; // nominal
else if (g <= 8'd16) bucket_of = 4; // repayment range
else bucket_of = 5; // idle link
endfunction
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < NBUCKET; i++) bucket[i] <= '0;
gaps_total <= '0;
win_q <= '0;
sum_q <= '0;
n_q <= '0;
mean_tenths <= '0;
mean_valid <= 1'b0;
smallest_gap <= '1;
smallest_valid <= 1'b0;
end else begin
mean_valid <= 1'b0;
if (clear) begin
for (int i = 0; i < NBUCKET; i++) bucket[i] <= '0;
gaps_total <= '0;
// smallest_gap deliberately survives.
end else if (gap_valid) begin
automatic int unsigned b = bucket_of(gap_octets);
if (!(&bucket[b])) bucket[b] <= bucket[b] + 1'b1;
if (!(&gaps_total)) gaps_total <= gaps_total + 1'b1;
sum_q <= sum_q + CNT_W'(gap_octets);
n_q <= n_q + 1'b1;
if (!smallest_valid || (gap_octets < smallest_gap)) begin
smallest_gap <= gap_octets;
smallest_valid <= 1'b1;
end
end
if (win_q == WIN_W'(WINDOW - 1)) begin
// Tenths, and the division happens once per window rather than
// once per gap -- the same placement argument as Chapter 5.6 §12.
mean_tenths <= (n_q == '0) ? '0 : ((sum_q * 10) / n_q);
mean_valid <= 1'b1;
sum_q <= '0; n_q <= '0; win_q <= '0;
end else begin
win_q <= win_q + 1'b1;
end
end
end
endmoduleClassification: synthesizable instrumentation.
What it teaches: that the mean needs a fractional part and a count does not. A conforming transmitter's mean gap sits just above twelve — twelve point one, twelve point four — and an integer mean reports "12" for a transmitter that is exactly conforming and for one that is fractionally under. Tenths is the smallest resolution that distinguishes margin from violation, and reporting the mean as an integer is a measurement that cannot detect the failure it exists to detect.
Deliberately simplified: six buckets with fixed edges. A design on a wider interface would place its edges at that interface's rounding granularity, because the shortened values cluster there rather than uniformly.
Production implication: bucket 0 and the mean answer different questions and both are required. Bucket 0 counts gaps below the permitted single-gap minimum, which are non-conforming individually regardless of the average. The mean catches a transmitter whose gaps are all individually legal and whose deficit is never repaid — every gap at 11, forever, which passes any per-gap check and violates the requirement. Neither check finds the other's failure.
7. RTL 3 — First Cause on an Undersized Gap
// SYNTHESIZABLE DIAGNOSTIC.
//
// An undersized gap is rare, consequential, and almost impossible to
// reconstruct after the fact -- so the moment it happens is the only
// moment the context exists.
//
// Captures, on the FIRST occurrence only:
// - the gap's size
// - the deficit at the time, which says whether the mechanism was
// involved or bypassed
// - the length of the frame that preceded it, because alignment
// rounding depends on it
// - whether a frame was queued and waiting, which distinguishes a
// scheduler bug from a back-pressure event
module undersized_gap_first_cause
import ifg_pkg::*;
#(
parameter int unsigned DEF_W = 2,
parameter int unsigned CNT_W = 32
) (
input logic clk,
input logic rst_n,
input logic clear,
input logic gap_valid,
input logic [7:0] gap_octets,
input logic [DEF_W-1:0] deficit,
input logic [13:0] prev_frame_octets,
input logic was_queued,
output logic undersized_seen,
output logic [7:0] first_gap,
output logic [DEF_W-1:0] first_deficit,
output logic [13:0] first_prev_frame,
output logic first_was_queued,
output logic [CNT_W-1:0] c_undersized
);
wire undersized = gap_valid && (gap_octets < 8'(MIN_SINGLE_GAP));
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
undersized_seen <= 1'b0;
first_gap <= '0;
first_deficit <= '0;
first_prev_frame <= '0;
first_was_queued <= 1'b0;
c_undersized <= '0;
end else begin
if (clear) begin
c_undersized <= '0;
// The captured context deliberately survives. It is the only
// record of an event that cannot be reproduced on demand.
end
if (undersized) begin
if (!(&c_undersized)) c_undersized <= c_undersized + 1'b1;
if (!undersized_seen) begin
undersized_seen <= 1'b1;
first_gap <= gap_octets;
first_deficit <= deficit;
first_prev_frame <= prev_frame_octets;
first_was_queued <= was_queued;
end
end
end
end
endmoduleClassification: synthesizable diagnostic.
What it teaches: that the captured context is chosen so that each field eliminates a hypothesis. first_deficit at its bound means the mechanism was working and something bypassed it; at zero it means the shortening had nothing to do with the deficit at all, and the enforcement was simply not applied. first_prev_frame tells you whether the preceding frame's length made the alignment awkward, which separates a rounding bug from an unconditional one. first_was_queued separates a scheduler that started too early from a gap that was cut short by something downstream.
Deliberately simplified: first occurrence only. A ring of the last few events is better where the fault is intermittent, and strictly worse where it is not — the first event is the one closest to whatever changed.
Production implication: the captured context survives clear while the count does not, and the asymmetry is deliberate. A count answers "is this still happening"; the context answers "what happened", and only one of those can be reconstructed later. Clearing them together loses the irreplaceable one to a routine action.
8. RTL 4 — Checking That Idle Is Actually Idle
// SYNTHESIZABLE.
//
// Verifies that the symbols occupying the gap are the ones that should be
// there, and counts what is not.
//
// Why this is worth doing at all: on a lightly loaded link, idle is the
// overwhelming majority of what crosses the channel. A bit error rate
// that has not yet produced a single frame error will already have
// produced many idle errors -- so this counter moves FIRST, and moves
// long before anything a user notices.
//
// It is a leading indicator built from a region nothing else examines.
module idle_pattern_monitor
import ifg_pkg::*;
#(
// The expected idle codeword for this line code. Chapter 3.5 owns what
// this actually is; parameterised so the monitor does not encode one
// code's choice.
parameter logic [7:0] IDLE_SYMBOL = 8'h07,
parameter int unsigned CNT_W = 40
) (
input logic clk,
input logic rst_n,
input logic clear,
input logic in_gap,
input logic sym_valid,
input logic [7:0] sym_data,
input logic sym_is_control, // control character, not data
output logic [CNT_W-1:0] c_idle_symbols,
output logic [CNT_W-1:0] c_idle_wrong_value,
output logic [CNT_W-1:0] c_idle_not_control,
// Errors per million idle symbols in the window that just closed. A
// RATE, because the raw count grows with link idleness and says nothing
// on its own.
output logic [CNT_W-1:0] error_ppm,
output logic rate_valid,
output logic [CNT_W-1:0] worst_ppm
);
logic [CNT_W-1:0] win_sym_q, win_err_q;
logic [15:0] win_q;
// The classification. Three outcomes, and the second and third are
// different faults rather than degrees of one.
wire idle_ok = in_gap && sym_valid && sym_is_control && (sym_data == IDLE_SYMBOL);
wire idle_wrong_val = in_gap && sym_valid && sym_is_control && (sym_data != IDLE_SYMBOL);
wire idle_not_ctrl = in_gap && sym_valid && !sym_is_control;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
c_idle_symbols <= '0;
c_idle_wrong_value <= '0;
c_idle_not_control <= '0;
win_sym_q <= '0;
win_err_q <= '0;
win_q <= '0;
error_ppm <= '0;
rate_valid <= 1'b0;
worst_ppm <= '0;
end else begin
rate_valid <= 1'b0;
if (clear) begin
c_idle_symbols <= '0;
c_idle_wrong_value <= '0;
c_idle_not_control <= '0;
// worst_ppm deliberately survives: the worst the channel has ever
// been is a property of the channel, not of a measurement window.
end else if (in_gap && sym_valid) begin
c_idle_symbols <= c_idle_symbols + 1'b1;
win_sym_q <= win_sym_q + 1'b1;
if (idle_wrong_val) c_idle_wrong_value <= c_idle_wrong_value + 1'b1;
if (idle_not_ctrl) c_idle_not_control <= c_idle_not_control + 1'b1;
if (idle_wrong_val || idle_not_ctrl) win_err_q <= win_err_q + 1'b1;
end
if (&win_q) begin
// A RATE, not a count. The raw count grows with how idle the link
// is and therefore says nothing on its own.
error_ppm <= (win_sym_q == '0) ? '0 : ((win_err_q * 1_000_000) / win_sym_q);
rate_valid <= 1'b1;
if (((win_sym_q == '0) ? '0 : ((win_err_q * 1_000_000) / win_sym_q)) > worst_ppm)
worst_ppm <= (win_sym_q == '0) ? '0 : ((win_err_q * 1_000_000) / win_sym_q);
win_sym_q <= '0;
win_err_q <= '0;
win_q <= '0;
end else begin
win_q <= win_q + 1'b1;
end
if (idle_ok) begin end // named for readability; no action needed
end
end
endmoduleClassification: synthesizable.
What it teaches: that an idle error count is meaningless without its denominator. c_idle_symbols grows with how idle the link is, so a quiet link accumulates idle errors simply by being quiet. The rate — errors per million idle symbols — is comparable across links and across time; the raw count is comparable with nothing.
Deliberately simplified: a single expected idle value. Real line codes rotate between several idle codewords to manage running disparity, so a production monitor tests membership in a small set rather than equality with one value — and a monitor that tests equality against a rotating code reports a permanent, enormous error rate.
Production implication: the split matters more than the total, and it is not arbitrary:
c_idle_wrong_value — a control character arrived where idle was expected, but with the wrong value. Consistent with a single bit error inside a codeword, which is a channel story.
c_idle_not_control — something arrived in the gap that is not a control character at all. Consistent with a decoder that lost alignment, so it is interpreting data-position bits as though they were control ones — a synchronisation story, and a much more serious one.
c_idle_symbols — the denominator. Without it, the other two are counts whose meaning depends on how busy the link has been, and a quiet link accumulates idle errors simply because it accumulates idle.
9. RTL 5 — A Scheduler That Cannot Emit a Short Gap
There are two ways to guarantee the gap. One is to check it and report violations; the other is to build a transmitter in which a violation is not expressible.
// SYNTHESIZABLE.
//
// The gap is guaranteed STRUCTURALLY: there is no state from which this
// module can assert `tx_start` too early, so the guarantee does not
// depend on a checker being correct or enabled.
//
// Contrast with Section 5, which enforces the gap by computing whether a
// start is permitted. Both are correct; this one cannot be made incorrect
// by a later edit to the permission logic, because there is no permission
// logic -- the timing is a property of the state graph.
module ifg_safe_scheduler
import ifg_pkg::*;
#(
parameter int unsigned LANES = 4,
parameter int unsigned DEF_W = 2
) (
input logic clk,
input logic rst_n,
input logic tx_done,
input logic frame_ready,
input logic [DEF_W-1:0] deficit_in,
output logic tx_start,
output logic [7:0] gap_octets,
output logic gap_valid,
output logic [DEF_W-1:0] deficit_out
);
typedef enum logic [1:0] { G_XMIT, G_HOLD, G_ARM, G_GO } g_e;
g_e state_q;
logic [7:0] cnt_q;
// The minimum this state machine will EVER wait. Not a comparison
// performed at run time -- a bound the state graph cannot cross,
// because G_HOLD has no exit before the count is reached.
localparam int unsigned FLOOR = MIN_SINGLE_GAP;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_q <= G_XMIT;
cnt_q <= '0;
tx_start <= 1'b0;
gap_octets <= '0;
gap_valid <= 1'b0;
deficit_out <= '0;
end else begin
tx_start <= 1'b0;
gap_valid <= 1'b0;
case (state_q)
G_XMIT: if (tx_done) begin
state_q <= G_HOLD;
cnt_q <= '0;
end
// The ONLY exit from this state is reaching the floor. No input
// can shorten it: frame_ready is not read here, and there is no
// condition on the transition. That is the guarantee.
G_HOLD: begin
cnt_q <= cnt_q + 8'(LANES);
if ((cnt_q + 8'(LANES)) >= 8'(FLOOR)) state_q <= G_ARM;
end
// Between the floor and the nominal, a start is permitted only if
// the deficit can absorb the shortfall. This is where the policy
// lives, and it can only ever make the gap LONGER than G_HOLD
// already guaranteed.
G_ARM: begin
cnt_q <= cnt_q + 8'(LANES);
if (frame_ready) begin
if ((cnt_q >= 8'(IFG_OCTETS)) ||
((deficit_in + DEF_W'(8'(IFG_OCTETS) - cnt_q)) <= DEF_W'(MAX_DEFICIT)))
state_q <= G_GO;
end
end
G_GO: begin
tx_start <= 1'b1;
gap_octets <= cnt_q;
gap_valid <= 1'b1;
deficit_out <= (cnt_q < 8'(IFG_OCTETS))
? (deficit_in + DEF_W'(8'(IFG_OCTETS) - cnt_q))
: ((DEF_W'(cnt_q - 8'(IFG_OCTETS)) >= deficit_in)
? '0 : (deficit_in - DEF_W'(cnt_q - 8'(IFG_OCTETS))));
state_q <= G_XMIT;
end
default: state_q <= G_XMIT;
endcase
end
end
endmoduleClassification: synthesizable.
What it teaches: the difference between a checked guarantee and a structural one. Section 5 computes whether a start is permitted, so its correctness depends on that computation staying correct through every future edit. Here the floor is enforced by a state with no early exit — G_HOLD does not read frame_ready and has no conditional transition — so no change to the policy in G_ARM can produce a gap below the floor. The policy can be got wrong; the floor cannot.
Deliberately simplified: the two states could be merged and the design would be smaller. Keeping them separate is the entire point — merging them puts the floor and the policy in one comparison, where an edit to either can break both.
Production implication: this is worth doing specifically because the failure it prevents is not caught by testing. A gap that is occasionally one octet short passes every functional test, produces no frame errors on a good channel, and fails only against a peer whose receiver is less tolerant than the one it was developed against. A structural guarantee removes the failure from the space of things that need to be tested for, which is the strongest form of assurance available and is not always available.
10. What a Receiver May Conclude From a Gap It Measured
The gap is a transmitter's obligation, and a receiver measuring it is measuring something that has been through a path. That changes what the measurement means.
A gap measured at a receiver is not the gap the transmitter emitted. Chapter 4.4's clock compensation deletes and inserts idle symbols in transit, and every repeater or retiming element on the path may do the same. The gap shrinks along a path, because every device that must absorb a positive frequency difference does so by removing idle.
So a receiver seeing a gap of ten octets has learned very little about the transmitter. The transmitter may have emitted twelve and two may have been consumed by compensation. Both are conforming.
Which is why receivers are required to tolerate gaps well below the nominal, and why a receiver built to reject anything under twelve rejects conforming traffic from a conforming transmitter across a conforming path. The tolerance is not generosity — it is the direct consequence of the gap being the medium in which rate compensation is paid for.
What a receiver can conclude:
| Observation | Conclusion |
|---|---|
| gaps consistently large | the link is not busy — says nothing about anyone's configuration |
| gaps clustered near the nominal | a busy link with a conforming transmitter |
| gaps shrinking over a long path | normal: compensation consumed idle at each hop |
| a gap below the receiver's own tolerance | a real problem, and not necessarily the transmitter's |
| gaps at zero — frames back to back | a transmitter or an intermediate device that is not honouring the gap at all |
And the entry that catches designs out is the fourth. A receiver that cannot handle a short gap will report the transmitter as faulty, when the actual chain is: a conforming transmitter, a path that legitimately consumed idle, and a receiver whose tolerance is narrower than the standard requires. All the evidence sits at the receiver, and the receiver is the fault.
11. Assertions — About an Average, and About a Floor
// ---------------------------------------------------------------------
// P1 -- THE FLOOR. No single gap is ever shorter than the permitted
// minimum, whatever the deficit says. This is the structural guarantee
// of Section 9, asserted so a refactor cannot remove it.
// ---------------------------------------------------------------------
property p_never_below_floor;
@(posedge clk) disable iff (!rst_n)
gap_valid |-> (gap_octets >= 8'(MIN_SINGLE_GAP));
endproperty
a_never_below_floor: assert property (p_never_below_floor)
else $error("gap below the permitted single-gap minimum");
// ---------------------------------------------------------------------
// P2 -- The deficit is bounded above. An unbounded deficit still repays
// eventually, which is a weaker guarantee than the one required.
// ---------------------------------------------------------------------
property p_deficit_bounded;
@(posedge clk) disable iff (!rst_n)
(deficit <= DEF_W'(MAX_DEFICIT));
endproperty
a_deficit_bounded: assert property (p_deficit_bounded);
// ---------------------------------------------------------------------
// P3 -- The deficit saturates at zero. Accumulating CREDIT during an idle
// period would buy a burst of short gaps afterwards -- exactly the burst
// a receiver cannot absorb.
// ---------------------------------------------------------------------
property p_no_credit_accumulation;
@(posedge clk) disable iff (!rst_n)
(deficit >= 0) and (gap_valid && gap_octets > 8'(IFG_OCTETS)) |=> (deficit >= '0);
endproperty
a_no_credit_accumulation: assert property (p_no_credit_accumulation);
// ---------------------------------------------------------------------
// P4 -- A shortened gap always increases the deficit by exactly the
// shortfall. Shortening without recording it is how the average is lost.
// ---------------------------------------------------------------------
property p_shortfall_is_recorded;
@(posedge clk) disable iff (!rst_n)
(gap_valid && gap_octets < 8'(IFG_OCTETS))
|=> (deficit == $past(deficit) + DEF_W'(8'(IFG_OCTETS) - $past(gap_octets)));
endproperty
a_shortfall_is_recorded: assert property (p_shortfall_is_recorded);
// ---------------------------------------------------------------------
// P5 -- A gap may only be shortened when the deficit can absorb it. The
// single safety condition of Section 5.
// ---------------------------------------------------------------------
property p_shorten_only_within_bound;
@(posedge clk) disable iff (!rst_n)
(gap_valid && gap_octets < 8'(IFG_OCTETS))
|-> (($past(deficit) + DEF_W'(8'(IFG_OCTETS) - gap_octets)) <= DEF_W'(MAX_DEFICIT));
endproperty
a_shorten_only_within_bound: assert property (p_shorten_only_within_bound);
// ---------------------------------------------------------------------
// P6 -- THE AVERAGE, which is the actual requirement. Over any window,
// the mean gap is at least the nominal. Note the WINDOW in the property:
// a requirement about a mean cannot be written without one.
// ---------------------------------------------------------------------
property p_mean_gap_at_least_nominal;
@(posedge clk) disable iff (!rst_n)
mean_valid |-> (mean_tenths >= CNT_W'(IFG_OCTETS * 10));
endproperty
a_mean_gap_at_least_nominal: assert property (p_mean_gap_at_least_nominal);
// ---------------------------------------------------------------------
// P7 -- Every frame start is on a lane boundary. The reason the gap
// cannot be a constant in the first place.
// ---------------------------------------------------------------------
property p_start_is_lane_aligned;
@(posedge clk) disable iff (!rst_n)
tx_start |-> ((start_octet_index % LANES) == 0);
endproperty
a_start_is_lane_aligned: assert property (p_start_is_lane_aligned);
// ---------------------------------------------------------------------
// P8 -- The floor state has no early exit. Written over the state graph
// rather than over the gap value, because this is what makes the
// guarantee structural rather than computed (Section 9).
// ---------------------------------------------------------------------
property p_hold_has_no_early_exit;
@(posedge clk) disable iff (!rst_n)
((state == G_HOLD) && (cnt_q + LANES < FLOOR)) |=> (state == G_HOLD);
endproperty
a_hold_has_no_early_exit: assert property (p_hold_has_no_early_exit);
// ---------------------------------------------------------------------
// P9 -- frame_ready cannot shorten the floor. The scheduler must not read
// it in the holding state at all.
// ---------------------------------------------------------------------
property p_ready_does_not_shorten;
@(posedge clk) disable iff (!rst_n)
((state == G_HOLD) && frame_ready) |-> !tx_start;
endproperty
a_ready_does_not_shorten: assert property (p_ready_does_not_shorten);
// ---------------------------------------------------------------------
// P10 -- Exactly one histogram bucket per measured gap.
// ---------------------------------------------------------------------
property p_one_bucket_per_gap;
@(posedge clk) disable iff (!rst_n)
gap_valid |=> (bucket_sum == $past(bucket_sum) + 1);
endproperty
a_one_bucket_per_gap: assert property (p_one_bucket_per_gap);
// ---------------------------------------------------------------------
// P11 -- The non-conforming bucket stays empty on a conforming design.
// A cover on this bucket would be a coverage goal for a BUG.
// ---------------------------------------------------------------------
property p_nonconforming_bucket_empty;
@(posedge clk) disable iff (!rst_n)
(bucket[0] == '0);
endproperty
a_nonconforming_bucket_empty: assert property (p_nonconforming_bucket_empty);
// ---------------------------------------------------------------------
// P12 -- The mean is reported in tenths. An integer mean cannot separate
// a transmitter that is exactly conforming from one fractionally under.
// ---------------------------------------------------------------------
property p_mean_has_resolution;
@(posedge clk) disable iff (!rst_n)
mean_valid |-> (mean_tenths != CNT_W'(0));
endproperty
a_mean_has_resolution: assert property (p_mean_has_resolution);
// ---------------------------------------------------------------------
// P13 -- First-cause context is captured once and survives a clear.
// ---------------------------------------------------------------------
property p_first_cause_stable;
@(posedge clk) disable iff (!rst_n)
undersized_seen |=> ($stable(first_gap) && $stable(first_deficit) &&
$stable(first_prev_frame));
endproperty
a_first_cause_stable: assert property (p_first_cause_stable);
// ---------------------------------------------------------------------
// P14 -- Idle symbols are only counted inside a gap. Counting them during
// a frame destroys the denominator the error rate depends on.
// ---------------------------------------------------------------------
property p_idle_counted_only_in_gap;
@(posedge clk) disable iff (!rst_n)
(!in_gap) |=> $stable(c_idle_symbols);
endproperty
a_idle_counted_only_in_gap: assert property (p_idle_counted_only_in_gap);
// ---------------------------------------------------------------------
// P15 -- The two idle failure classes are disjoint. A symbol is either
// not a control character, or a control character with a wrong value.
// ---------------------------------------------------------------------
property p_idle_failures_disjoint;
@(posedge clk) disable iff (!rst_n)
!(idle_wrong_val && idle_not_ctrl);
endproperty
a_idle_failures_disjoint: assert property (p_idle_failures_disjoint);
// ---------------------------------------------------------------------
// P16 -- COVERAGE. The deficit reaches its bound. A run that never got
// there never exercised the mechanism this chapter is about.
// ---------------------------------------------------------------------
c_deficit_at_bound: cover property (
@(posedge clk) disable iff (!rst_n) (deficit == DEF_W'(MAX_DEFICIT))
);
// ---------------------------------------------------------------------
// P17 -- COVERAGE. A gap at exactly the permitted floor, which is one
// octet from a violation and is legal.
// ---------------------------------------------------------------------
c_gap_at_floor: cover property (
@(posedge clk) disable iff (!rst_n)
(gap_valid && gap_octets == 8'(MIN_SINGLE_GAP))
);
// ---------------------------------------------------------------------
// P18 -- COVERAGE. A shortened gap immediately followed by a repayment,
// which is the mechanism completing a cycle rather than merely borrowing.
// ---------------------------------------------------------------------
c_borrow_then_repay: cover property (
@(posedge clk) disable iff (!rst_n)
(gap_valid && gap_octets < 8'(IFG_OCTETS))
##[1:$] (gap_valid && gap_octets > 8'(IFG_OCTETS))
);12. Verification — Twenty-Two Scenarios and a Deficit That Random Traffic Will Not Reach
| # | Scenario | Stimulus | What must be observed |
|---|---|---|---|
| 1 | Idle link | no frames queued | gaps grow without bound; deficit stays at zero |
| 2 | Aligned frame, nominal gap | frame length a multiple of LANES | gap exactly 12; deficit unchanged |
| 3 | Back-to-back aligned frames | continuous traffic, aligned lengths | every gap 12; bucket 3 only |
| 4 | Misaligned frame, round up | alignment forces 14 | gap 14; deficit repaid or stays zero |
| 5 | Misaligned frame, round down | alignment permits 10, deficit 0 | gap 10; deficit becomes 2 (P4) |
| 6 | Second shortening | another 10-octet gap, deficit 2 | refused — would exceed the bound (P5) |
| 7 | Repayment | a long gap after a deficit of 3 | deficit returns to 0 |
| 8 | Repayment saturates | a very long gap after deficit 1 | deficit reaches 0 and stops — no credit (P3) |
| 9 | Gap at exactly the floor | alignment and deficit permit 9 | permitted; deficit becomes 3 |
| 10 | One below the floor | force an 8-octet gap | refused — P1 is the only property that must never fail |
| 11 | Deficit at bound, misalignment persists | a run of misaligned frames | transmitter rounds up, gaps grow, deficit falls |
| 12 | Lane alignment | every frame start | start octet index is a multiple of LANES (P7) |
| 13 | Ready during hold | frame_ready asserted early in G_HOLD | no tx_start; the floor is unaffected (P9) |
| 14 | Mean over a window | 65 536 gaps with mixed alignment | mean_tenths at least 120 (P6) |
| 15 | Mean just under | every gap forced to 11 | mean_tenths = 110 — P6 fails, P1 passes |
| 16 | Histogram partition | any traffic | one bucket per gap; bucket 0 empty (P10, P11) |
| 17 | Undersized first cause | inject one 8-octet gap | context captured: gap, deficit, previous frame length, queued flag |
| 18 | First cause survives clear | assert clear after capture | count zeroes; captured context stable (P13) |
| 19 | Idle symbols counted | traffic with gaps | c_idle_symbols increments only inside gaps (P14) |
| 20 | Idle wrong value | corrupt one idle codeword | c_idle_wrong_value increments; c_idle_not_control does not (P15) |
| 21 | Idle not control | inject a data character in the gap | c_idle_not_control increments — the more serious class |
| 22 | Idle error rate | 1% of idle symbols corrupted | error_ppm near 10 000; worst_ppm survives clear |
13. Debugging — Reading Gaps Back to a Cause
Symptom — a peer reports interframe gap violations and this transmitter's own checker is clean.
Two candidates, and the histogram separates them. If bucket 0 is empty and the mean is at least 12, this transmitter is conforming and the peer's receiver tolerance is narrower than the standard requires — a receiver fault reported as a transmitter fault, which is Section 10's fourth table row. If the mean is below 12 while every individual gap is legal, the deficit is being borrowed and never repaid, and no per-gap check on either device will find it.
Symptom — throughput on a link is a few percent below the calculated maximum, with no errors.
Suspect the gap before suspecting anything else. A transmitter that rounds every gap up to the next lane boundary pays extra idle on every frame whose length is not a multiple of the interface width — which is most frames. Read the histogram: a distribution with nothing below 12 and a cluster at 13 to 15 is a design that never uses its deficit, and the deficit is exactly the mechanism that would recover the shortfall.
Symptom — gaps at the receiver are consistently smaller than the transmitter reports emitting.
Expected, and not a fault. Chapter 4.4's clock compensation deletes idle symbols in transit whenever a device must absorb a positive frequency difference, and every retiming element on the path may do it. The gap shrinks along a path by design. The finding is only real if the received gap falls below the receiver's tolerance, and even then the fault may be at any hop.
Symptom — occasional frame loss on a long-idle link that recovers under load.
Look at the idle monitor rather than at frame counters. c_idle_not_control climbing points at a decoder losing alignment during long idle periods — the receive path is drifting when there is no data to keep it honest, and the first frame after the drift is lost while re-acquisition happens. The frame counters cannot see this, because the damage occurred entirely inside a region they do not observe.
Symptom — error_ppm from the idle monitor is enormous and constant, on a link with no frame errors at all.
Almost certainly the monitor, not the channel. A line code that rotates between several idle codewords to manage running disparity will fail an equality test against a single expected value on most symbols. The tell is that the rate is stable and very large — a real channel problem produces a rate that varies with temperature, load and time, while a comparison error produces one that is essentially constant.
Symptom — the deficit sits at its bound permanently.
The traffic's frame lengths are systematically unfavourable for this interface width — a workload of fixed-size frames whose length has an awkward residue. Not a bug, and worth knowing, because it means the transmitter is rounding up on a large fraction of frames and the throughput cost is structural rather than incidental. It is one of the few findings in this chapter that is fixed above the MAC, by changing the frame sizes the workload generates.
14. Common Misconceptions
"The interframe gap is idle time."
The wrong model: nothing is transmitted; the wire is quiet.
What it costs: you cannot explain why a link needs a minimum gap at all, why Chapter 4.4's elastic buffer works, or why a receiver stays locked between frames. And you have no way to reason about the failures that occur there, because you believe there is nothing there to fail.
The corrected model: the gap carries idle symbols continuously, and they do three jobs at once — keeping clock recovery locked, supplying the deletable symbols rate compensation spends, and maintaining the line code's transition and balance invariants. A genuinely quiet wire would break all three.
"The minimum gap is 12 octets, so every gap is at least 12."
The wrong model: an instantaneous floor.
What it costs: Section 11's rejected property, and the bad fix that follows it — rounding every gap up to the nominal, which costs idle on every frame whose length is not a multiple of the interface width, permanently.
The corrected model: on a wide interface a frame must start on a lane boundary, so the gap is quantised. The standard's answer is a bounded deficit: individual gaps may fall to nine octets, the shortfall is recorded, and the bound guarantees the average holds over any interval.
"A short gap from a peer means the peer is broken."
The wrong model: the gap you measure is the gap that was sent.
What it costs: you report a conforming transmitter as faulty, and the actual fault — a receiver whose tolerance is narrower than required — goes uninvestigated, because all the evidence sits at the device that has the bug.
The corrected model: the gap shrinks along a path. Every device absorbing a positive frequency difference pays for it by deleting idle, so a receiver measuring ten octets cannot tell twelve-minus-two from ten. Receiver tolerance is a direct consequence of the compensation mechanism, not a concession to sloppy transmitters.
"Idle errors do not matter — the symbols are discarded anyway."
The wrong model: corruption in the gap has no consequence, so counting it is pointless.
What it costs: you throw away the earliest warning available. Idle occupies the channel most of the time on most links, so a rising bit error rate produces idle errors long before it produces its first frame error.
The corrected model: the idle error rate is a leading indicator of a degrading channel, measured in a region the check sequence explicitly does not cover and the datapath explicitly discards. It is the same argument Chapter 3.7 made for the pre-correction error rate — measure the quantity that degrades continuously, not the one that only moves after something is lost.
"A gap enforced by a checker is as good as one enforced by design."
The wrong model: correctness is correctness, however it is achieved.
What it costs: the failure mode is a gap that is occasionally one octet short — which produces no frame errors on a good channel, passes every functional test, and fails only against a peer whose receiver is less tolerant than the one you developed against.
The corrected model: a structural guarantee removes the failure from the space of things that can happen. Section 9's holding state has no early exit and does not read the ready signal, so no edit to the shortening policy can produce a gap below the floor. The policy can be got wrong; the floor cannot.
15. Interview Reasoning
"What is the interframe gap for?"
The weak answer is "so the receiver can recover". The answer that ends the topic gives all three jobs and notes that the first one largely expired: modern receivers pipeline, so frame n's check sequence is verified while frame n+1 is already arriving. What did not expire is rate compensation — the elastic buffer deletes and inserts idle, and the gap is the only place those symbols exist — and the line code's invariants, which are far more demanding at high rates than they were at 10 Mb/s. The constant survived by acquiring a second purpose as the first shrank.
"Is the minimum gap 96 bit times on every interface?"
The trap is to say yes. On a narrow interface, effectively. On a wide one a frame must start on a lane boundary, so the gap is quantised to the interface width and cannot always be the nominal value. The strong answer names the mechanism — a bounded deficit, individual gaps as short as nine octets, the shortfall recorded and repaid — and then the reason the bound matters: it is what turns "the average tends to twelve" into "the average over any interval is at least twelve".
"A peer says our gaps are too short. Where do you look?"
Not at the transmitter first. The strong answer asks two questions: is the mean over a window at least the nominal, and is any individual gap below the permitted floor — because they are different failures and only one is a transmitter fault. Then the third possibility, which is the one that distinguishes experience: the gap shrinks along a path as clock compensation deletes idle, so a receiver measuring a short gap may be looking at a conforming transmitter, a conforming path, and its own tolerance being narrower than required.
"Would you assert that every gap is at least twelve octets?"
Only on a narrow interface. On a wide one it forbids the deficit mechanism the standard defines, and it fires on a design that is behaving exactly as specified. The complete answer names the tell — the requirement mentions an average and the property has no window in it — and then the bad fix, which is what makes it worth catching: somebody satisfies it by rounding every gap up, which costs idle on most frames forever. The right shape is two properties: an instantaneous one at the permitted floor, and a windowed one on the mean.
16. Understanding Check
Idle symbols, continuously, doing three jobs at once.
Clock recovery stays locked because the symbols carry transitions. Without them the receiver's recovered clock drifts between frames and must re-acquire on every preamble, which the preamble is not long enough to guarantee at high rates.
Rate compensation spends them. Chapter 4.4 showed the two ends running from independent references, so an elastic buffer must delete a symbol when it fills and insert one when it empties — and the symbols it deletes have to be ones nobody needs. The gap is the only place they exist.
The line code's invariants hold across the gap as well as within frames. Chapter 3.5's transition density and DC balance requirements do not pause between frames, and an absence of signal satisfies none of them.
So the gap is not slack left for politeness — it is a budget the physical layer spends, and how much it spends depends on how far apart the two ends' frequencies are, which is a quantity neither end controls.
17. What's Next
The claim this chapter defended: idle is a signal with three jobs, and the minimum gap is an average held by a bounded deficit rather than a floor honoured every time.
Ninety-six bit times was a recovery interval for hardware that had to finish a frame before the next one began. Modern receivers pipeline and no longer need it for that — but the elastic buffer of Chapter 4.4 needs deletable symbols in every gap, and Chapter 3.5's coding invariants must hold between frames as well as within them. The constant outlived its first purpose by acquiring a second that grew as the first shrank.
And on a wide interface it cannot be a constant at all, because a frame starts on a lane boundary. So the standard constrains an aggregate: individual gaps as short as nine octets, a deficit bounded at three, repayment that saturates at zero so no quiet period can buy a later burst — and a property written without a window in it forbids the whole arrangement while looking exactly like the specification.
Module 5 ends here. Nine chapters walked the frame from the first bit of the preamble to the last octet of the gap, and the recurring finding was that almost none of its constants were chosen for the reasons they are now given: the floor is a propagation time, the ceiling is a 1980 buffer-cost compromise, the gap is a recovery interval that became a maintenance window, and the length/type field's disjoint ranges are an accident of two standards meeting.
Chapter 6.1 — What Error Detection Must Guarantee opens the next module by asking a question this one deferred four times. Chapter 5.8 established what the check sequence covers and where it is computed, and stopped short of what it guarantees. 6.1 supplies the missing half, and the answer is uncomfortable: "detects errors" is not a specification. A detector's real specification is a probability — the chance that a corrupted frame passes anyway — and that number depends on the error model, on the frame length, and on nothing anybody usually states. 6.1 builds the error model first, from the physics of Chapters 3.3, 3.6 and 3.7, and only then asks what CRC-32 must achieve.
The full path is on the Ethernet curriculum index.
Continue learning
Related tutorials
- Related topic
Elastic Buffering and Clock Compensation
Two independent oscillators differ by a bounded amount forever, and a bounded rate difference still accumulates without limit unless something discharges it. The interframe gap is that opportunity — which is why it is not negotiable and why the buffer is far smaller than intuition suggests.
- 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.
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.
