Skip to content
VLSI Mentor

Ethernet · Module 4

The Reconciliation Sublayer and the xMII Contract

The xMII generations are a record of what each had to give up — width, pins, timing margin, even parallelism — to keep carrying the same vocabulary as rates rose. That one vocabulary survived six unrelated physical forms is what media-independence actually means.

Chapter 4.1 established who owns what — a responsibility belongs to the side that can detect its own failure — and stated the assumption contract in both directions. Chapter 3.4 approached the same boundary from below and named the media-independent interface as the PHY's top edge.

Neither asked the question this chapter exists for.

A boundary and an interface are not the same thing. A boundary is an agreement about responsibility. An interface is a concrete set of wires, widths, clock rates and encodings — and there have been at least six of them for Ethernet, with completely different pin counts, completely different clocking, and in one case no parallel data path at all.

If the interface changed six times, in what sense is there one contract?

The usual answer is that the standard defines a service and the interfaces implement it. True, and it explains nothing — it just moves the question to what makes a service definition durable when every implementation of it differs.

What makes an interface media-independent, and what did each generation have to give up to stay that way?

1. Scope — What This Chapter Owns

This chapter owns: what the reconciliation sublayer maps between and why that mapping is thin; the vocabulary test for media-independence and how to apply it to a proposed signal; the xMII generations as a record of trades rather than a list of speeds; what a generation change actually costs in RTL; and the conformance checking that belongs to the interface as distinct from the contract.

This chapter does not own: the ownership rule or the assumption contract — Chapter 4.1 owns both and this chapter cites them. Nor the sublayer-split criterion, which is Chapter 3.4's. Chapter 4.3 owns the cycle-by-cycle sequence across the interface; this chapter gives the interface's shape and vocabulary, not its timing. Clock-domain conversion is Chapter 4.4 and Chapter 4.6.

The distinction from its two neighbours, stated once: Chapter 4.1 asked who is answerable. Chapter 3.4 asked where does the unit of information change. This chapter asks what makes a concrete interface durable across implementations that share nothing physically — and the answer is a property of its vocabulary, not of its wires.

2. What the Reconciliation Sublayer Reconciles

The name is unhelpful, and the block is small enough that its smallness is the point.

Above it, the MAC speaks in a service: a request to transmit a frame, an indication that one has arrived, an indication about whether the medium is available. Abstract, timeless, and expressed in terms of frames.

Below it, the interface speaks in signals: a data bus, an enable, an error, a valid, a clock. Concrete, cycle-by-cycle, and expressed in terms of nibbles or octets.

The reconciliation sublayer maps one onto the other, and does nothing else. It adds no function. It buffers nothing of consequence. It makes no decision the MAC did not already make.

A stack. The MAC client sits at the top, then the MAC which speaks in frames and an abstract service. Below it the reconciliation sublayer maps that service onto interface signals. Below that the media-independent interface carries data, control and status. Below that the PHY, and below that the medium.Where the mapping happensMAC clienthands over payload, expects delivery or a reporthands over payload, expects delivery or a reportMACspeaks in frames — an abstract, timeless servicespeaks in frames — an abstract, timeless serviceReconciliation sublayermaps that service onto signals, and adds nothingmaps that service onto signals, and adds nothingThe interfacedata, control, status — concrete, and cycle by cycledata, control, status — concrete, and cycle by cyclePHYeverything Module 3 describedeverything Module 3 describedThe mediumcopper, fibre, or something not yet inventedcopper, fibre, or something not yet invented
Figure 1 — a thin mapping, and everything below it is replaceable because of that thinness.

Why the thinness matters more than the function. A thick reconciliation sublayer — one that buffered, reordered, retimed, or made decisions — would put behaviour between the MAC and the interface. Behaviour has to be re-verified when either side changes, and the whole point of the boundary is that it does not.

So the reconciliation sublayer is where the interface generation is absorbed, and nowhere else. When MII becomes GMII, this block changes. The MAC above it does not. That is the mechanism by which the vocabulary outlives the wires, and it only works because the block is thin enough to rewrite.

3. The Vocabulary Test

Section 2's table is the evidence for the claim in the opening callout, and the claim is worth stating as a test you can apply.

A signal belongs on a media-independent interface only if it can be named without reference to a medium or a coding.

Apply it to the existing signals:

SignalNames whatPasses?
TXD / RXDdatayes — data is data on any medium
TX_EN"a frame is being transmitted"yes
TX_ER"this frame is bad"yes
RX_DV"a frame is arriving"yes
RX_ER"these octets are suspect"yes
CRS / COLmedium busy, collisionyes, but see below

Now apply it to signals a designer might propose adding:

ProposedNames whatPasses?Why
fec_corrected_countFEC correctionsnopresumes FEC exists — Chapter 3.7 showed some links have none
eye_marginvertical marginnopresumes an eye, so an analog electrical channel
lane_skewinter-lane delaynopresumes multiple lanes
block_lockPCS block alignmentnopresumes a block code — Chapter 3.5 showed which families have one
link_usablecan this carry framesyesa claim about capability, not mechanism
ratehow fastyesa number, not a mechanism

Every "no" is a medium or a coding leaking upward, and Chapter 4.1 §5 named the cost precisely: a MAC that reads FEC counts cannot be paired with a PHY that has no FEC.

The CRS/COL row deserves its caveat. They name a medium condition — busy, collided — which is arguably medium-specific in spirit even though the words are generic. That is why later generations dropped them: the conditions they name stopped existing. The test is about vocabulary, and vocabulary can pass the test and still become obsolete.

4. Six Generations, One Vocabulary

Now the evidence for the chapter's central claim. The physical form changed completely; the vocabulary barely moved.

GenerationData widthClockSignallingWhat it gave up
MII4 bits each way25 MHz for 100 Mb/sSDRnothing — the baseline
RMII2 bits each way50 MHz referenceSDRwidth, to halve the pins
GMII8 bits each way125 MHzSDRpin count, to reach gigabit at a tolerable clock
RGMII4 bits each way125 MHzDDRtiming margin — data on both clock edges
SGMII625 MHzDDRparallelism itself — serialised
XGMII32 bits each way156.25 MHzDDRpin count and timing margin together
A single MAC speaks one frame-oriented service. That service reaches three different reconciliation sublayers, one per interface generation, each driving a physically different interface: a four-bit parallel bus, a four-bit double-data-rate bus, and a serial stream. The MAC is unchanged in all three cases.One MACspeaks a frame service,unchangedRS for MIImaps to nibblesRS for RGMIImaps to nibbles, both edgesRS for SGMIImaps to a serial stream4 bits, 25 MHzsingle data rate4 bits, 125 MHzdouble data rateNo parallel busserialised entirelyAny PHYcopper, fibre, or somethingnew12
Figure 2 — one MAC, one vocabulary, and a different reconciliation sublayer per generation.

Read the last column downward. That is the chapter's thesis in one column.

