Skip to content
VLSI Mentor

Ethernet · Module 10

XGMII — Seventy-Four Signals, and the Interface Built to Avoid Them

32 data and 4 control bits each way at 156.25 MHz DDR is 74 signals nobody routes. XAUI carries the same 10 Gb/s on sixteen wires and pays in per-lane deskew — plus a transparency that holds for frames and not for the stream.

Chapter 9.4 established what XGMII carries: 64B/66B below it, four byte lanes, a start character confined to lane 0, and a deficit idle mechanism that keeps the interframe gap honest four bytes at a time.

What it did not confront is the pin count that width implies.

32 data + 4 control + 1 clock = 37 signals per direction 37 × 2 = 74 signals

Seventy-four, clocked at 156.25 MHz on both edges, across a board.

That is not an interface anybody wants to route, and 10 Gigabit's answer was to define a second interface whose only job is to make the first one unnecessary between chips: XAUI, four lanes at 3.125 Gbaud with 8B/10B, sixteen wires instead of seventy-four.

And XAUI brings back everything Chapter 10.5 just built, multiplied by four — comma alignment per lane, running disparity per lane, ordered sets per lane — plus one problem SGMII does not have. Four independently serialised lanes traverse four paths and arrive at genuinely different times, so a receiver must deskew them before the four streams can be reassembled into one.

1. Scope — What This Chapter Owns

This chapter owns XGMII and XAUI as interfaces: the pin count, the clocking, the serialisation that exists because of the pin count, the per-lane machinery it requires, and the deskew problem it introduces.

It does not re-derive what other chapters own. Chapter 9.4 owns 64B/66B, the lane-0 start rule and deficit idle; Chapter 3.5 owns 8B/10B's construction; Chapter 10.5 owns comma alignment, running disparity and their convergence behaviour, which this chapter uses four times over; Chapter 4.4 owns clock compensation as a mechanism, which the /R/ column implements here; Chapter 9.5 owns lane striping and deskew at 100 Gigabit, which is the same problem at a different scale with a different marker.

Chapter 11.1 opens the module that owns link discovery and autonegotiation in their own right.

The claim this chapter defends: a layer described as transparent is transparent at exactly one granularity, and asserting its guarantee at a finer one produces a property that is false because of the mechanism the layer needs in order to be transparent at all.

2. Seventy-Four Signals

The ten gigabit media independent interface carries thirty two data bits, four control bits and a clock in each direction, which is seventy four signals in total clocked at one hundred and fifty six point two five megahertz on both edges. That is impractical to route across a board, so an extender sublayer converts it into the ten gigabit attachment unit interface: four serial lanes in each direction at three point one two five gigabaud using eight bit to ten bit coding, sixteen wires in total. A second extender sublayer at the far end converts back, so both the media access control layer and the physical layer see an ordinary interface and neither knows the serial link is there.ReconciliationXGMII: 74 signalsXGXSextender, near endXAUI4 lanes, 3.125 GBdXGXSextender, far end16 wiresvs 74PCSXGMII again12
Figure 1 — XGMII's width is the reason XAUI exists; the extender sublayer makes one look like the other.
SignalWidthDirectionNotes
TXD[31:0]32MAC → PHYfour byte lanes
TXC[3:0]4MAC → PHYone control bit per lane
TX_CLK1MAC → PHY156.25 MHz, both edges
RXD[31:0]32PHY → MAC
RXC[3:0]4PHY → MAC
RX_CLK1PHY → MAC156.25 MHz, both edges
74

The clocking, computed:

10 Gb/s ÷ 32 bits = 312.5 M transfers/s 312.5 ÷ 2 = 156.25 MHz DDR

And 74 signals at 156.25 MHz DDR is not a routing problem so much as a package problem. Every signal is single-ended, every group must be length-matched, and Chapter 10.4's entire delay argument applies to thirty-six data and control lines against one clock rather than to four.

Compare the whole module's pin counts in one place, because XGMII is the outlier in both directions.

InterfaceRateSignals
SGMII1 Gb/s4
RMII100 Mb/s8
RGMII1 Gb/s12
MII100 Mb/s16
GMII1 Gb/s24
XGMII10 Gb/s74
XAUI10 Gb/s16

XGMII carries ten times GMII's rate on three times its pins, which is efficient per bit and unusable in absolute terms. XAUI carries the same 10 Gb/s on sixteen wires — a 4.6× reduction — and is what actually appears on a board.

3. The Rate, Computed

XAUI's numbers all fall out of one requirement: carry an XGMII column, and do it on four serial lanes.

StepWorkingResult
XGMII column4 bytes32 bits
columns per second10 Gb/s ÷ 32312.5 M
one lane carries one byte per column312.5 M bytes/s per lane
as code groupsone per byte312.5 M code groups/s
8B/10B× 10 bits each3125 Mbaud per lane
four lanes4 × 3.125 × 8/1010 Gb/s
unit interval1 ÷ 3.125 GHz320 ps

The third row is the design. An XGMII column is four bytes on four byte lanes; XAUI gives each byte lane its own serial lane. So the mapping is not a striping decision at all — lane n of XAUI carries byte lane n of XGMII, always — which makes the adapter almost trivial and the reassembly exact.

Compare that with Chapter 9.5's 100 Gigabit striping, where 66-bit blocks are dealt round-robin across twenty PCS lanes and the receiver must recover the deal from alignment markers.

XAUI100GBASE-R
how data maps to lanesbyte lane n → serial lane nround-robin across 20
lanes are interchangeableno — each carries its own byte positionno — each carries its own deal position
how a receiver knows which lane is which/A/ columns, sent simultaneouslyalignment markers naming the lane
what identifies a laneits wireits marker's contents
a swapped lanebreaks the linkis absorbed

The last row is the difference that matters. XAUI lanes are identified by which wire they arrive on, so swapping two is a board fault that produces perfectly legal traffic with the bytes rearranged — exactly Chapter 9.4 §9's XGMII lane swap, one layer down. 100 Gigabit's markers carry the lane number in the data, which is why a swap there is a supported condition.

XAUI's /A/ columns solve a different problem: not which lane, but when.

4. RTL 1 — XGMII In, Four Lanes Out

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// The XGMII Extender Sublayer's transmit half: take an XGMII column,
// convert each byte lane to an 8B/10B code group, and hand it to that
// lane's serialiser.
//
// THE MAPPING:
//   XGMII byte lane n -> XAUI serial lane n. Always. Every column.
//   There is no striping and no deal, because the receiver identifies
//   a lane by WHICH WIRE it arrives on -- unlike Chapter 9.5's PCS
//   lanes, which carry their own names.
//
// THE NUMBERS:
//   10 Gb/s / 32 bits          = 312.5 M columns/s
//   one byte per lane per column = 312.5 M code groups/s per lane
//   x 10 bits (8B/10B)         = 3125 Mbaud per lane
//   4 lanes x 3.125 x 8/10     = 10 Gb/s   <- closes
//   1 UI                       = 320 ps
//
// XGMII CONTROL BITS BECOME CODE GROUPS. TXC[n] set means TXD[8n+7:8n]
// is a control character, which maps to an 8B/10B K code group. So the
// four control WIRES disappear into the encoding, exactly as Chapter
// 10.5's TX_CTL did.
package xaui_pkg;
 
  localparam int unsigned LANES        = 4;
  localparam int unsigned CODE_BITS    = 10;
  localparam int unsigned LANE_MBAUD   = 3125;
  localparam int unsigned COLUMNS_PER_S_M = 312;   // 312.5, truncated
 
  // 1 UI = 320 ps. Clause 47 receivers absorb roughly 40 UI of skew:
  //   40 x 320 ps = 12.8 ns
  // which is the number Section 9 turns into a FIFO depth.
  localparam int unsigned UI_PS         = 320;
  localparam int unsigned SKEW_UI       = 40;
  localparam int unsigned SKEW_PS       = SKEW_UI * UI_PS;   // 12800
 
  // XAUI code groups. /K/ carries the comma, /A/ is the deskew marker,
  // /R/ is the skip used for clock compensation.
  localparam logic [7:0] K28_0 = 8'h1C;   // /R/ skip
  localparam logic [7:0] K28_3 = 8'h7C;   // /A/ align
  localparam logic [7:0] K28_5 = 8'hBC;   // /K/ sync, contains the comma
  localparam logic [7:0] K27_7 = 8'hFB;   // /S/ start
  localparam logic [7:0] K29_7 = 8'hFD;   // /T/ terminate
  localparam logic [7:0] K30_7 = 8'hFE;   // /E/ error
 
  // The idle sequence is A K R K R K R ... with an /A/ column roughly
  // every 16 columns, so deskew markers arrive often enough to re-align
  // quickly and rarely enough to cost almost nothing.
  localparam int unsigned A_INTERVAL = 16;
 
  typedef enum logic [2:0] {
    COL_DATA, COL_IDLE_A, COL_IDLE_K, COL_IDLE_R,
    COL_START, COL_TERMINATE, COL_ERROR
  } column_kind_e;
 
endpackage
 
module xgmii_to_xaui_adapter
  import xaui_pkg::*;