Each generation faced the same problem — carry more bits per second — and each solved it by giving up something different:

  • RMII gave up width to save pins, and paid by doubling the clock.
  • GMII gave up pins to keep the clock manageable at gigabit.
  • RGMII gave up timing margin: DDR halves the pins again, and Chapter 3.3's two-axis margin argument applies directly — a DDR interface has half the setup-and-hold window of an SDR one at the same clock.
  • SGMII gave up the parallel bus entirely, which is the largest departure of all and the one that most tests the claim of a persistent vocabulary.
  • XGMII gave up both pins and margin, taking 32 bits and DDR.

Meanwhile the vocabulary did not change. Every one of these still carries data, an enable or its equivalent, an error indication, and a validity indication. A reconciliation sublayer rewritten for a new generation maps the same service onto different wires, and the MAC above it is untouched.

5. RTL 1 — The Reconciliation Sublayer, Both Directions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. The reconciliation sublayer, both directions.
//
// It adds NO function. It buffers nothing of consequence. It makes no
// decision the MAC did not already make. That is deliberate: behaviour
// placed here has to be re-verified whenever either side changes, and the
// entire value of the boundary is that it does not.
//
// This is the ONLY block that changes when the interface generation changes.
// Everything above it is untouched, which is the mechanism by which the
// vocabulary of Section 3 outlives the wires of Section 4.
package rs_pkg;
  // The MAC's service, as signals. Frame-oriented and generation-free.
  typedef enum logic [1:0] {
    SVC_IDLE     = 2'd0,
    SVC_TRANSMIT = 2'd1,   // a frame is in progress
    SVC_ABORT    = 2'd2    // abandon the frame in progress
  } tx_service_e;
 
  typedef enum logic [1:0] {
    IND_IDLE     = 2'd0,
    IND_RECEIVE  = 2'd1,   // a frame is arriving
    IND_SUSPECT  = 2'd2    // arriving, and the PHY does not trust it
  } rx_indication_e;
endpackage
 
module reconciliation_sublayer
  import rs_pkg::*;
#(
  // Interface data width. 4 for MII, 8 for GMII, 32 for XGMII. The
  // generation is a PARAMETER, which is the concrete form of Section 4's
  // claim -- one block, several forms, one vocabulary.
  parameter int unsigned IF_W = 8
) (
  input  logic clk,
  input  logic rst_n,
 
  // ── MAC service, transmit ───────────────────────────────────────────────
  input  tx_service_e      mac_tx_service,
  input  logic [IF_W-1:0]  mac_tx_data,
  output logic             mac_tx_accept,
 
  // ── Interface, transmit ─────────────────────────────────────────────────
  output logic [IF_W-1:0]  txd,
  output logic             tx_en,
  output logic             tx_er,
  input  logic             tx_accept,     // not present on every generation
 
  // ── Interface, receive ──────────────────────────────────────────────────
  input  logic [IF_W-1:0]  rxd,
  input  logic             rx_dv,
  input  logic             rx_er,
 
  // ── MAC indication, receive ─────────────────────────────────────────────
  output rx_indication_e   mac_rx_indication,
  output logic [IF_W-1:0]  mac_rx_data,
 
  // ── Medium conditions, passed through unchanged ─────────────────────────
  // These are MII-era vocabulary (Chapter 1.2). On a full-duplex link they
  // are unreachable, exactly as Chapter 1.5 showed for the access logic.
  // They are passed rather than interpreted -- interpreting them here would
  // be behaviour, and behaviour is what this block must not contain.
  input  logic             crs,
  input  logic             col,
  output logic             mac_medium_busy,
  output logic             mac_collision
);
 
  // ── Transmit mapping ────────────────────────────────────────────────────
  // A frame boundary becomes an enable's rising and falling edge. An abort
  // becomes a marked cycle. That is the entire transmit direction.
  always_comb begin
    txd    = mac_tx_data;
    tx_en  = (mac_tx_service != SVC_IDLE);
    // TX_ER asserted WITH TX_EN means "this frame is bad". Asserted without
    // it means something else entirely on some generations, which is why
    // the two are driven together and never independently.
    tx_er  = (mac_tx_service == SVC_ABORT);
    mac_tx_accept = tx_accept;
  end
 
  // ── Receive mapping ─────────────────────────────────────────────────────
  always_comb begin
    mac_rx_data = rxd;
    if      (!rx_dv) mac_rx_indication = IND_IDLE;
    else if (rx_er)  mac_rx_indication = IND_SUSPECT;
    else             mac_rx_indication = IND_RECEIVE;
  end
 
  // ── Medium conditions ───────────────────────────────────────────────────
  assign mac_medium_busy = crs;
  assign mac_collision   = col;
 
endmodule

Classification: synthesizable.

What it teaches: that the mapping is combinational and stateless, and that this is a deliberate architectural property rather than a simplification. The moment this block holds state, it holds behaviour, and behaviour must be re-verified whenever either neighbour changes — which defeats the boundary.

Deliberately simplified: no generation-specific timing. Real reconciliation sublayers for DDR generations must handle the two half-cycles, and tx_accept does not exist on every generation — MII has no flow control at this level at all, which Section 9 develops.

Production implication: tx_en and tx_er are driven together, from one service enum, never independently. On several generations TX_ER asserted without TX_EN means something entirely different from an in-frame error — it is a separate signalling mechanism. A design that drives them from two unrelated sources will eventually assert that combination by accident and transmit a signal it did not intend.

Later ownership: the cycle-by-cycle timing of these signals is Chapter 4.3; clock-domain handling is Chapter 4.4 and Chapter 4.6.

6. RTL 2 — A Generation Adapter

Section 4 claimed one vocabulary survives across generations. This is that claim made executable: a block that speaks GMII on one side and MII on the other, with the MAC above it unaware.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. An 8-bit to 4-bit interface adapter.
//
// Section 4's claim, executable: the same vocabulary at two different
// widths. The MAC above sees octets; the interface below sees nibbles; and
// the words data, enable, error and valid mean the same on both sides.
//
// THE ADAPTER HAS STATE AND THE RECONCILIATION SUBLAYER DOES NOT, and the
// reason is worth being precise about. The reconciliation sublayer maps a
// vocabulary; this changes a RATE. One MAC octet becomes two interface
// nibbles, so something must remember which half is being sent -- and that
// is exactly the fact Section 13's rejected property assumes away.
module width_adapter_8_to_4 (
  input  logic clk,           // the NIBBLE clock -- twice the octet rate
  input  logic rst_n,
 
  // Wide side: one octet per octet-clock, offered continuously.
  input  logic       wide_valid,
  input  logic [7:0] wide_data,
  input  logic       wide_en,
  input  logic       wide_er,
  output logic       wide_accept,
 
  // Narrow side: two nibbles per octet.
  output logic       narrow_valid,
  output logic [3:0] narrow_data,
  output logic       narrow_en,
  output logic       narrow_er,
 
  // Which half is on the wire. Exposed because a receiver that guesses this
  // wrong reassembles every octet with its halves swapped -- and that
  // failure looks like data corruption on a link reporting perfect health,
  // which is the same class of contract fault Chapter 3.4 §10 named for
  // lane ordering.
  output logic       second_half
);
 
  logic       phase_q;      // 0 = first nibble, 1 = second
  logic [7:0] hold_q;
  logic       en_q, er_q, valid_q;
 
  // NIBBLE ORDER IS A CONTRACT. Least-significant nibble first is the
  // convention here and it must match the far end. Nothing detects a
  // mismatch; every bit arrives and every octet is wrong.
  always_comb begin
    narrow_data  = phase_q ? hold_q[7:4] : hold_q[3:0];
    narrow_valid = valid_q;
    narrow_en    = en_q;
    narrow_er    = er_q;
    second_half  = phase_q;
  end
 
  // Accept a new octet only when the second nibble is going out, so the
  // wide side runs at exactly half the nibble rate.
  assign wide_accept = phase_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      phase_q <= 1'b0;
      hold_q  <= '0;
      en_q    <= 1'b0;
      er_q    <= 1'b0;
      valid_q <= 1'b0;
    end else begin
      if (!phase_q) begin
        // First nibble going out; the octet was captured last cycle.
        phase_q <= 1'b1;
      end else begin
        // Second nibble going out; take the next octet now.
        phase_q <= 1'b0;
        hold_q  <= wide_data;
        en_q    <= wide_en;
        er_q    <= wide_er;
        valid_q <= wide_valid;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that a generation change is a rate change, not a rewiring. One MAC octet becomes two interface cycles, so the adapter holds state and the two sides run at different rates. Any reasoning that assumes a one-to-one correspondence between MAC cycles and interface cycles is generation-specific — and Section 13 shows the assertion that encodes that assumption.

Deliberately simplified: no clock-domain crossing. Both sides here run from the nibble clock with the wide side accepting every other cycle. A real adapter between genuinely asynchronous domains needs the structures of Chapter 4.4 and Chapter 4.6.

Production implication: nibble order is a contract with no self-check. Least-significant-first is the convention used here, and if the far end reassembles most-significant-first, every bit arrives and every octet is wrong. The symptom is data corruption on a link whose every status indicator is healthy — structurally identical to Chapter 3.4 §10's lane-ordering fault and Chapter 3.1 §6's pair-striping fault. Exposing second_half costs one flip-flop of visibility and makes the contract observable.

Later ownership: asynchronous width conversion belongs to Chapter 4.6.

7. RTL 3 — The Reassembler, Where the Contract Bites

Section 6 split octets into nibbles. This is the other direction, and it is where the nibble-order contract actually produces a failure — because splitting cannot be wrong on its own, only disagreeing about the convention can.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Narrow to wide, the receive direction.
//
// SPLITTING CANNOT BE WRONG BY ITSELF. Any consistent convention produces a
// stream some receiver can reassemble. The fault is always a DISAGREEMENT,
// and disagreements only become visible here -- which is why this module,
// not Section 6's, carries the alignment recovery.
//
// The subtlety: a receiver joining mid-stream does not know whether the
// first nibble it sees is a low half or a high half. It must be TOLD, by
// the frame boundary, and a receiver that guesses produces octets that are
// each built from halves of two adjacent octets -- every bit present, every
// octet wrong.
module width_reassembler_4_to_8 (
  input  logic       clk,
  input  logic       rst_n,
 
  input  logic       narrow_valid,
  input  logic [3:0] narrow_data,
  input  logic       narrow_en,       // frame in progress
  input  logic       narrow_er,
 
  output logic       wide_valid,
  output logic [7:0] wide_data,
  output logic       wide_en,
  output logic       wide_er,
 
  // Phase is derived from the FRAME BOUNDARY, never guessed. The rising
  // edge of narrow_en defines the first nibble of the first octet, and
  // everything after it follows by counting.
  output logic       phase,
 
  // A frame whose nibble count was ODD. The frame carried half an octet,
  // which means either the transmitter truncated mid-octet or a nibble was
  // lost. Either way the frame is unusable and must be marked -- silently
  // dropping the stray nibble produces a frame one octet short whose check
  // value then fails, and the loss is attributed to the link.
  output logic        odd_nibble_count,
  output logic [19:0] c_odd_nibble_frames
);
 
  logic       phase_q;
  logic [3:0] low_q;
  logic       er_q;
  logic       in_frame_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      phase_q             <= 1'b0;
      low_q               <= '0;
      er_q                <= 1'b0;
      in_frame_q          <= 1'b0;
      wide_valid          <= 1'b0;
      wide_data           <= '0;
      wide_en             <= 1'b0;
      wide_er             <= 1'b0;
      odd_nibble_count    <= 1'b0;
      c_odd_nibble_frames <= '0;
    end else begin
      wide_valid       <= 1'b0;
      odd_nibble_count <= 1'b0;
 
      if (narrow_valid && narrow_en) begin
        if (!in_frame_q) begin
          // Frame start: this nibble is a LOW half, by definition. The
          // boundary supplies the phase so it never has to be guessed.
          in_frame_q <= 1'b1;
          phase_q    <= 1'b1;
          low_q      <= narrow_data;
          er_q       <= narrow_er;
        end else if (!phase_q) begin
          phase_q <= 1'b1;
          low_q   <= narrow_data;
          er_q    <= narrow_er;
        end else begin
          // High half arrived: emit the octet.
          phase_q    <= 1'b0;
          wide_valid <= 1'b1;
          wide_data  <= {narrow_data, low_q};   // low-first convention
          wide_en    <= 1'b1;
          wide_er    <= er_q || narrow_er;
        end
      end else if (in_frame_q) begin
        // Frame ended. If a low half is held, the frame had an odd number
        // of nibbles and is unusable.
        in_frame_q <= 1'b0;
        phase_q    <= 1'b0;
        wide_en    <= 1'b0;
        if (phase_q) begin
          odd_nibble_count <= 1'b1;
          if (!(&c_odd_nibble_frames)) c_odd_nibble_frames <= c_odd_nibble_frames + 1'b1;
        end
      end
    end
  end
 
  assign phase = phase_q;
 
endmodule

Classification: synthesizable.

What it teaches: that phase must be derived from the frame boundary, never guessed. A receiver joining a stream mid-frame cannot know whether the nibble it sees is a low half or a high half — and if it assumes, every octet it builds is assembled from halves of two adjacent octets. Every bit is present, every octet is wrong, and the link reports perfect health. The rising edge of the enable is what supplies the answer.

Deliberately simplified: no clock-domain crossing, and no recovery from a lost nibble mid-frame beyond marking the frame. A production design may resynchronise at the next frame boundary, which this does implicitly by re-deriving phase.