#(
  parameter int unsigned CNT_W = 24
) (
  input  logic clk,                       // 312.5 MHz column clock
  input  logic rst_n,
 
  // XGMII, as Chapter 9.4 defines it.
  input  logic [31:0] xgmii_d,
  input  logic [3:0]  xgmii_c,
  input  logic        xgmii_valid,
 
  // To the four lane encoders.
  output logic [LANES-1:0][7:0] lane_octet,
  output logic [LANES-1:0]      lane_is_k,
  output logic                  lane_valid,
  output column_kind_e          column_kind,
 
  output logic [CNT_W-1:0] c_columns,
  output logic [CNT_W-1:0] c_data_columns,
  output logic [CNT_W-1:0] c_idle_columns,
  // A start character on a lane other than 0. Chapter 9.4 owns the
  // rule; this module owns DETECTING it before it is serialised,
  // because a serialised illegal column is four lanes of legal-looking
  // code groups that mean nothing.
  output logic             start_off_lane0,
  output logic [CNT_W-1:0] c_start_off_lane0
);
 
  integer i;
 
  always_comb begin
    column_kind = COL_DATA;
    if (xgmii_c[0] && (xgmii_d[7:0] == K27_7))       column_kind = COL_START;
    else if (|xgmii_c && (xgmii_d[7:0] == K29_7))    column_kind = COL_TERMINATE;
    else if (&xgmii_c && (xgmii_d[7:0] == K28_5))    column_kind = COL_IDLE_K;
    else if (&xgmii_c && (xgmii_d[7:0] == K28_3))    column_kind = COL_IDLE_A;
    else if (&xgmii_c && (xgmii_d[7:0] == K28_0))    column_kind = COL_IDLE_R;
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      lane_valid <= 1'b0; c_columns <= '0; c_data_columns <= '0;
      c_idle_columns <= '0; start_off_lane0 <= 1'b0;
      c_start_off_lane0 <= '0;
      for (i = 0; i < LANES; i = i + 1) begin
        lane_octet[i] <= 8'd0;
        lane_is_k[i]  <= 1'b0;
      end
    end else begin
      lane_valid      <= 1'b0;
      start_off_lane0 <= 1'b0;
 
      if (xgmii_valid) begin
        // THE MAPPING. Byte lane n to serial lane n, and the control
        // bit becomes the K/data distinction the encoder needs.
        for (i = 0; i < LANES; i = i + 1) begin
          lane_octet[i] <= xgmii_d[8*i +: 8];
          lane_is_k[i]  <= xgmii_c[i];
        end
        lane_valid <= 1'b1;
 
        if (!(&c_columns)) c_columns <= c_columns + 1'b1;
        if (|xgmii_c) begin
          if (!(&c_idle_columns)) c_idle_columns <= c_idle_columns + 1'b1;
        end else begin
          if (!(&c_data_columns)) c_data_columns <= c_data_columns + 1'b1;
        end
 
        // A start character anywhere but lane 0 is illegal (Chapter
        // 9.4 §4). Detecting it HERE matters, because once serialised
        // it becomes four lanes of individually valid code groups and
        // nothing downstream has any reason to object.
        for (i = 1; i < LANES; i = i + 1) begin
          if (xgmii_c[i] && (xgmii_d[8*i +: 8] == K27_7)) begin
            start_off_lane0 <= 1'b1;
            if (!(&c_start_off_lane0))
              c_start_off_lane0 <= c_start_off_lane0 + 1'b1;
          end
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that XGMII's four control wires disappear into the encoding, exactly as Chapter 10.5's TX_CTL did. TXC[n] does not travel — it selects whether lane n's byte becomes a K code group or a data code group, and the receiver recovers it from the 8B/10B decode. Four wires become zero, and the information survives.

Deliberately simplified: the column classifier examines lane 0 for most kinds. A production adapter checks all four lanes against the full column format, since a well-formed idle column requires the same character on all four.

Production implication: start_off_lane0 is detected before serialisation and that placement is the point. After the adapter, an illegal column has become four individually valid, individually disparity-correct code groups, and nothing downstream has any reason to reject them. The far end will reassemble a column with a start character in the wrong place and behave undefinedly, which is Chapter 9.4 §12's rejected property arriving in a new form: the rule must be enforced where it is still expressible.

5. RTL 2 — Four Encoders, Four Disparities

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE (with an 8B/10B encoder core per lane).
//
// Four independent 8B/10B encoders, one per XAUI lane.
//
// THE WORD THAT MATTERS IS "INDEPENDENT". Each lane has its OWN running
// disparity, because each lane is its own serial stream with its own
// DC-balance requirement. There is no shared disparity across a column,
// and a design that keeps one is wrong in a way that produces valid
// code groups and a slowly drifting DC level on three of four lanes.
//
// Chapter 10.5 owns 8B/10B's convergence behaviour on ONE lane. Here
// there are four of them, converging independently after a common
// alignment event -- so the convergence WINDOW is per lane, and the
// slowest lane sets when the link is usable.
module xaui_8b10b_lane_encoder
  import xaui_pkg::*;