Production implication: odd_nibble_count must be its own indication. A frame with an odd nibble count carried half an octet, and silently dropping the stray nibble produces a frame one octet short — whose check value then fails, and whose loss is attributed to the link. That misattribution is exactly the class Chapter 4.1 §14 described: the reporting side is not the causing side.

And keeping this separate from Section 6's splitter is deliberate. A single module holding both directions invites someone to share the nibble-order constant, after which a convention change applies to one direction only — producing a link that transmits correctly and receives garbage, with both halves individually reviewable and correct.

8. RTL 4 — The Control-Encoding Translator

Width is the easy half of a generation change. Control encoding is the hard half, because generations disagree about how "this is not data" is expressed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Control-encoding translation between two conventions.
//
// Generations disagree about HOW to say "this is not data":
//
//   SIDEBAND : separate wires. TX_EN says a frame is in progress; TX_ER
//              says it is bad. Costs pins. Data code space is untouched.
//
//   IN-BAND  : reserved values inside the data path itself. Costs code
//              space -- some data values become unusable -- and pins are
//              saved.
//
// Translating is not relabelling. Going sideband-to-in-band can FAIL: if
// the payload contains the reserved value, it must be escaped or the
// receiver will read data as control. That failure has no counterpart in
// the other direction, and it is the asymmetry this module exists to show.
package ctrl_enc_pkg;
  typedef enum logic [1:0] {
    ENC_SIDEBAND = 2'd0,
    ENC_INBAND   = 2'd1
  } encoding_e;
endpackage
 
module control_encoding_translator
  import ctrl_enc_pkg::*;
#(
  parameter int unsigned W = 8,
  // The in-band reserved values. Any data octet equal to one of these must
  // be escaped, because a receiver cannot distinguish them otherwise.
  parameter logic [7:0] IB_START  = 8'hFB,
  parameter logic [7:0] IB_END    = 8'hFD,
  parameter logic [7:0] IB_ERROR  = 8'hFE,
  parameter logic [7:0] IB_ESCAPE = 8'hFC
) (
  input  logic clk,
  input  logic rst_n,
 
  // ── Sideband side ───────────────────────────────────────────────────────
  input  logic         sb_valid,
  input  logic [W-1:0] sb_data,
  input  logic         sb_en,
  input  logic         sb_er,
  output logic         sb_accept,
 
  // ── In-band side ────────────────────────────────────────────────────────
  output logic         ib_valid,
  output logic [W-1:0] ib_data,
  input  logic         ib_accept,
 
  // A data octet collided with a reserved value and had to be escaped,
  // costing an extra cycle. Counted because a payload that happens to be
  // rich in reserved values silently costs THROUGHPUT -- a real effect that
  // depends on the data and is invisible without this counter.
  output logic [23:0]  c_escapes_inserted,
  // The worst run of consecutive escapes seen. Sizing upstream buffering
  // from the average rather than this is how a design overflows under a
  // pathological payload.
  output logic [15:0]  longest_escape_run
);
 
  logic         escaping_q;   // an escape was emitted; the octet follows
  logic [W-1:0] held_q;
  logic [15:0]  run_q;
 
  logic needs_escape_c;
  assign needs_escape_c = sb_valid && sb_en && !sb_er
                       && ((sb_data == IB_START)  || (sb_data == IB_END)
                        || (sb_data == IB_ERROR)  || (sb_data == IB_ESCAPE));
 
  always_comb begin
    ib_valid = sb_valid || escaping_q;
 
    if (escaping_q)         ib_data = held_q;          // the escaped octet
    else if (sb_er)         ib_data = IB_ERROR;
    else if (!sb_en)        ib_data = IB_END;
    else if (needs_escape_c) ib_data = IB_ESCAPE;
    else                    ib_data = sb_data;
 
    // The upstream is held for one cycle while an escape goes out. This is
    // the throughput cost, and it is data-dependent.
    sb_accept = ib_accept && !needs_escape_c;
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      escaping_q         <= 1'b0;
      held_q             <= '0;
      c_escapes_inserted <= '0;
      run_q              <= '0;
      longest_escape_run <= '0;
    end else if (ib_accept) begin
      if (needs_escape_c && !escaping_q) begin
        escaping_q <= 1'b1;
        held_q     <= sb_data;
        if (!(&c_escapes_inserted)) c_escapes_inserted <= c_escapes_inserted + 1'b1;
        run_q <= run_q + 1'b1;
        if (run_q + 1'b1 > longest_escape_run) longest_escape_run <= run_q + 1'b1;
      end else begin
        escaping_q <= 1'b0;
        if (!needs_escape_c) run_q <= '0;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that in-band and sideband control are not equivalent encodings of the same thing. Sideband costs pins and leaves the data space intact. In-band costs code space, and the moment payload can collide with a reserved value, escaping becomes necessary — which makes throughput data-dependent. Sideband has no equivalent failure.

Deliberately simplified: the reserved values are parameters chosen for the model, not any generation's actual encoding. Real in-band control encodings are defined per generation and interact with the block coding of Chapter 3.5 — which is where the real solution lives, because a block code's invalid-pattern space provides control symbols that cannot collide with data at all.

Production implication: longest_escape_run is the number that matters for sizing, not c_escapes_inserted. A payload rich in reserved values — a test pattern, a compressed stream, a memory dump — produces a burst of escapes, and upstream buffering sized from the average escape rate overflows on the burst. This is the same average-versus-worst-case trap Chapter 3.4 §12 named for stall depth.

And it explains a real design decision. Chapter 3.5 §2 listed a control-symbol space as one of the four things a line code buys. This module is what you have to build when you do not have one — and comparing them is the clearest argument for why a proper code's invalid-pattern space is worth its overhead.

9. RTL 5 — What a Generation Change Actually Costs

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE INSTRUMENTATION.
//
// Section 4 said each generation gave up something. This measures what,
// at runtime, so the trade is a number rather than an adjective.
//
// Three costs, and they are not interchangeable:
//   PINS    -- fixed at design time, but worth computing for comparison
//   TIMING  -- the setup-and-hold window, which DDR halves
//   THROUGHPUT -- cycles spent on control and escapes rather than data
module generation_cost_monitor #(
  parameter int unsigned IF_W        = 8,
  parameter bit          IS_DDR      = 1'b0,
  parameter int unsigned CLK_MHZ     = 125,
  parameter int unsigned CNT_W       = 32
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic if_valid,
  input  logic if_is_data,      // this cycle carried payload
  input  logic if_is_control,   // this cycle carried control or an escape
 
  // Pin count for this configuration. Data lines both ways, plus control
  // and clock. Computed rather than documented so a parameter change
  // updates it.
  output logic [7:0]  pin_estimate,
 
  // The available setup-and-hold window in picoseconds. DDR halves it,
  // which is Section 4's "gave up timing margin" as an actual quantity.
  output logic [15:0] window_ps,
 
  // Throughput efficiency: data cycles as a fraction of valid cycles,
  // scaled by 256. Below 256 means cycles are being spent on control.
  output logic [15:0] efficiency_x256,
 
  output logic [CNT_W-1:0] c_data_cycles,
  output logic [CNT_W-1:0] c_control_cycles
);
 
  // Data both directions, plus enable, error, valid and clock each way.
  localparam int unsigned CTRL_PINS = 6;
  assign pin_estimate = 8'((IF_W * 2) + CTRL_PINS + (IS_DDR ? 2 : 2));
 
  // One full clock period in ps, halved for DDR. This is the entire
  // arithmetic behind "DDR gave up timing margin" -- half the window at the
  // same clock, which is why RGMII needs controlled trace delay where GMII
  // does not.
  localparam int unsigned PERIOD_PS = 1_000_000 / CLK_MHZ;
  assign window_ps = 16'(IS_DDR ? (PERIOD_PS / 2) : PERIOD_PS);
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || clear) begin
      c_data_cycles    <= '0;
      c_control_cycles <= '0;
    end else if (if_valid) begin
      if (if_is_data    && !(&c_data_cycles))    c_data_cycles    <= c_data_cycles + 1'b1;
      if (if_is_control && !(&c_control_cycles)) c_control_cycles <= c_control_cycles + 1'b1;
    end
  end
 
  always_comb begin
    automatic logic [CNT_W:0] total = c_data_cycles + c_control_cycles;
    efficiency_x256 = (total == 0) ? 16'd0
                                   : 16'((c_data_cycles * 256) / total);
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that Section 4's trades are quantities, and the timing one especially. window_ps is the whole argument for why DDR is a real cost: at 125 MHz, an SDR interface has an 8 ns window and a DDR one has 4 ns for the same clock. That is why RGMII requires controlled trace delay — often a deliberate delay on the clock — where GMII does not, and it is a board-design consequence of an interface choice.

Deliberately simplified: pin_estimate counts signal pins only and ignores power, ground and the return-path pins that a real count includes.

Production implication: efficiency_x256 is the one that surprises people. An interface whose control encoding is in-band spends cycles on control and escapes, so its effective throughput is below its nominal rate — and Section 8 showed that shortfall is data-dependent. A design sized from the nominal figure will be short by the escape rate under exactly the payloads that produce the most escapes.

Later ownership: the board-level timing consequences of DDR belong to a physical-design discipline rather than to this track.

10. RTL 6 — Interface Conformance, Distinct From Contract Conformance

Chapter 4.1 built two conformance checkers for the contract — the assumptions each side makes about the other. This checks something different and both are needed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE INSTRUMENTATION. Interface-level, not contract-level.
//
// Chapter 4.1's checkers ask "is each side honouring what the other
// assumed?" -- a semantic question about the contract.
//
// This asks "are these signals in a legal combination for THIS interface
// generation?" -- a syntactic question about the wires. The two are
// independent: a design can honour every assumption and still drive an
// illegal signal combination, and the far end's behaviour is then
// undefined rather than wrong.
//
// Generation-specific rules are PARAMETERS, because the same wires mean
// different things on different generations -- which is Section 4's claim
// seen from the checking side.
module interface_conformance #(
  parameter int unsigned IF_W = 8,
  // Does this generation define TX_ER asserted WITHOUT TX_EN as meaningful?
  // On some it signals something specific; on others it is illegal. The
  // checker cannot know without being told.
  parameter bit ER_WITHOUT_EN_LEGAL = 1'b0,
  // Minimum idle cycles this generation requires between frames.
  parameter int unsigned MIN_IDLE = 12,
  parameter int unsigned CNT_W = 20
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic [IF_W-1:0] txd,
  input  logic            tx_en,
  input  logic            tx_er,
  input  logic [IF_W-1:0] rxd,
  input  logic            rx_dv,
  input  logic            rx_er,
 
  // ── Illegal combinations ────────────────────────────────────────────────
  // TX_ER without TX_EN. Legal on some generations, illegal on others, and
  // a design ported between them without checking will drive it.
  output logic v_er_without_en,
  output logic [CNT_W-1:0] c_er_without_en,
 
  // Data driven while the enable is low. Not an error on every generation,
  // but it wastes power and on some it is genuinely illegal.
  output logic v_data_while_disabled,
  output logic [CNT_W-1:0] c_data_while_disabled,
 
  // ── Timing conformance ──────────────────────────────────────────────────
  output logic v_idle_too_short,
  output logic [CNT_W-1:0] c_idle_too_short,
 
  // ── Receive-side conformance ────────────────────────────────────────────
  // RX_ER asserted outside a frame. On generations where this is defined it
  // carries a specific meaning; where it is not, it is noise the MAC may
  // act on.
  output logic v_rx_er_outside_frame,
  output logic [CNT_W-1:0] c_rx_er_outside_frame,
 
  // Sticky: this design drove a combination that is legal on SOME
  // generation but not the one configured. The most useful single bit here,
  // because it is the signature of a design ported without re-checking.
  output logic generation_mismatch_suspected
);
 
  logic [$clog2(MIN_IDLE+2)-1:0] idle_q;
  logic                          in_rx_frame_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_comb begin
    v_er_without_en       = tx_er && !tx_en && !ER_WITHOUT_EN_LEGAL;
    v_data_while_disabled = !tx_en && (txd != '0);
    v_idle_too_short      = tx_en && (idle_q < ($clog2(MIN_IDLE+2))'(MIN_IDLE));
    v_rx_er_outside_frame = rx_er && !rx_dv;
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      idle_q                        <= '1;
      in_rx_frame_q                 <= 1'b0;
      c_er_without_en               <= '0;
      c_data_while_disabled         <= '0;
      c_idle_too_short              <= '0;
      c_rx_er_outside_frame         <= '0;
      generation_mismatch_suspected <= 1'b0;
    end else begin
      if (clear) begin
        c_er_without_en       <= '0;
        c_data_while_disabled <= '0;
        c_idle_too_short      <= '0;
        c_rx_er_outside_frame <= '0;
        // The sticky bit deliberately survives a clear -- it is a property
        // of the design's configuration, not of a measurement window.
      end else begin
        c_er_without_en       <= bump(c_er_without_en,       v_er_without_en);
        c_data_while_disabled <= bump(c_data_while_disabled, v_data_while_disabled);
        c_idle_too_short      <= bump(c_idle_too_short,      v_idle_too_short);
        c_rx_er_outside_frame <= bump(c_rx_er_outside_frame, v_rx_er_outside_frame);
      end
 
      // Idle tracking on the transmit side.
      if (tx_en)              idle_q <= '0;
      else if (!(&idle_q))    idle_q <= idle_q + 1'b1;
 
      if (rx_dv) in_rx_frame_q <= 1'b1;
      else       in_rx_frame_q <= 1'b0;
 
      // A combination that is legal on another generation but not this one.
      if (v_er_without_en || v_rx_er_outside_frame)
        generation_mismatch_suspected <= 1'b1;
    end
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that syntactic and semantic conformance are independent, and that checking one does not check the other. A MAC can honour every one of Chapter 4.1 §4's assumptions and still drive a signal combination that this generation does not define — and the far end's response is then undefined, which is worse than wrong because it is not reproducible.

Deliberately simplified: the generation-specific rules are parameters rather than a table of every generation's legality matrix. A production checker for a design supporting several generations carries the matrix.

Production implication: generation_mismatch_suspected is the highest-value bit in the module and it survives clear deliberately. It is the signature of a design ported between generations without re-checking its signal usage — a very common integration fault, because the ported design works for the overwhelming majority of traffic and drives the illegal combination only in a corner case. By the time anyone investigates, the corner case has passed; the sticky bit is what remains.

Later ownership: the full per-generation legality matrix belongs in a design's own interface specification.

11. Assertions

Every property below is a property of these teaching models and of the interface conventions they parameterise. IEEE 802.3 specifies each interface generation's signals and timing per clause; which combinations a particular design may drive is set by the generation it targets, and the checker of Section 10 takes that as a parameter for exactly that reason.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─── Causation: the reconciliation mapping is stateless ────────────────────
// The architectural property of Section 5. Catches state creeping into the
// mapping, which puts behaviour between the MAC and the interface -- after
// which both neighbours must be re-verified whenever either changes.
property p_rs_mapping_is_combinational;
  @(posedge clk) disable iff (!rst_n)
  (tx_en == (mac_tx_service != SVC_IDLE))
    && (tx_er == (mac_tx_service == SVC_ABORT));
endproperty
 
// ─── Safety: TX_EN and TX_ER are driven together ───────────────────────────
// Catches two independent drivers, which will eventually produce TX_ER
// without TX_EN by accident -- a combination that means something entirely
// different on several generations.
property p_er_implies_en;
  @(posedge clk) disable iff (!rst_n)
  tx_er |-> tx_en;
endproperty
 
// ─── Conservation: the receive indication is exhaustive ────────────────────
// Catches a fourth state appearing in the mapping, which would leave a
// received cycle unclassified.
property p_rx_indication_exhaustive;
  @(posedge clk) disable iff (!rst_n)
  (mac_rx_indication == IND_IDLE)
    || (mac_rx_indication == IND_RECEIVE)
    || (mac_rx_indication == IND_SUSPECT);
endproperty
 
// ─── Ordering: the adapter alternates halves ───────────────────────────────
// Catches a phase counter that can stall or skip, after which every
// subsequent octet is assembled from halves of two different octets.
property p_adapter_alternates;
  @(posedge clk) disable iff (!rst_n)
  1'b1 |=> (second_half != $past(second_half));
endproperty
 
// ─── Conservation: the wide side runs at half the narrow rate ──────────────
// Catches an accept that asserts every cycle, which drops every other
// octet -- and drops them silently, because nothing downstream can tell an
// octet was never offered.
property p_wide_accepts_every_other;
  @(posedge clk) disable iff (!rst_n)
  wide_accept |=> !wide_accept;
endproperty
 
// ─── Safety: the nibble contract holds ─────────────────────────────────────
// Least-significant nibble first. Catches a reordering that delivers every
// bit into the wrong half, which presents as data corruption on a link
// reporting perfect health.
property p_nibble_order;
  @(posedge clk) disable iff (!rst_n)
  (narrow_valid && !second_half) |-> (narrow_data == hold_q[3:0]);
endproperty
 
// ─── Safety: reserved values are always escaped ────────────────────────────
// Catches a payload octet equal to a reserved value reaching the in-band
// side unescaped, after which the receiver reads data as control and the
// frame structure collapses from that point on.
property p_reserved_always_escaped;
  @(posedge clk) disable iff (!rst_n)
  (ib_valid && !escaping_q && (ib_data == IB_START)) |-> !$past(sb_en);
endproperty
 
// ─── Conservation: an escape is followed by its octet ──────────────────────
// Catches an escape emitted without the octet it introduces, which shifts
// every subsequent byte by one.
property p_escape_followed_by_data;
  @(posedge clk) disable iff (!rst_n)
  (ib_valid && ib_accept && (ib_data == IB_ESCAPE)) |=> escaping_q;
endproperty
 
// ─── Bounded response: escaping terminates ─────────────────────────────────
// Catches an escape state that can persist, stalling the interface
// indefinitely on one octet.
property p_escape_is_one_cycle;
  @(posedge clk) disable iff (!rst_n)
  (escaping_q && ib_accept) |=> !escaping_q;
endproperty
 
// ─── Safety: the timing window follows the signalling mode ─────────────────
// Section 9's arithmetic, asserted. Catches a DDR configuration reporting an
// SDR window, which makes every downstream timing budget optimistic by a
// factor of two.
property p_window_halves_for_ddr;
  @(posedge clk) disable iff (!rst_n)
  IS_DDR |-> (window_ps == (1_000_000 / CLK_MHZ) / 2);
endproperty
 
// ─── Conservation: efficiency accounting closes ────────────────────────────
// Catches a cycle counted as neither data nor control, which biases the
// efficiency figure in a way no downstream analysis can detect.
property p_cycles_classified;
  @(posedge clk) disable iff (!rst_n)
  if_valid |-> (if_is_data ^ if_is_control);
endproperty
 
// ─── Stability: the generation-mismatch flag never clears ──────────────────
// Catches the sticky bit folded into the clear branch, destroying the
// signature of a design ported without re-checking its signal usage.
property p_mismatch_flag_sticky;
  @(posedge clk) disable iff (!rst_n)
  generation_mismatch_suspected |=> generation_mismatch_suspected;
endproperty
 
// ─── Causation: idle enforcement uses the configured minimum ───────────────
// Catches a checker hard-coded to one generation's gap, which then passes
// a design wired to a generation with a different requirement.
property p_idle_check_uses_parameter;
  @(posedge clk) disable iff (!rst_n)
  v_idle_too_short |-> (idle_q < MIN_IDLE);
endproperty
 
// ─── Causation: reassembly phase comes from the frame boundary ─────────────
// Catches a receiver that guesses phase, which builds every octet from
// halves of two adjacent octets -- every bit present, every octet wrong,
// and the link reporting perfect health.
property p_phase_from_frame_start;
  @(posedge clk) disable iff (!rst_n)
  ($rose(narrow_en) && narrow_valid) |=> phase;
endproperty
 
// ─── Conservation: two nibbles make exactly one octet ──────────────────────
// Catches an emit on the wrong phase, which halves or doubles the octet
// count without any other symptom.
property p_octet_per_two_nibbles;
  @(posedge clk) disable iff (!rst_n)
  wide_valid |-> $past(phase);
endproperty
 
// ─── Safety: an odd nibble count is reported, never dropped ────────────────
// Catches silent truncation of a stray half-octet, which produces a frame
// one octet short whose check value then fails -- and the loss is
// attributed to the link rather than to the reassembler.
property p_odd_nibble_reported;
  @(posedge clk) disable iff (!rst_n)
  ($fell(narrow_en) && $past(phase)) |=> odd_nibble_count;
endproperty

12. Verification

Conceptual — an octet becoming two nibbles

8 cycles
A conceptual timing diagram over eight interface cycles. The interface clock runs throughout. The MAC accept signal asserts every other cycle on the narrow interface. The narrow data lane carries the low nibble then the high nibble of each octet, indicated by the second-half signal alternating. The transmit enable stays asserted across the whole frame.low nibble of octet onelow nibble of octet onehigh nibble, and accept the next octethigh nibble, and accept thenext octetthe ratio is two, not onethe ratio is two, not onetx_en spans the whole frametx_en spans the whole frameif_clkwide_accept01010101second_halftx_en11111111narrowt0t1t2t3t4t5t6t7
Figure 3 — conceptual: one octet, one interface cycle at eight bits and two at four.

This figure is conceptual and labelled so. It shows the ratio and the ordering correctly — which is what Section 11's rejected property gets wrong — and does not attempt to show DDR half-cycles, which no digital waveform can represent.

Scenarios

  1. A frame through the reconciliation sublayer, transmit. Verify tx_en rises with the first octet and falls after the last, and that txd tracks mac_tx_data combinationally with no cycle of delay.
  2. A frame through the reconciliation sublayer, receive. Verify mac_rx_indication is IND_RECEIVE while rx_dv is asserted and IND_IDLE outside it.
  3. A received frame the PHY does not trust. Assert rx_er mid-frame and verify the indication becomes IND_SUSPECT — not IND_IDLE, which would drop the frame silently, and not IND_RECEIVE, which would deliver it as good.
  4. An aborted transmit. Drive SVC_ABORT and verify tx_er asserts with tx_en, never without it.
  5. Medium conditions. Toggle crs and col and verify they pass through unchanged and are not interpreted. Interpretation here would be behaviour, which Section 5 argued must not live in this block.
  6. The mapping is stateless. Hold the inputs constant across a reset and verify the outputs are unchanged by the reset — a combinational mapping has nothing to reset.
  7. Adapter, a single octet. Verify exactly two narrow cycles, low nibble first, and wide_accept asserted on exactly one of them.
  8. Adapter, a long frame. Verify the phase alternates for the whole frame with no slip, and that the octet count out equals the octet count in.
  9. Adapter, reset mid-octet. Verify no half-octet escapes and the phase returns to its reset value.
  10. Adapter, nibble order. Drive a distinct octet and verify by hand which half appears first. Then reverse the convention in the model and verify the check catches it — a scoreboard sharing the design's ordering verifies nothing.
  11. Translator, a payload with no reserved values. Verify no escapes and full throughput.
  12. Translator, a payload octet equal to each reserved value. Four runs, one per value. Verify an escape precedes each and the octet arrives intact.
  13. Translator, consecutive reserved values. Verify longest_escape_run records the run and that throughput halves for its duration.
  14. Translator, a reserved value in an abort. Verify the abort's control encoding wins and the data is not escaped into it.
  15. Cost monitor, SDR against DDR. Two parameterisations. Verify window_ps halves for DDR at the same clock — Section 9's arithmetic, checked.
  16. Cost monitor, efficiency. Drive a known mix of data and control cycles and verify efficiency_x256 matches the computed fraction exactly.
  17. Conformance, TX_ER without TX_EN. Verify the violation fires when the parameter says it is illegal and does not fire when the parameter says it is legal. Both runs are needed; a checker verified only in the illegal configuration would pass while hard-coded.
  18. Conformance, sticky mismatch across a clear. Trigger the flag, clear the counters, and verify the flag survives.
  19. Reassembly, phase from the frame boundary. Start a frame and verify the first nibble is treated as a low half without the receiver having to be told separately.
  20. Reassembly, a joiner mid-frame. Start the reassembler mid-stream and verify it produces nothing until a frame boundary supplies the phase — a receiver that guesses builds every octet from halves of two adjacent ones.
  21. A frame with an odd nibble count. Verify odd_nibble_count pulses and the frame is marked rather than silently one octet short.

What the checker must own

  • A reference model of the mapping, written from the service definition rather than from the RTL. Section 5's block is small enough that a scoreboard copied from it verifies only self-consistency.
  • A parameterised octet reassembler that reads the declared ratio and the exposed phase rather than assuming either. Scenario 19 part 4 exists to test the scoreboard, not the design.
  • At least two elaborations in regression. One width proves nothing about parameterisation, and the whole architectural claim of this chapter is about surviving a change of form.
  • Coverage crosses of interface generation against control encoding against escape occurrence. The bin (in-band encoding, reserved value in payload, escape emitted) must be well populated — it is data-dependent, so a run on clean payloads never reaches it.

13. Debugging — Vocabulary, Ratio, or Encoding

The symptom: a MAC and a PHY that were each verified independently, wired together, and delivering corrupted frames.

Step 1 — read generation_mismatch_suspected. One sticky bit, and it answers the first question: has this design driven a combination that is illegal for the generation it is wired to? A design ported between generations without re-checking its signal usage sets it, usually long before anyone investigates, and the corner case that set it has passed.

Step 2 — if the frames are corrupted but structurally intact, suspect the ratio. The signature is specific and worth recognising:

Corruption patternLikely causeWhere to look
every octet has its halves swappednibble order — the contract of Section 6the adapter's convention at both ends
every octet after the first is shifted by onean escape emitted without its dataSection 8's translator
exactly half the octets are missingwide_accept asserting every cyclethe adapter's ratio
octets fine, frame boundaries wrongcontrol encoding disagreementSection 8, and the generation's legality rules

Row one is the one that wastes the most time, because every bit arrives, every status indicator is healthy, and the corruption looks random until someone notices it is a consistent nibble swap. Exposing second_half is what turns that into a two-minute check.

Step 3 — if throughput is below nominal with no errors, read the escape counters. Section 8 showed in-band control makes throughput data-dependent. A link that is fast on random traffic and slow on a specific payload is not degrading — it is escaping, and longest_escape_run says how badly.

Step 4 — if the interface is DDR and the failures are intermittent and temperature-dependent, read window_ps and compare it against the board's actual timing. Section 9's arithmetic is the check: a DDR interface has half the window of an SDR one at the same clock, and a design ported from SDR to DDR without re-doing timing closure will work at room temperature and fail at the extremes.

Step 5 — only now suspect the contract. Chapter 4.1 §13's attribution is the right tool, and it is deliberately last: syntactic faults are cheaper to rule out than semantic ones, and a signal-level mismatch will make the contract checkers fire spuriously and name the wrong thing.

The method stated once: the sticky mismatch bit rules out a bad port, the corruption pattern names the ratio or the encoding, the escape counters explain throughput, and the timing window explains intermittency — and all four are cheaper than reasoning about the contract, which is why they come first.

14. Common Misconceptions

"The xMII is just a bus."

The wrong model: a set of wires between two blocks, no different from any other parallel connection.

What it costs: you add signals to it freely — a FEC count here, a lane-skew reading there — and each one silently narrows which PHYs your MAC can work with. You cannot explain why the interface is defined by a standard at all, since a bus is a local design decision.

The corrected model: it is a vocabulary with a durability property. Its signals name data, frame boundaries, errors and validity, and nothing about a medium or a coding — which is why a MAC written against it works over media that did not exist when it was written. Every signal you add must pass Section 3's test or it is a medium leaking upward.

"Media-independent means the interface never changes."

The wrong model: one interface, fixed forever, is what independence means.

What it costs: you are surprised by the generations, and you treat a generation change as a failure of the abstraction rather than as the abstraction working. You budget a port as a rewiring.

The corrected model: the form changes constantly — 4 bits at 25 MHz, 8 at 125, 4 double-pumped, no parallel bus at all, 32 double-pumped — and the vocabulary persists. SGMII is the strongest evidence, not the exception: it abandons the parallel bus entirely and the MAC above is unaffected, because what crosses the boundary was never wires.

"A generation change is a rewiring."

The wrong model: different widths, same logic, adjust the connections.

What it costs: you assume one MAC cycle equals one interface cycle, which is Section 11's rejected property in design form. You size buffers from the wrong ratio, you write assertions that are green on one platform and fail everywhere else, and a port throttles throughput to satisfy them.

The corrected model: a generation change is a rate change. An 8-bit MAC feeding a 4-bit interface produces two interface cycles per octet, so the adapter holds state and the two sides run at different rates. Nibble order becomes a contract with no self-check, exactly as lane order is in Chapter 3.4 §10.

"In-band and sideband control are the same thing, differently packaged."

The wrong model: both say "this is not data", so the choice is cosmetic.

What it costs: you assume throughput is independent of payload, and it is not. A payload rich in reserved values produces a burst of escapes, and buffering sized from the average overflows on the burst. You also cannot explain why block codes bother with a control-symbol space.

The corrected model: sideband costs pins and leaves the data space intact. In-band costs code space, and the moment payload can collide with a reserved value, escaping makes throughput data-dependent. Sideband has no equivalent failure. Chapter 3.5 §2 listed a control-symbol space as one of the four things a line code buys — Section 8's translator is what you build when you do not have one.

"The reconciliation sublayer is a small MAC."

The wrong model: it is a bit of logic that helps the MAC talk to the PHY, so putting useful things in it is natural.

What it costs: you add buffering, retiming, or a decision to it — and now behaviour sits between the MAC and the interface. That behaviour must be re-verified whenever either neighbour changes, which defeats the entire purpose of the boundary and makes every generation port a verification project.

The corrected model: it is a stateless mapping and its thinness is the design. It maps a frame-oriented service onto cycle-oriented signals and adds nothing. That is precisely what makes it cheap enough to rewrite per generation, which is the mechanism by which the vocabulary outlives the wires.

15. Interview Reasoning

"What does media-independent actually mean?"

The weak answer is "it works with any PHY". The answer that ends the topic gives the vocabulary test — the interface names data, frame boundaries, errors and validity, and nothing about a medium or a coding — and then applies it to a signal that fails, such as a FEC correction count, naming the cost: a MAC that cannot be paired with a PHY that has no FEC. Citing SGMII as evidence rather than as an exception is what signals real understanding, because it shows the contract surviving the largest possible change of form.

"What does a generation change cost?"

Not "a bit of rewiring". Name the three costs and which generation paid which: RMII gave up width to save pins, GMII gave up pins to keep the clock manageable, RGMII gave up timing margin by going DDR — half the setup-and-hold window at the same clock, which is why it needs controlled trace delay where GMII does not. The strong follow-up is what it costs in RTL: a rate change, state in the adapter, and a nibble-order contract with no self-check.

"You port a verified MAC to a new interface generation and the assertion suite lights up. What happened?"

The likely answer is not that the design broke. It is that the suite contained ratio-dependent properties written as general invariants — true at the old width, false at the new one — and the danger is the fix: satisfying them by forcing a one-to-one ratio the new interface cannot provide, which throttles throughput permanently. The correct properties are conservation properties with the ratio as a parameter, and the way to find out before the port is to run the suite against two elaborations.

16. Understanding Check

An interface is media-independent to the degree that its vocabulary contains nothing from the medium.

Look at what the interface names: data, a transmit enable, a transmit error, a receive data valid, a receive error. Not one of those words is optical, electrical, or about coding. No wavelength, no impedance, no disparity, no lane. That absence is the design, and it is why a MAC written against this vocabulary works over media that did not exist when it was written.

The test: could this signal be named without referring to a medium or a coding?

ProposedPasses?Why
link_usableyesa claim about capability, not mechanism
rateyesa number
fec_corrected_countnopresumes FEC exists
block_locknopresumes a block code
lane_skewnopresumes multiple lanes

Every "no" is a medium leaking upward, and Chapter 4.1 §5 named the cost: a MAC that reads FEC counts cannot be paired with a PHY that has no FEC.

The follow-up to be ready for: what about the link status vector? It half-passes — block_locked and lanes_aligned name mechanisms not every family has. Chapter 4.1 carries it anyway as a deliberate trade: a little vocabulary leakage for a MAC that can say where a link failed. The test is a reasoning tool, not a rule that decides every case.

17. What's Next

The claim this chapter defended: the xMII generations are not a progression of speeds. They are a record of what had to be given up to keep carrying the same vocabulary as rates rose — and the survival of that vocabulary across six completely different physical forms is what media-independence means.

The vocabulary test gives the working rule: a signal belongs on this interface only if it can be named without reference to a medium or a coding. The reconciliation sublayer is stateless because state is behaviour, and behaviour at a boundary must be re-verified whenever either neighbour changes. And a generation change is a rate change, which is why adapters hold state, why nibble order becomes an unchecked contract, and why an assertion written at one ratio is a coincidence rather than an invariant.

What this chapter deliberately did not give is time. Every signal here was described by what it means, not by when it moves. tx_en rises at the start of a frame — but on which cycle relative to the first data? Where is the preamble consumed? What happens to a frame already in flight when back-pressure arrives?

Chapter 4.3 — Data Flow Across the Boundary answers those, tracing a frame across the interface cycle by cycle in both directions. Chapter 4.1 gave a static contract and this chapter gave a static vocabulary; 4.3 puts both in time, and the temporal view reveals obligations that neither static view can express.

The full path is on the Ethernet curriculum index.

Continue learning

Standards & specifications

Governing standard
IEEE Std 802.3 (Ethernet)(opens IEEE in a new tab)

Defines the Ethernet MAC, the media-independent interfaces and the physical-layer sublayers, including framing, access control, auto-negotiation and per-rate PHY specifications. VLAN tagging, priority and time-sensitive shaping are defined by IEEE 802.1, not by 802.3.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the Ethernet curriculum.