#(
  parameter int unsigned CNT_W = 24
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic [LANES-1:0][7:0] lane_octet,
  input  logic [LANES-1:0]      lane_is_k,
  input  logic                  lane_valid,
 
  output logic [LANES-1:0][CODE_BITS-1:0] lane_code,
  output logic [LANES-1:0]                lane_code_valid,
 
  // Per-lane running disparity, exported. Four numbers, not one.
  output logic [LANES-1:0] lane_rd,
  // A lane whose running disparity has been the same polarity for an
  // implausibly long run. Not illegal, and a strong hint that the
  // encoder's alternate-code selection is broken on that lane.
  output logic [LANES-1:0] lane_rd_stuck,
  output logic [LANES-1:0][CNT_W-1:0] c_lane_codes,
  output logic [LANES-1:0][7:0]       lane_rd_run
);
 
  // A run of same-polarity disparity longer than this is a hint, not a
  // violation -- legal data can produce long runs, but not this long
  // by chance.
  localparam int unsigned RD_RUN_ALARM = 8'd64;
 
  // Placeholder for the 8B/10B encoder core. Chapter 3.5 owns the
  // table; what matters here is that it CONSUMES and PRODUCES the
  // running disparity, which is why each lane needs its own copy.
  function automatic logic [CODE_BITS-1:0] encode_8b10b
    (input logic [7:0] octet, input logic is_k, input logic rd_in);
    encode_8b10b = {rd_in, is_k, octet};
  endfunction
 
  function automatic logic next_rd
    (input logic [7:0] octet, input logic is_k, input logic rd_in);
    next_rd = rd_in ^ (^octet) ^ is_k;
  endfunction
 
  integer i;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      for (i = 0; i < LANES; i = i + 1) begin
        lane_code[i]       <= '0;
        lane_code_valid[i] <= 1'b0;
        lane_rd[i]         <= 1'b0;
        lane_rd_stuck[i]   <= 1'b0;
        lane_rd_run[i]     <= 8'd0;
        c_lane_codes[i]    <= '0;
      end
    end else begin
      for (i = 0; i < LANES; i = i + 1) lane_code_valid[i] <= 1'b0;
 
      if (lane_valid) begin
        for (i = 0; i < LANES; i = i + 1) begin
          lane_code[i]       <= encode_8b10b(lane_octet[i], lane_is_k[i],
                                             lane_rd[i]);
          lane_code_valid[i] <= 1'b1;
 
          // EACH LANE UPDATES ITS OWN DISPARITY. Sharing one across
          // four lanes produces valid code groups on every lane and a
          // DC level that drifts on three of them.
          lane_rd[i] <= next_rd(lane_octet[i], lane_is_k[i], lane_rd[i]);
 
          if (next_rd(lane_octet[i], lane_is_k[i], lane_rd[i]) == lane_rd[i]) begin
            if (lane_rd_run[i] != 8'hFF) lane_rd_run[i] <= lane_rd_run[i] + 8'd1;
            if (lane_rd_run[i] == 8'(RD_RUN_ALARM)) lane_rd_stuck[i] <= 1'b1;
          end else begin
            lane_rd_run[i]   <= 8'd0;
            lane_rd_stuck[i] <= 1'b0;
          end
 
          if (!(&c_lane_codes[i])) c_lane_codes[i] <= c_lane_codes[i] + 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable, with an 8B/10B encoder core per lane; the encode and disparity functions are placeholders for Chapter 3.5's table.

What it teaches: that running disparity is per lane and not per column, which is easy to get wrong because a column looks like the natural unit. Each XAUI lane is an independent, AC-coupled serial link with its own DC-balance requirement — sharing one disparity variable across four encoders produces code groups that are individually valid on every lane and a DC level that drifts on three of them, because three lanes' balance is being corrected using a fourth lane's data.

Deliberately simplified: the encoder and disparity functions are placeholders. The real 8B/10B table maps each octet to one of two code groups chosen by the current disparity, and the choice is what keeps the running total bounded.

Production implication: lane_rd_stuck is a hint rather than a violation, and that distinction is deliberate. Legal data can produce long same-polarity runs; sixty-four in a row cannot happen by chance on random data, and it is the signature of an encoder whose alternate-code selection is broken on that lane — which produces perfectly valid code groups and a lane that fails AC coupling after a few microseconds.

6. Why XAUI Needs Alignment Columns

An align column is transmitted on all four lanes at the same instant. The four lanes then traverse four separate serial paths of slightly different lengths, so the align characters arrive at the receiver spread out in time by up to about forty unit intervals, which is twelve point eight nanoseconds at three point one two five gigabaud. The receiver finds each lane's align character and measures how late that lane is relative to the earliest, then buffers each lane by that amount so that all four are presented to the reassembly stage on the same column. The align columns are embedded in the idle sequence roughly every sixteen columns, so realignment after a disturbance is quick and the cost in bandwidth is negligible./A/ on all 4 lanessame instantFour pathsslightly differentArrive apartup to ~40 UI = 12.8 nsMeasure the offsetsper lane, in code groupsBuffer to theslowestthen reassemble/A/ every ~16columnscheap and frequent12
Figure 2 — the four lanes leave together and arrive apart; the /A/ column is what tells the receiver by how much.

XGMII's four byte lanes arrive together because they are length-matched traces clocked by one clock. XAUI's four lanes are four independent serial links, and nothing makes them agree.

Each lane has its own serialiser, its own transmission line, its own receiver and its own clock-data recovery loop. They leave the transmitter on the same column and arrive at different times, and the standard's receivers absorb roughly 40 UI of that difference:

40 × 320 ps = 12.8 ns

So the receiver needs a marker that was transmitted simultaneously on all four lanes, and that is the /A/ column: K28.3 on every lane at once.

And /A/ lives inside the idle sequence, which is the elegant part:

A K R K R K R K R K R K R K R K A K R K R …

An /A/ column roughly every sixteen columns, embedded in idle that had to be transmitted anyway. Frequent enough that a disturbed link re-aligns in well under a microsecond, and cheap enough to be free — it displaces idle, not data.

ColumnCharacterJob
/A/K28.3deskew — sent simultaneously on all four lanes
/K/K28.5sync — carries the comma for code-group alignment
/R/K28.0skip — inserted and deleted for clock compensation

Three characters, three completely different jobs, all inside the idle stream.

7. RTL 3 — Generating the Idle That Carries Everything

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Generates XAUI's idle sequence, which is three mechanisms wearing one
// costume.
//
//   A K R K R K R K R K R K R K R K A K R K R ...
//
//   /A/ K28.3 -- transmitted SIMULTANEOUSLY on all four lanes, roughly
//        every 16 columns. The deskew reference.
//   /K/ K28.5 -- carries the comma, so per-lane code-group alignment
//        has something to hunt for.
//   /R/ K28.0 -- the skip column. Section 10's clock compensation
//        inserts and deletes these, and ONLY these.
//
// WHY /R/ AND NOT /K/ OR /A/ IS THE DELETABLE ONE:
//   /A/ is a timing reference -- deleting one loses a deskew
//        opportunity, and deleting a pair changes the interval the far
//        end measures against.
//   /K/ carries the comma -- deleting them starves alignment recovery.
//   /R/ carries NOTHING. It exists to be deletable, which is why the
//        idle sequence is more than half /R/.
module xaui_align_sequencer
  import xaui_pkg::*;
#(
  parameter int unsigned CNT_W = 24
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic idle_request,       // no data to send this column
  input  logic force_align,        // e.g. after a link event
 
  output logic [LANES-1:0][7:0] idle_octet,
  output logic [LANES-1:0]      idle_is_k,
  output logic                  idle_valid,
  output column_kind_e          idle_kind,
 
  // Exported so Section 10's compensator knows which columns it may
  // delete and which it must not.
  output logic                  is_deletable,
  output logic [4:0]            columns_since_a,
 
  output logic [CNT_W-1:0] c_a_columns,
  output logic [CNT_W-1:0] c_k_columns,
  output logic [CNT_W-1:0] c_r_columns,
  // The interval since the last /A/ exceeded the nominal. Legal after
  // a compensation event; a fault if it persists, because the far end
  // measures skew against these.
  output logic             a_interval_stretched
);
 
  logic [4:0] since_a_q;
  logic       kr_phase_q;      // alternates K and R between /A/ columns
 
  assign columns_since_a = since_a_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      since_a_q <= 5'd0; kr_phase_q <= 1'b0;
      idle_valid <= 1'b0; idle_kind <= COL_IDLE_K;
      is_deletable <= 1'b0; a_interval_stretched <= 1'b0;
      c_a_columns <= '0; c_k_columns <= '0; c_r_columns <= '0;
      for (int i = 0; i < LANES; i = i + 1) begin
        idle_octet[i] <= K28_5;
        idle_is_k[i]  <= 1'b1;
      end
    end else begin
      idle_valid           <= 1'b0;
      is_deletable         <= 1'b0;
      a_interval_stretched <= 1'b0;
 
      if (idle_request) begin
        idle_valid <= 1'b1;
 
        if (force_align || (since_a_q >= 5'(A_INTERVAL))) begin
          // /A/ COLUMN. K28.3 on ALL FOUR LANES, at the same instant --
          // which is the property that makes it a skew measurement
          // rather than just another idle character.
          for (int i = 0; i < LANES; i = i + 1) begin
            idle_octet[i] <= K28_3;
            idle_is_k[i]  <= 1'b1;
          end
          idle_kind    <= COL_IDLE_A;
          is_deletable <= 1'b0;          // NEVER deletable
          since_a_q    <= 5'd0;
          kr_phase_q   <= 1'b0;
          if (!(&c_a_columns)) c_a_columns <= c_a_columns + 1'b1;
 
        end else if (!kr_phase_q) begin
          // /K/ -- carries the comma. Not deletable: alignment recovery
          // needs a supply of them.
          for (int i = 0; i < LANES; i = i + 1) begin
            idle_octet[i] <= K28_5;
            idle_is_k[i]  <= 1'b1;
          end
          idle_kind    <= COL_IDLE_K;
          is_deletable <= 1'b0;
          kr_phase_q   <= 1'b1;
          since_a_q    <= since_a_q + 5'd1;
          if (!(&c_k_columns)) c_k_columns <= c_k_columns + 1'b1;
 
        end else begin
          // /R/ -- carries nothing, and exists to be deletable. More
          // than half of the idle sequence is /R/ for exactly this
          // reason: clock compensation needs a supply of columns whose
          // removal costs nothing.
          for (int i = 0; i < LANES; i = i + 1) begin
            idle_octet[i] <= K28_0;
            idle_is_k[i]  <= 1'b1;
          end
          idle_kind    <= COL_IDLE_R;
          is_deletable <= 1'b1;          // THE deletable column
          kr_phase_q   <= 1'b0;
          since_a_q    <= since_a_q + 5'd1;
          if (!(&c_r_columns)) c_r_columns <= c_r_columns + 1'b1;
        end
 
        // The /A/ interval is what the far end measures skew against.
        // Stretching it is legal after a compensation event and a fault
        // if it persists.
        if (since_a_q > 5'(A_INTERVAL + 4)) a_interval_stretched <= 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that /R/ exists in order to be deleted, and the idle sequence's composition follows from that. /A/ is a timing reference and deleting one costs a deskew opportunity. /K/ carries the comma and deleting them starves alignment recovery. /R/ carries nothing at all — it is a placeholder whose only property is that removing it changes nothing — and more than half the idle sequence is /R/ precisely so that clock compensation always has something safe to consume.

Deliberately simplified: the sequence is a strict A K R K R … with a counter. Real transmitters randomise the idle to spread its spectrum, and the /A/ interval is a range rather than a constant.

Production implication: is_deletable is exported rather than recomputed downstream, and the reason is that getting it wrong is silent. A compensator that deletes an /A/ column removes a deskew reference the far end is counting; one that deletes a /K/ starves the comma supply and slows every future re-alignment. Both produce a link that works and re-aligns more slowly than it should, which nothing measures unless something is looking for it.

8. RTL 4 — Deskewing Four Lanes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Aligns the four lanes using the /A/ column, and reports the measured
// skew.
//
// THE DEPTH, computed from the standard rather than guessed:
//   1 UI at 3.125 Gbaud   = 320 ps
//   clause 47 skew budget = ~40 UI
//   40 x 320 ps           = 12.8 ns
//   one code group        = 10 UI = 3.2 ns
//   12.8 / 3.2            = 4 code groups
//   so a 6-deep FIFO per lane covers the budget with margin
//
// AND THE MEASUREMENT IS FREE. The /A/ columns left the transmitter
// simultaneously, so the difference in their arrival times IS the skew
// -- obtained as a by-product of the alignment the receiver had to do
// anyway, exactly as Chapter 9.5 §7 gets it from alignment markers.
module xaui_lane_deskew_stage
  import xaui_pkg::*;
#(
  parameter int unsigned DEPTH = 6,
  parameter int unsigned DW    = 3,
  parameter int unsigned CNT_W = 20
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic [LANES-1:0]      lane_valid,
  input  logic [LANES-1:0][7:0] lane_octet,
  input  logic [LANES-1:0]      lane_is_k,
  input  logic [LANES-1:0]      lane_a_seen,     // /A/ on this lane
  input  logic [LANES-1:0]      lane_synced,     // comma alignment held
 
  output logic                  deskew_done,
  output logic [LANES-1:0][7:0] aligned_octet,
  output logic [LANES-1:0]      aligned_is_k,
  output logic                  aligned_valid,
 
  // The measured skew, per lane, in code groups. The number a board
  // engineer needs and nothing else in the design produces.
  output logic [LANES-1:0][DW-1:0] lane_depth,
  output logic [DW-1:0]            worst_depth,
 
  // A lane arrived further behind than the buffer holds. REPORTED and
  // deskew REFUSED -- aligning three lanes and guessing the fourth
  // produces a stream that reassembles into plausible garbage.
  output logic                     skew_exceeded,
  output logic [1:0]               skew_exceeded_lane,
 
  output logic [CNT_W-1:0] c_deskews,
  output logic             ever_skew_exceeded,
  // Deskew is only meaningful once every lane has code-group sync.
  output logic             waiting_for_sync
);
 
  logic [DW-1:0]     depth_q [LANES];
  logic [LANES-1:0]  seen_q;
  logic              hunting_q;
 
  assign waiting_for_sync = !(&lane_synced);
 
  integer i;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      deskew_done <= 1'b0; aligned_valid <= 1'b0; worst_depth <= '0;
      skew_exceeded <= 1'b0; skew_exceeded_lane <= 2'd0;
      c_deskews <= '0; ever_skew_exceeded <= 1'b0;
      seen_q <= '0; hunting_q <= 1'b1;
      for (i = 0; i < LANES; i = i + 1) begin
        depth_q[i]    <= '0;
        lane_depth[i] <= '0;
      end
    end else if (clear || !(&lane_synced)) begin
      // Losing code-group sync on ANY lane invalidates the deskew: the
      // code-group boundary that /A/ was measured against is gone.
      deskew_done <= 1'b0; hunting_q <= 1'b1; seen_q <= '0;
      skew_exceeded <= 1'b0;
      for (i = 0; i < LANES; i = i + 1) depth_q[i] <= '0;
      // ever_skew_exceeded survives: a board is a board.
    end else begin
      aligned_valid <= 1'b0;
      skew_exceeded <= 1'b0;
 
      if (hunting_q) begin
        for (i = 0; i < LANES; i = i + 1) begin
          if (lane_valid[i]) begin
            if (lane_a_seen[i] && !seen_q[i]) begin
              // This lane's /A/. Its current depth IS how far behind
              // the earliest lane it is.
              seen_q[i]     <= 1'b1;
              lane_depth[i] <= depth_q[i];
              if (depth_q[i] > worst_depth) worst_depth <= depth_q[i];
            end else if (|seen_q) begin
              if (depth_q[i] == DW'(DEPTH - 1)) begin
                // OUT OF RANGE. Refuse; do not wrap. A wrapped buffer
                // aligns on stale data and reports a plausible SMALL
                // skew, which is the most convincing wrong answer this
                // module can give.
                skew_exceeded      <= 1'b1;
                skew_exceeded_lane <= 2'(i);
                ever_skew_exceeded <= 1'b1;
                hunting_q          <= 1'b1;
                seen_q             <= '0;
              end else begin
                depth_q[i] <= depth_q[i] + 1'b1;
              end
            end
          end
        end
 
        if (&seen_q) begin
          // ALL FOUR. Three is not deskew -- an XGMII column needs all
          // four bytes, so a missing lane means every column is wrong.
          hunting_q   <= 1'b0;
          deskew_done <= 1'b1;
          if (!(&c_deskews)) c_deskews <= c_deskews + 1'b1;
        end
 
      end else begin
        aligned_octet <= lane_octet;
        aligned_is_k  <= lane_is_k;
        aligned_valid <= &lane_valid;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that losing code-group sync on any one lane invalidates the deskew for all four, and the ordering is not negotiable. Deskew is measured in code groups, and a code group only exists once comma alignment has found its boundary — so a lane that loses sync has lost the unit the measurement was expressed in. waiting_for_sync gates the whole stage, and a design that deskews first and syncs later is measuring an offset in a unit it does not yet have.

Deliberately simplified: one code group per lane per cycle and a restart on any excess. Production XGXS implementations hunt continuously and re-verify against a second /A/ column before declaring alignment.

Production implication: lane_depth is the measured skew and it is the module's most valuable output, exactly as in Chapter 9.5 §8. Every other signal says the link works; this one says by how much. A board with 1 code group of skew and a board with 5 both report deskew_done, both pass traffic, and the second is one connector, one temperature step or one layout revision from failing.

9. The Skew Budget, Computed

Turn the standard's tolerance into a FIFO depth, because the arithmetic is short and the conclusion is not obvious.

StepWorkingResult
lane baud rate3.125 Gbaud
unit interval1 ÷ 3.125 GHz320 ps
skew budget~40 UI40 × 320 = 12.8 ns
one code group10 UI10 × 320 = 3.2 ns
budget in code groups12.8 ÷ 3.24 code groups
FIFO depth with marginround up6 per lane

Four code groups is a small number, and that is the point.

Compare it with Chapter 9.5's 100 Gigabit budget:

XAUI100GBASE-R
skew tolerated12.8 ns180 ns
in units of the lane's code/block4 code groups14 blocks
what the lanes traversetraces on one boardfibres in a building
equivalent physical difference≈ 2.6 m of FR-4≈ 37 m of fibre
lanes can be reorderednoyes

The fourth row explains the first. XAUI's lanes are four differential pairs going the same 20 to 50 cm across one board, so 12.8 ns is generous — it is about 2.6 m of FR-4 at roughly 150 ps per inch, on traces that are a few tens of centimetres long. 100 Gigabit's lanes may be separate fibres in separate bundles, so its budget has to be fourteen times larger.

And the smaller budget is what lets XAUI skip lane identification entirely. A lane cannot be more than four code groups late, so its identity is never in doubt — there is no possibility of confusing lane 2's /A/ with lane 2's next /A/. At 100 Gigabit, with 14 blocks of skew and 20 lanes, the markers must name themselves.

10. RTL 5 — Deleting the Column That Carries Nothing

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Clock compensation: absorb the frequency difference between the two
// ends by inserting and deleting /R/ columns.
//
// WHY IT IS NEEDED. The transmitting XGXS runs on ITS clock; the
// receiving one runs on ITS clock. Both are within tolerance and
// neither is exactly 156.25 MHz. Over time the receiver either
// accumulates columns it cannot forward or runs out of columns to
// forward -- Chapter 4.4's elastic buffer problem, at 312.5 M columns
// per second.
//
// THE MECHANISM, and it is the reason for this chapter's rejected
// property: the XGXS DELETES an /R/ column when its buffer fills and
// INSERTS one when it empties. So the column sequence leaving the far
// XGXS is NOT the sequence that entered the near one -- by design, on
// every conforming link, whenever the two clocks differ at all.
//
// AND THE GUARANTEE IT DOES MAKE is that FRAMES are unchanged. Not one
// byte of frame content, not one column between /S/ and /T/, is ever
// added or removed. Transparency is stated over frames; the stream is
// deliberately modified.
module xaui_clock_compensation
  import xaui_pkg::*;
#(
  parameter int unsigned FIFO_DEPTH = 32,
  parameter int unsigned HIGH_MARK  = 24,
  parameter int unsigned LOW_MARK   = 8,
  parameter int unsigned CNT_W = 24
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic         col_valid,
  input  column_kind_e col_kind,
  input  logic         col_is_deletable,   // TRUE only for /R/
  input  logic         in_frame,
 
  input  logic [5:0]   fifo_level,
 
  output logic         forward_column,
  output logic         delete_column,
  output logic         insert_column,
 
  output logic [CNT_W-1:0] c_deletions,
  output logic [CNT_W-1:0] c_insertions,
  // The buffer reached a limit with no deletable column available.
  // This is the real failure: compensation had nowhere to act, so a
  // column will be lost or duplicated somewhere it matters.
  output logic             compensation_starved,
  output logic [CNT_W-1:0] c_starvations,
  output logic             ever_starved,
  // Net columns added minus removed. A number that should hover near
  // zero and drift slowly; a monotone trend means the clocks differ by
  // more than the mechanism can absorb.
  output logic signed [CNT_W-1:0] net_adjustment
);
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      forward_column <= 1'b0; delete_column <= 1'b0; insert_column <= 1'b0;
      c_deletions <= '0; c_insertions <= '0;
      compensation_starved <= 1'b0; c_starvations <= '0;
      ever_starved <= 1'b0; net_adjustment <= '0;
    end else if (clear) begin
      c_deletions <= '0; c_insertions <= '0; c_starvations <= '0;
      net_adjustment <= '0;
      // ever_starved survives.
    end else begin
      forward_column       <= 1'b0;
      delete_column        <= 1'b0;
      insert_column        <= 1'b0;
      compensation_starved <= 1'b0;
 
      if (col_valid) begin
        if (in_frame) begin
          // NEVER touch a frame. Not one column between /S/ and /T/,
          // whatever the buffer is doing. This is the guarantee that
          // makes "transparent" true at the granularity it is stated.
          forward_column <= 1'b1;
 
        end else if (fifo_level > 6'(HIGH_MARK)) begin
          if (col_is_deletable) begin
            // DELETE an /R/. The stream is now shorter than it was,
            // and nothing above notices, because /R/ carries nothing.
            delete_column <= 1'b1;
            net_adjustment <= net_adjustment - 1;
            if (!(&c_deletions)) c_deletions <= c_deletions + 1'b1;
          end else begin
            // The buffer is filling and this column is /A/ or /K/,
            // which must not be removed. Forward it and wait.
            forward_column <= 1'b1;
            if (fifo_level == 6'(FIFO_DEPTH - 1)) begin
              compensation_starved <= 1'b1;
              ever_starved         <= 1'b1;
              if (!(&c_starvations)) c_starvations <= c_starvations + 1'b1;
            end
          end
 
        end else if (fifo_level < 6'(LOW_MARK)) begin
          // INSERT an /R/. Also invisible above, for the same reason.
          insert_column  <= 1'b1;
          forward_column <= 1'b1;
          net_adjustment <= net_adjustment + 1;
          if (!(&c_insertions)) c_insertions <= c_insertions + 1'b1;
 
        end else begin
          forward_column <= 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the layer's transparency is stated over frames and deliberately not over the stream, and this module is where that distinction lives. Not one column between /S/ and /T/ is ever added or removed — the in_frame branch is unconditional, whatever the buffer is doing. Everything outside a frame is fair game, and /R/ in particular exists to be consumed.

Deliberately simplified: one deletable column kind and fixed high and low marks. Production compensators may delete or insert several columns at once when the idle sequence permits, and the marks are usually programmable to trade buffer depth against how often compensation acts.

Production implication: compensation_starved is the genuine failure this module can have, and it is not the same as the buffer filling. A full buffer with an /R/ available is handled; a full buffer arriving at an /A/ or /K/ column is compensation with nowhere to act — and the next thing to overflow will be a column that matters. A rising c_starvations on a link that still works means the idle sequence is not supplying enough /R/ columns for the clock difference, which is a design or configuration fact and not a fault in the link.

11. RTL 6 — Proving the Extender Is There and Working

A column sequence enters the near extender sublayer, crosses four serial lanes, and emerges from the far extender sublayer. Inside a frame, between the start and terminate characters, every column is preserved exactly and none is ever added or removed. Outside a frame, skip columns are inserted when the receiving buffer runs low and deleted when it fills, in order to absorb the difference between the two ends' clocks. So the frame content that emerges is identical to what entered, while the total column sequence is not, and it is different by design on every conforming link. An assertion comparing the streams bit for bit therefore fails whenever the two clocks differ at all, which is always.Column sequence inframes + idleInside a framenever touchedFrames identicalthe real guaranteeOutside a frame/R/ added and removedStream NOT identicalby design, alwaysTwo clocks differthe reason12
Figure 3 — the XGXS pair preserves frames exactly and modifies the idle stream on purpose; a bit-exact comparison of the stream is false by design.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// A loopback self-test for the XGXS pair, and its checker is built
// around what the extender ACTUALLY guarantees.
//
// THE GUARANTEE, stated precisely:
//   FRAMES are preserved exactly. Every column between /S/ and /T/,
//   in order, unmodified, none added and none removed.
//   THE STREAM IS NOT. /R/ columns are inserted and deleted outside
//   frames whenever the two ends' clocks differ -- which is always.
//
// So this checker compares FRAMES and counts IDLE ADJUSTMENTS. A test
// that compared the two streams bit for bit would fail on every
// conforming link, which is Section 14's rejected property in the form
// of a testbench rather than an assertion.
module xgxs_loopback_selftest
  import xaui_pkg::*;
#(
  parameter int unsigned CNT_W = 20,
  parameter int unsigned PATTERN_FRAMES = 16'd1000
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic start,
 
  // What was sent into the near XGXS.
  input  logic         tx_col_valid,
  input  logic [31:0]  tx_col_data,
  input  logic         tx_in_frame,
 
  // What emerged from the far XGXS.
  input  logic         rx_col_valid,
  input  logic [31:0]  rx_col_data,
  input  logic         rx_in_frame,
 
  output logic running,
  output logic done,
  output logic passed,
 
  // FRAME comparison -- the real check.
  output logic [CNT_W-1:0] c_frame_columns_sent,
  output logic [CNT_W-1:0] c_frame_columns_received,
  output logic [CNT_W-1:0] c_frame_column_mismatches,
 
  // IDLE accounting -- expected to be non-zero, and reported rather
  // than treated as an error.
  output logic signed [CNT_W-1:0] idle_columns_net,
  output logic                    idle_adjusted,
 
  // The two verdicts, which are deliberately separate.
  output logic frames_preserved,
  output logic stream_modified
);
 
  logic [15:0] frames_q;
  logic [31:0] tx_fifo_q;      // one-entry model of the frame path
  logic        tx_have_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      running <= 1'b0; done <= 1'b0; passed <= 1'b0;
      frames_q <= '0; tx_fifo_q <= '0; tx_have_q <= 1'b0;
      c_frame_columns_sent <= '0; c_frame_columns_received <= '0;
      c_frame_column_mismatches <= '0;
      idle_columns_net <= '0; idle_adjusted <= 1'b0;
      frames_preserved <= 1'b0; stream_modified <= 1'b0;
    end else if (start) begin
      running <= 1'b1; done <= 1'b0; passed <= 1'b0;
      frames_q <= '0; tx_have_q <= 1'b0;
      c_frame_columns_sent <= '0; c_frame_columns_received <= '0;
      c_frame_column_mismatches <= '0;
      idle_columns_net <= '0; idle_adjusted <= 1'b0;
    end else if (running) begin
      idle_adjusted <= 1'b0;
 
      // ---- FRAME columns: compared exactly ----
      if (tx_col_valid && tx_in_frame) begin
        tx_fifo_q <= tx_col_data;
        tx_have_q <= 1'b1;
        if (!(&c_frame_columns_sent))
          c_frame_columns_sent <= c_frame_columns_sent + 1'b1;
      end
 
      if (rx_col_valid && rx_in_frame) begin
        if (!(&c_frame_columns_received))
          c_frame_columns_received <= c_frame_columns_received + 1'b1;
        if (tx_have_q && (rx_col_data != tx_fifo_q)) begin
          // A frame column that changed. THIS is a failure.
          if (!(&c_frame_column_mismatches))
            c_frame_column_mismatches <= c_frame_column_mismatches + 1'b1;
        end
        tx_have_q <= 1'b0;
      end
 
      // ---- IDLE columns: counted, never compared ----
      if (tx_col_valid && !tx_in_frame) idle_columns_net <= idle_columns_net + 1;
      if (rx_col_valid && !rx_in_frame) begin
        idle_columns_net <= idle_columns_net - 1;
        // A net difference means the compensator acted, which is the
        // mechanism working. Recorded as an EVENT, not an error.
        if (idle_columns_net != 0) idle_adjusted <= 1'b1;
      end
 
      if (rx_col_valid && rx_in_frame && !tx_in_frame) begin
        frames_q <= frames_q + 16'd1;
      end
 
      if (frames_q == PATTERN_FRAMES) begin
        running <= 1'b0;
        done    <= 1'b1;
 
        // THE TWO VERDICTS, and they are independent.
        //   frames_preserved -- the guarantee the XGXS makes.
        //   stream_modified  -- the mechanism it needed to make it.
        // A healthy link has the first TRUE and the second ALSO TRUE.
        frames_preserved <= (c_frame_column_mismatches == '0) &&
                            (c_frame_columns_sent == c_frame_columns_received);
        stream_modified  <= (idle_columns_net != 0);
        passed           <= (c_frame_column_mismatches == '0) &&
                            (c_frame_columns_sent == c_frame_columns_received);
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that frames_preserved and stream_modified are both expected to be true on a healthy link, and reporting them as two independent verdicts is the entire design. The XGXS guarantees the first and needs the second in order to deliver it — a compensator that never modified the stream would eventually overflow, and the overflow would land inside a frame.

Deliberately simplified: the frame comparison uses a one-entry model of the path. A real self-test compares against a much deeper reference or uses a known pattern with an embedded sequence number, because the XGXS's latency is many columns.

Production implication: passed is computed only from the frame comparison, and that is deliberate. A self-test whose pass criterion included stream equality would fail on every conforming link — which is not a hypothetical, because it is the natural way to write a loopback checker: send a sequence, capture a sequence, compare. The correct checker compares what the layer promised and counts what it changed, and the counting is diagnostic rather than a verdict.

12. RTL 7 — Conformance Across Four Lanes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Checks the XAUI rules that only exist because there are four lanes.
//
// THE FOUR-LANE RULES, none of which has a single-lane analogue:
//   1. /A/ is transmitted on ALL FOUR lanes simultaneously. An /A/ on
//      one lane and not the others is not an align column; it is a lane
//      that has lost sync and is decoding rubbish that happens to look
//      like K28.3.
//   2. Idle columns are the SAME character on all four lanes. A column
//      with /K/ on two lanes and /R/ on two is not a legal column.
//   3. /S/ appears on lane 0 only (Chapter 9.4 §4), and after
//      deskew that rule is checkable again.
//   4. Every lane must hold code-group sync. Three synced lanes is not
//      75% of a link -- an XGMII column needs all four bytes.
module xaui_conformance_monitor
  import xaui_pkg::*;
#(
  parameter int unsigned CNT_W = 20
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic                  deskew_done,
  input  logic [LANES-1:0]      lane_synced,
  input  logic [LANES-1:0][7:0] lane_octet,
  input  logic [LANES-1:0]      lane_is_k,
  input  logic                  col_valid,
  input  logic                  in_frame,
 
  output logic partial_align_column,
  output logic mixed_idle_column,
  output logic start_off_lane0,
  output logic lane_sync_incomplete,
 
  output logic [CNT_W-1:0] c_partial_align,
  output logic [CNT_W-1:0] c_mixed_idle,
  output logic [CNT_W-1:0] c_start_off_lane0,
  output logic [CNT_W-1:0] c_sync_incomplete,
 
  output logic [LANES-1:0] ever_lost_sync,
  output logic       first_violation_valid,
  output logic [1:0] first_violation_kind,
  output logic       ever_violated
);
 
  logic a_any_c, a_all_c;
  logic idle_any_c, idle_same_c;
  logic any_c;
  logic [1:0] kind_c;
 
  always_comb begin
    a_any_c = 1'b0;
    a_all_c = 1'b1;
    for (int i = 0; i < LANES; i = i + 1) begin
      if (lane_is_k[i] && (lane_octet[i] == K28_3)) a_any_c = 1'b1;
      else                                          a_all_c = 1'b0;
    end
 
    // A legal idle column carries the SAME control character on all
    // four lanes. Mixed characters mean the lanes are not aligned to
    // the same column, whatever the deskew stage believes.
    idle_any_c  = 1'b0;
    idle_same_c = 1'b1;
    for (int i = 0; i < LANES; i = i + 1) begin
      if (lane_is_k[i]) idle_any_c = 1'b1;
      if (lane_is_k[i] && (lane_octet[i] != lane_octet[0])) idle_same_c = 1'b0;
    end
 
    any_c  = 1'b0;
    kind_c = 2'd0;
    if      (partial_align_column)  begin any_c = 1'b1; kind_c = 2'd0; end
    else if (mixed_idle_column)     begin any_c = 1'b1; kind_c = 2'd1; end
    else if (start_off_lane0)       begin any_c = 1'b1; kind_c = 2'd2; end
    else if (lane_sync_incomplete)  begin any_c = 1'b1; kind_c = 2'd3; end
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      partial_align_column <= 1'b0; mixed_idle_column <= 1'b0;
      start_off_lane0 <= 1'b0; lane_sync_incomplete <= 1'b0;
      c_partial_align <= '0; c_mixed_idle <= '0;
      c_start_off_lane0 <= '0; c_sync_incomplete <= '0;
      ever_lost_sync <= '0;
      first_violation_valid <= 1'b0; first_violation_kind <= 2'd0;
      ever_violated <= 1'b0;
    end else if (clear) begin
      c_partial_align <= '0; c_mixed_idle <= '0;
      c_start_off_lane0 <= '0; c_sync_incomplete <= '0;
      first_violation_valid <= 1'b0;
      // ever_lost_sync and ever_violated survive.
    end else begin
      partial_align_column <= 1'b0;
      mixed_idle_column    <= 1'b0;
      start_off_lane0      <= 1'b0;
      lane_sync_incomplete <= 1'b0;
 
      for (int i = 0; i < LANES; i = i + 1)
        if (!lane_synced[i]) ever_lost_sync[i] <= 1'b1;
 
      // Nothing is checked before deskew. Before it, every lane is
      // presenting a code group from a different column, so every rule
      // is violated simultaneously and none of it is information.
      if (deskew_done && col_valid) begin
        if (!(&lane_synced)) begin
          lane_sync_incomplete <= 1'b1;
          if (!(&c_sync_incomplete)) c_sync_incomplete <= c_sync_incomplete + 1'b1;
        end
 
        // /A/ ON SOME LANES BUT NOT ALL. Not a partial align column --
        // there is no such thing. It is a lane decoding rubbish.
        if (a_any_c && !a_all_c) begin
          partial_align_column <= 1'b1;
          if (!(&c_partial_align)) c_partial_align <= c_partial_align + 1'b1;
        end
 
        if (!in_frame && idle_any_c && !idle_same_c) begin
          mixed_idle_column <= 1'b1;
          if (!(&c_mixed_idle)) c_mixed_idle <= c_mixed_idle + 1'b1;
        end
 
        for (int i = 1; i < LANES; i = i + 1) begin
          if (lane_is_k[i] && (lane_octet[i] == K27_7)) begin
            start_off_lane0 <= 1'b1;
            if (!(&c_start_off_lane0))
              c_start_off_lane0 <= c_start_off_lane0 + 1'b1;
          end
        end
      end
 
      if (any_c) begin
        ever_violated <= 1'b1;
        if (!first_violation_valid) begin
          first_violation_valid <= 1'b1;
          first_violation_kind  <= kind_c;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that there is no such thing as a partial align column, and treating one as an alignment event is how a receiver locks onto rubbish. /A/ is transmitted on all four lanes simultaneously; /A/ on one lane and not the others means that lane has lost code-group sync and is decoding a bit-shifted stream that happens to produce K28.3. Acting on it deskews against a phantom reference.

Deliberately simplified: four rules and a two-bit violation kind. Production monitors also check the /A/ interval, the idle sequence's K/R alternation, and the column format for /S/ and /T/.

Production implication: every check is gated on deskew_done, for the same reason Chapter 10.5 §14's are gated on aligned. Before deskew, each lane is presenting a code group from a different column, so mixed idle columns, partial aligns and misplaced starts occur on essentially every column — all of it true, none of it useful, and it buries first_violation_kind under aftermath from a single root cause.

13. What the XGXS Hides, and What It Cannot

The extender pair's whole purpose is that neither end knows it is there. It succeeds at that, with three specific exceptions.

PropertyPreserved across the XGXS pair?
frame content, byte for byteyes, exactly
column order within a frameyes, exactly
the start character's laneyes — lane 0 in, lane 0 out
the number of columns between framesNO/R/ inserted and deleted
the exact idle sequenceNO — same reason
latencyNO — the XGXS adds serialisation and deskew delay

The first three are the guarantee and the last three are the price, and the interesting thing is that only one of them is usually thought about.

Latency, computed:

StageCost
8B/10B encode≈ 1 code group = 3.2 ns
serialise≈ 1 code group
propagate, 50 cm of FR-43 ns
deserialise≈ 1 code group
comma alignmentone-time, at link-up
deskew bufferup to DEPTH code groups = ≈ 19 ns
clock compensation bufferup to half its depth
total, one direction≈ 40–60 ns

Against Chapter 8.4's terms at 10 Gb/s:

TermValue
XGXS pair, one direction≈ 50 ns
serialising a 64-octet frame at 10 Gb/s64 × 8 ÷ 10 G = 51.2 ns
serialising a 1518-octet frame1214 ns
Chapter 9.6's KP4 FEC at 400 Gb/s≈ 100 ns

The XGXS costs roughly the time it takes to serialise one minimum-length frame — which is far from negligible at 10 Gigabit and is the price of not routing 74 signals.

14. Properties Worth Asserting, and One Worth Refusing

The organising question here is one of granularity: at what level does this layer make its promise, and is the property being written at that level or at a finer one?

The XGMII-to-XAUI mapping

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P1. Byte lane n maps to serial lane n. Always, every column, with no
// striping -- because the receiver identifies lanes by their wires.
property p_lane_mapping_fixed;
  @(posedge clk) disable iff (!rst_n)
  lane_valid |-> (lane_octet[2] == $past(xgmii_d[23:16]));
endproperty
a_lane_mapping_fixed: assert property (p_lane_mapping_fixed);
 
// P2. An XGMII control bit becomes a K code group on its own lane.
// The four control wires disappear into the encoding.
property p_control_becomes_k;
  @(posedge clk) disable iff (!rst_n)
  lane_valid |-> (lane_is_k == $past(xgmii_c));
endproperty
a_control_becomes_k: assert property (p_control_becomes_k);
 
// P3. A start character off lane 0 is detected BEFORE serialisation,
// while it is still expressible. Afterwards it is four valid code
// groups nothing downstream can object to.
property p_start_off_lane0_detected;
  @(posedge clk) disable iff (!rst_n)
  (xgmii_valid && xgmii_c[2] && (xgmii_d[23:16] == K27_7))
    |=> start_off_lane0;
endproperty
a_start_off_lane0_detected: assert property (p_start_off_lane0_detected);
 
// P4. Every column produces exactly one code group per lane. No
// column is dropped and none is duplicated in the adapter.
property p_one_code_per_lane_per_column;
  @(posedge clk) disable iff (!rst_n)
  xgmii_valid |=> lane_valid;
endproperty
a_one_code_per_column: assert property (p_one_code_per_lane_per_column);

Per-lane encoding

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P5. Each lane's running disparity is updated from ITS OWN data.
// Sharing one across four produces valid code groups and a drifting
// DC level on three lanes.
property p_disparity_is_per_lane;
  @(posedge clk) disable iff (!rst_n)
  lane_valid |=> (lane_rd[1] == next_rd($past(lane_octet[1]),
                                        $past(lane_is_k[1]),
                                        $past(lane_rd[1])));
endproperty
a_disparity_per_lane: assert property (p_disparity_is_per_lane);
 
// P6. An implausibly long same-polarity run is reported as a hint.
property p_rd_stuck_reported;
  @(posedge clk) disable iff (!rst_n)
  (lane_rd_run[0] == 8'(RD_RUN_ALARM)) |=> lane_rd_stuck[0];
endproperty
a_rd_stuck_reported: assert property (p_rd_stuck_reported);

The idle sequence

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P7. An /A/ column is the SAME character on all four lanes. There is
// no such thing as a partial align column.
property p_align_is_all_lanes;
  @(posedge clk) disable iff (!rst_n)
  (idle_valid && (idle_kind == COL_IDLE_A))
    |-> ((idle_octet[0] == K28_3) && (idle_octet[1] == K28_3) &&
         (idle_octet[2] == K28_3) && (idle_octet[3] == K28_3));
endproperty
a_align_all_lanes: assert property (p_align_is_all_lanes);
 
// P8. /A/ is NEVER marked deletable. It is a timing reference the far
// end measures skew against.
property p_align_not_deletable;
  @(posedge clk) disable iff (!rst_n)
  (idle_kind == COL_IDLE_A) |-> !is_deletable;
endproperty
a_align_not_deletable: assert property (p_align_not_deletable);
 
// P9. Nor is /K/ -- it carries the comma, and alignment recovery needs
// a supply of them.
property p_sync_not_deletable;
  @(posedge clk) disable iff (!rst_n)
  (idle_kind == COL_IDLE_K) |-> !is_deletable;
endproperty
a_sync_not_deletable: assert property (p_sync_not_deletable);
 
// P10. /R/ is the ONLY deletable column. It exists for that purpose.
property p_only_r_deletable;
  @(posedge clk) disable iff (!rst_n)
  is_deletable |-> (idle_kind == COL_IDLE_R);
endproperty
a_only_r_deletable: assert property (p_only_r_deletable);
 
// P11. The /A/ interval is bounded. The far end's deskew depends on
// them arriving regularly.
property p_align_interval_bounded;
  @(posedge clk) disable iff (!rst_n)
  idle_valid |-> (columns_since_a <= 5'(A_INTERVAL + 8));
endproperty
a_align_interval_bounded: assert property (p_align_interval_bounded);

Deskew

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P12. Deskew requires code-group sync on EVERY lane first. Skew is
// measured in code groups, and a lane without sync has no code groups.
property p_deskew_needs_sync;
  @(posedge clk) disable iff (!rst_n)
  deskew_done |-> (&lane_synced);
endproperty
a_deskew_needs_sync: assert property (p_deskew_needs_sync);
 
// P13. And it requires all four /A/ characters. Three is not deskew --
// an XGMII column needs four bytes.
property p_deskew_needs_all_lanes;
  @(posedge clk) disable iff (!rst_n)
  $rose(deskew_done) |-> $past(&seen_q);
endproperty
a_deskew_needs_all_lanes: assert property (p_deskew_needs_all_lanes);
 
// P14. THE ALIASING PROPERTY. No recorded depth ever reaches DEPTH.
// A wrapped FIFO reports a PLAUSIBLE SMALL skew, which is the most
// convincing wrong answer this module can produce.
property p_depth_within_buffer;
  @(posedge clk) disable iff (!rst_n)
  deskew_done |-> (worst_depth < DW'(DEPTH));
endproperty
a_depth_within_buffer: assert property (p_depth_within_buffer);
 
// P15. An excess REFUSES deskew and names the lane.
property p_excess_refuses_and_names;
  @(posedge clk) disable iff (!rst_n)
  skew_exceeded |-> (!deskew_done && (skew_exceeded_lane < 2'(LANES)));
endproperty
a_excess_refuses: assert property (p_excess_refuses_and_names);
 
// P16. Losing sync on any lane invalidates the deskew.
property p_sync_loss_invalidates_deskew;
  @(posedge clk) disable iff (!rst_n)
  !(&lane_synced) |=> !deskew_done;
endproperty
a_sync_loss_invalidates: assert property (p_sync_loss_invalidates_deskew);

Clock compensation — the granularity properties

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P17. THE GUARANTEE. No column inside a frame is ever deleted.
property p_never_delete_in_frame;
  @(posedge clk) disable iff (!rst_n)
  in_frame |-> !delete_column;
endproperty
a_never_delete_in_frame: assert property (p_never_delete_in_frame);
 
// P18. Nor inserted into one.
property p_never_insert_in_frame;
  @(posedge clk) disable iff (!rst_n)
  in_frame |-> !insert_column;
endproperty
a_never_insert_in_frame: assert property (p_never_insert_in_frame);
 
// P19. Only a deletable column is ever deleted.
property p_delete_only_deletable;
  @(posedge clk) disable iff (!rst_n)
  delete_column |-> col_is_deletable;
endproperty
a_delete_only_deletable: assert property (p_delete_only_deletable);
 
// P20. Delete and insert are mutually exclusive.
property p_adjust_exclusive;
  @(posedge clk) disable iff (!rst_n)
  !(delete_column && insert_column);
endproperty
a_adjust_exclusive: assert property (p_adjust_exclusive);
 
// P21. Starvation is REPORTED. The buffer reached a limit with no
// deletable column available, which is the real failure -- as distinct
// from the buffer being full, which is the mechanism working.
property p_starvation_reported;
  @(posedge clk) disable iff (!rst_n)
  (col_valid && !in_frame && (fifo_level == 6'(FIFO_DEPTH - 1)) &&
   !col_is_deletable) |=> compensation_starved;
endproperty
a_starvation_reported: assert property (p_starvation_reported);
 
// P22. FRAMES are preserved across the pair -- the property the layer
// actually promises, at the granularity it promises it.
property p_frames_preserved;
  @(posedge clk) disable iff (!rst_n)
  done |-> (c_frame_column_mismatches == '0);
endproperty
a_frames_preserved: assert property (p_frames_preserved);

15. Verification Scenarios

The adapter

  1. A data column — four bytes to four lanes, lane_is_k all zero.
  2. An idle /K/ columnK28.5 on all four lanes, lane_is_k all one.
  3. A start column/S/ on lane 0, data on lanes 1 to 3, start_off_lane0 low.
  4. A start character on lane 2start_off_lane0 high, and the column is still serialised so downstream behaviour can be observed.
  5. A terminate on each of the four lanes in turn — all legal, all mapped correctly.
  6. xgmii_c set on lanes 0 and 2 only — the control bits map to lane_is_k per lane, not per column.
  7. Column throughput — one output column per input column, no drops, no duplicates.

Per-lane encoding

  1. Four lanes carrying identical bytes — the four running disparities evolve identically, which is correct and looks like a shared variable.
  2. Four lanes carrying different bytes — the four disparities diverge. The scenario that distinguishes a per-lane implementation from a shared one.
  3. A shared-disparity mutation — P5 fires; three lanes' balance is corrected using a fourth lane's data.
  4. A lane fed a pattern that holds disparity at one polarity for 64 code groupslane_rd_stuck[n], the others clear.

The idle sequence

  1. A long idle period — the sequence A K R K R … with an /A/ roughly every 16 columns.
  2. /A/ columnsis_deletable low, always.
  3. /K/ columnsis_deletable low.
  4. /R/ columnsis_deletable high, and they are the only ones.
  5. force_align — an /A/ column is produced immediately regardless of the counter.
  6. A stretched /A/ interval after several compensation eventsa_interval_stretched; legal transiently, a fault if it persists.

Deskew

  1. Zero skew — all four /A/ characters in the same column; depths all zero; immediate deskew_done.
  2. Lane 1 late by 1 code group — depth 1, deskew_done, no excess.
  3. Lane 3 late by DEPTH − 1 — the boundary that must still align.
  4. Lane 3 late by DEPTHskew_exceeded with skew_exceeded_lane = 3, deskew_done low, and no wrap.
  5. Three /A/ characters and the fourth never arrivingdeskew_done never asserts. Three lanes is not 75% of a link.
  6. Lane 2 losing code-group sync after deskewdeskew_done falls; the measurement's unit is gone.
  7. /A/ on lane 0 onlypartial_align_column, and deskew is not attempted against it.
  8. Re-deskew after a link bounce — depths re-measured; ever_skew_exceeded survives a clear.

Clock compensation

  1. Two clocks exactly equalc_deletions and c_insertions both zero, net_adjustment zero.
  2. The transmitter 100 ppm fast — the buffer fills, /R/ columns are deleted, net_adjustment drifts negative, and no frame column is touched.
  3. The transmitter 100 ppm slow/R/ columns inserted, net_adjustment positive.
  4. A high-water event arriving at an /A/ column — the column is forwarded, not deleted; the buffer keeps filling.
  5. The buffer reaching its limit at an /A/ columncompensation_starved, ever_starved sticky. The real failure.
  6. A high-water event during a framenothing is deleted; the in_frame branch is unconditional. P17.
  7. A low-water event during a frame — nothing inserted. P18.
  8. An idle sequence with /R/ removed entirely (a deliberate mutation) — compensation_starved within a few thousand columns under any clock offset.

Loopback and conformance

  1. A 1000-frame loopback with equal clocksframes_preserved true, stream_modified false.
  2. The same with 100 ppm offsetframes_preserved true, stream_modified true, passed true. Both verdicts, and the pass criterion uses only the first.
  3. A single frame column corruptedc_frame_column_mismatches non-zero, passed false.
  4. A mixed idle column — /K/ on two lanes, /R/ on twomixed_idle_column.
  5. Checks before deskew_doneno violations reported at all. The gate.

16. Debugging: Four Lanes, Four Places to Fail

ObservationLikely causeThe distinguishing check
deskew_done never asserts, one lane never syncedthat lane's SerDes, trace or connectorever_lost_sync[n] names it
deskew_done low, skew_exceeded higha lane routed too longskew_exceeded_lane, then measure that trace
deskew done, small measured skew, 100% FCS failuresa wrapped deskew FIFOworst_depth implausibly low; the aliasing signature
partial_align_column risinga lane decoding rubbish that resembles K28.3it has lost sync; check lane_synced
mixed_idle_column risinglanes aligned to different columnsdeskew believed itself; re-check /A/ handling
frames corrupt, control stream legallanes swapped on the boardXAUI identifies lanes by wire; only a known pattern finds it
one lane's DC level driftingshared running disparitylane_rd_stuck[n] on three of four lanes
compensation_starved risingnot enough /R/ in the idle for the clock offsetcheck the idle sequence and the clock tolerance
a frame column lost occasionally, no other errorscompensation acting inside a frameP17 and P18; the in_frame gate
loopback passes bit-exactthe testbench has one clockthe compensator never ran; nothing is verified

Four habits.

First, read lane_synced before anything else on a four-lane interface. Almost every downstream symptom — partial aligns, mixed idle columns, deskew failures — is a consequence of one lane having lost code-group sync, and reporting the consequences buries the cause.

Second, treat a small measured skew with a 100% error rate as aliasing. A wrapped FIFO reports a plausible number, because DEPTH + 1 code groups of delay looks like 1. The most convincing wrong answer this subsystem produces is a reassuring one, which is why P14 asserts that no depth ever reaches DEPTH.

Third, distinguish a full buffer from a starved compensator. A full buffer with an /R/ available is the mechanism working. A full buffer at an /A/ column is compensation with nowhere to act, and the next overflow lands somewhere that matters.

Fourth, distrust a bit-exact loopback result. If the two XGXS instances share a clock — which they do in almost every testbench — the compensator never ran, and the mechanism most likely to be wrong is the one never exercised.

17. Common Misconceptions

"XGMII is an interface you route between chips."

The wrong model: a connector-level interface like MII or RGMII.

What it costs: you cannot explain why XAUI exists, or why the standard defines two interfaces for one boundary.

The corrected model: XGMII is 74 signals32 + 4 + 1 in each direction — at 156.25 MHz DDR, and essentially nobody routes it between packages. It specifies where the reconciliation sublayer ends and the PCS begins, so two vendors' blocks can be described against one boundary; inside a chip, 74 wires on a die is nothing. XAUI exists for when that boundary crosses a package, and it carries the same 10 Gb/s on sixteen wires — a 4.6× reduction — with an extender sublayer at each end so that both sides still see XGMII.

"XAUI stripes data across four lanes."

The wrong model: a round-robin distribution like Chapter 9.5's.

What it costs: you expect lane identification in the data, and cannot explain why a swapped lane breaks a XAUI link and is absorbed at 100 Gigabit.

The corrected model: XGMII byte lane n goes to XAUI serial lane n, always — there is no striping decision at all. A lane is identified by which wire it arrives on, not by anything in the data, which is why /A/ answers when rather than which. 100 Gigabit's alignment markers name their lane and can therefore be reordered; XAUI's cannot, and that simplification is correct at 50 cm and wrong at 10 km.

"The XGXS is transparent, so what goes in comes out."

The wrong model: transparency means bit-exactness.

What it costs: Section 14's rejected property, and — worse — a "fix" that disables clock compensation and corrupts frames.

The corrected model: transparency is stated over frames. Not one column between /S/ and /T/ is ever added or removed. The stream is deliberately modified: /R/ columns are inserted and deleted to absorb the difference between two 156.25 MHz references that are never exactly equal. The stream modification is load-bearing — it is what makes the frame guarantee keepable — and a design that removes it overflows its buffer into a frame.

"Running disparity can be shared across the four lanes."

The wrong model: a column is the natural unit, so one disparity per column.

What it costs: three lanes whose DC balance is corrected using a fourth lane's data.

The corrected model: each XAUI lane is an independent, AC-coupled serial link with its own DC-balance requirement. Four encoders, four running disparities, updated from four different byte streams. A shared variable produces code groups that are individually valid on every lane and a DC level that drifts on three of them — which passes every code-group check and fails AC coupling after a few microseconds.

"Three of four lanes working is most of a link."

The wrong model: graceful degradation across parallel lanes.

What it costs: you misread a 100% error rate as a total failure rather than as one lane's fault.

The corrected model: an XGMII column is four bytes and recovering any column requires all four. One lane out of sync corrupts one byte of every column, so every frame fails its FCS and the error rate is 100% — indistinguishable, at the frame layer, from a completely dead link. The per-lane vectors — lane_synced, lane_depth, lane_rd_stuck, ever_lost_sync — are the only things that can attribute it, and they exist only below the combine.

18. Interview Reasoning

"Why does 10 Gigabit define two interfaces for one boundary?"

Because XGMII's width makes it unroutable and its width is not optional. 32 data + 4 control + 1 clock in each direction is 74 signals at 156.25 MHz DDR, and every one is single-ended and length-matched — Chapter 10.4's delay problem applied to thirty-six lines against one clock. XGMII specifies the functional boundary between the reconciliation sublayer and the PCS, which is exactly what is needed inside a chip where 74 wires cost nothing. XAUI is what crosses a package: four lanes per direction at 3.125 Gbaud with 8B/10B, 4 × 3.125 × 8/10 = 10 Gb/s, on sixteen wires — a 4.6× reduction. The finishing point: XAUI is not a different interface, it is XGMII with an extender sublayer on each end, so the RS and the PCS both still see XGMII and neither knows the serial link is there.

"What are XAUI's three idle characters for?"

Three different alignment problems, all solved inside the idle stream so all three are free. /K/ (K28.5) carries the comma, which is how each lane finds its code-group boundary — Chapter 10.5's problem, four times over. /A/ (K28.3) is transmitted simultaneously on all four lanes, so the difference in its arrival times is the inter-lane skew, measured rather than estimated; it appears roughly every 16 columns. /R/ (K28.0) is the skip column: it carries nothing at all and exists to be deleted and inserted for clock compensation. The strong answer says why /R/ and not the others: deleting an /A/ costs a deskew reference the far end is counting; deleting a /K/ starves comma supply for future re-alignment; /R/ carries no information, which is why more than half the idle sequence is /R/.

"How deep does a XAUI deskew buffer need to be?"

Compute it. One UI at 3.125 Gbaud is 1 ÷ 3.125 GHz = 320 ps; the receiver absorbs about 40 UI, so 40 × 320 = 12.8 ns; a code group is 10 UI = 3.2 ns; therefore 12.8 ÷ 3.2 = 4 code groups, and a 6-deep FIFO per lane covers it with margin. The strong answer contrasts it with Chapter 9.5's 180 ns at 100 Gigabit and explains the ratio: XAUI's lanes are four traces going the same 20 to 50 cm across one board, while 100 Gigabit's may be separate fibres in a building. And the finishing observation: the small budget is what lets XAUI skip lane identification entirely — a lane cannot be more than four code groups late, so its /A/ can never be confused with its next /A/, whereas 20 lanes with 14 blocks of skew need markers that name themselves.

"Would you assert that the XGXS pair is bit-exact?"

No — it is false on every conforming link, and the reason is granularity rather than content. The standard calls the extender transparent, and it is: frames cross unchanged, not one column between /S/ and /T/ added or removed. But the stream does not, because the two ends' 156.25 MHz references are never exactly equal and the XGXS absorbs the difference by inserting and deleting /R/ columnsChapter 4.4's elastic buffering as a code group. So the property asserts a stronger guarantee than the layer owes, and it is false precisely because of the mechanism the layer needs in order to deliver the weaker one. The damage is specific: a team that writes it either concludes the extender is broken or disables compensation to satisfy it, at which point the buffer overflows into a frame and the guarantee that mattered is the one that breaks. Assert the guarantee at its own granularity: nothing inside a frame is ever added or deleted, only /R/ is ever deleted, insert and delete are exclusive, starvation is reported, and frames compare equal end to end. The test: at what granularity does this layer state its promise? A promise about frames says nothing about columns.

19. Understanding Check

Seventy-four against sixteen, and the gap is why there are two.

XGMIIXAUI
per direction32 + 4 + 1 = 374 lanes
both directions74 signals4 × 2 × 2 = 16 wires
clocking10 G ÷ 32 = 312.5 MT/s → 156.25 MHz DDR3.125 Gbaud per lane
encodingnone8B/10B
reduction4.6×

XGMII specifies a functional boundary, not a connector — where the reconciliation sublayer ends and the PCS begins, so two vendors' blocks can be described against one interface. Inside a chip, 74 wires on a die is nothing.

XAUI exists for when the boundary crosses a package, and it is not a different interface: an extender sublayer at each end converts XGMII to XAUI and back, so both sides still see XGMII.

And XAUI's rate closes exactly: 4 lanes × 3.125 Gbaud × 8/10 = 10 Gb/s, with each lane carrying one XGMII byte lane at 312.5 M code groups/s.

20. What's Next

The claim this chapter defended: a layer described as transparent is transparent at exactly one granularity.

XGMII is 74 signals — 32 data, 4 control and a clock in each direction at 156.25 MHz DDR — which specifies a boundary well and crosses a package badly. XAUI carries the same 10 Gb/s on sixteen wires, four lanes per direction at 3.125 Gbaud with 8B/10B, and an extender sublayer at each end makes both sides believe they are still talking XGMII.

What four independent serial lanes cost is everything Chapter 10.5 built, four times over, plus a problem SGMII does not have. Per-lane comma alignment, per-lane running disparity — sharing one across four is a DC-balance failure that passes every code-group check — and deskew, because four lanes traverse four paths and arrive up to 40 × 320 ps = 12.8 ns apart, which is 4 code groups and therefore a 6-deep FIFO.

XAUI solves all three inside its idle sequence. /K/ carries the comma; /A/ is transmitted simultaneously on all four lanes and measures the skew directly; /R/ carries nothing and exists to be deleted — which is why more than half the idle is /R/.

And that last mechanism is the chapter's rejected property. The XGXS inserts and deletes /R/ columns to absorb the two ends' clock difference, so the stream that emerges is not the stream that entered — by design, on every conforming link, always. Its transparency is a guarantee about frames: not one column between /S/ and /T/ is ever touched. Asserting bit-exactness of the stream is a property one granularity too fine, false because of the mechanism that makes the real guarantee keepable — and a design "fixed" to satisfy it overflows into a frame.

Module 10 is complete. Six interfaces, one boundary, and a single question asked six ways: where does the clock come from, and what varies when the link's speed does? MII gave both clocks to the PHY and got a clock that stops. RMII gave the clock to nobody and got a clock nobody drives. GMII split them and got a timing relationship RTL cannot see. RGMII folded the bus and got a delay with no owner. SGMII serialised and got a fixed rate with no per-speed timing at all. XAUI serialised four times over and got deskew.

Chapter 11.1 — Link Discovery: FLP Bursts and Parallel Detection opens the module that owns the other end of the problem.

Every interface in Module 10 assumed a link that already existed at a known speed. Module 11 asks how two devices that have never met agree on one — and its first chapter is about what can be observed before any configuration is agreed at all. Chapter 9.2 §5 gave the FLP burst's structure; 11.1 goes into its timing tolerances, the link-integrity-pulse fallback that keeps a 1990 device working, the parallel-detection window and what it can and cannot determine, and the full ordering from a signal is present to abilities are available.

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.