Skip to content
VLSI Mentor

Ethernet · Module 11

Full Link Bring-Up Sequence

Eight stages from power-on to a legally transmitting MAC, and the ordering is a data dependency. Each stage measures what the next assumes, and a stage run early does not fail — it succeeds against garbage.

Chapter 11.1 established what can be observed before anything is agreed. Chapter 11.2 established what is exchanged and — more importantly — what is not.

This chapter puts the whole path together, and its argument is about ordering.

Bring-up is eight stages, and each one measures something the next one assumes. Discovery measures what kind of partner this is; the ability exchange measures what it can do; resolution measures what they have in common; technology-dependent establishment measures the channelChapter 9.3's echo cancellers converging against a cable nobody has characterised; the interface bring-up measures the board.

Run them out of order and every one of them still completes. They just complete against a measurement that has not been taken.

Which produces the failure this whole track keeps meeting: a link that comes up and does not work.

1. Scope — What This Chapter Owns

This chapter owns the sequence: its stages, their preconditions, their timers, their failures, and the gate at the end.

It does not re-derive what other chapters own. Chapter 11.1 owns FLP bursts, classification and parallel detection; Chapter 11.2 owns the link code word, the priority table and acknowledge; Chapter 9.3 owns master/slave arbitration and canceller training; Chapter 10.4 through Chapter 10.6 own the interface bring-ups this chapter sequences. Chapter 4.5 owns MDC and MDIO, which stages 1 and 2 run over.

Chapter 11.4 owns the failure taxonomy — this chapter names failures per stage and hands the diagnosis over.

The claim this chapter defends: bring-up ordering is a data dependency rather than a convention — each stage measures something the next one assumes — and a design that asserts "the link is up" has written a conjunction whose failure identifies nothing.

2. The Eight Stages

Link bring up proceeds through eight stages in a fixed order because each consumes a measurement the previous one produced. Reset establishes that the device is present. Reading the capability registers establishes what the hardware can do, which is what discovery will advertise. Discovery classifies the partner, which decides whether an ability exchange or a parallel detection follows. The exchange yields the partner's advertisement, which the resolution needs. The resolution selects a technology, which decides which establishment procedure runs. Establishment characterises the channel, including timing and adaptive cancellation. The interface bring up characterises the board between the two chips. And only then may the media access control layer's transmit enable be asserted, which measures nothing and simply asserts that all seven earlier measurements were taken.1 Resetdevice present2 Capabilitieswhat we can do3 Discoverywhat partner is this4–5 Exchange,resolvewhat they can do6 Establishmentthe channel7 Interfacethe board8 TX enablemeasures nothing12
Figure 1 — every arrow is a data dependency: the later stage consumes a measurement the earlier one took.

Each stage's output is the next stage's input, and the dependency is data rather than etiquette.

StageProducesConsumed byNamed failure
1 resetthe device answers2F_NO_DEVICE
2 capabilitiesthe ability set to advertise3F_IMPLAUSIBLE_CAPS
3 discoverythe partner's kind4 or 5F_NO_CLASSIFICATION
4 exchangethe partner's advertisement5F_NO_ADVERTISEMENT
5 resolutionthe technology6F_NO_COMMON_TECH
6 establishmenta characterised channel7F_ESTABLISH_TIMEOUT
7 interfacea characterised board8F_INTERFACE_TIMEOUT
8 enablepermissionthe MAC

Two of these dependencies are worth stating explicitly, because they are the ones designs get wrong.

Stage 6 cannot start until stage 5 has chosen, because the establishment procedure is different for each technology. 1000BASE-T runs Chapter 9.3's master/slave arbitration, then loop timing, then skew measurement, then four independent canceller convergences. 100BASE-TX runs none of that. A design that begins establishment before the resolution has committed is running the wrong procedure.

Stage 7 cannot start until stage 6 has finished, because Chapter 10.4's sampling calibration is measured against a UI that depends on the speed — and the speed is stage 5's output, delivered through stage 6. A tap swept at the wrong speed is calibrated against a window five times too wide.

3. Why the Ordering Is Not a Convenience

Take the strongest possible objection: the stages look independent, so why not run them concurrently and save time?

Because five of the seven consume a measurement, and a measurement that has not been taken still reads as something.

Run earlyWhat it consumesWhat it gets insteadThe result
discovery before capabilitiesthe ability set to advertisea reset default — usually all zeros or all onesadvertises nothing, or advertises abilities the hardware lacks
exchange before discoverythe partner's kindan unclassified partnerdecodes link pulses as bursts, or waits forever for bursts that will never come
resolution before the exchangethe partner's advertisementa stale or zero wordresolves to TECH_NONE, or to whatever the previous link agreed
establishment before resolutionthe technologythe previous technology, or a defaultruns 100BASE-TX's procedure on a gigabit link
interface before establishmentthe speedthe previous speedChapter 10.4's tap calibrated against a 20 ns UI, used on a 4 ns one
enable before any of itseven measurementsseven assumptionsa link that comes up and corrupts everything

Every row completes. Nothing hangs, nothing errors, and every stage reports success — because a stage's success criterion is about its own behaviour and not about the validity of its input.

Which is the general shape worth naming. A sequence of measurements has a data dependency graph, and a stage running early does not fail; it succeeds against garbage. The failure surfaces two stages later, or at the frame layer, or under load a week after commissioning.

And the fifth row is the one this track has met most often. Chapter 10.4 §13 established that a sampling tap calibrated at 100 Mb/s and carried into 1000 Mb/s is a tap chosen when the eye was five times wider. The interface bring-up completed successfully at the old speed; it is the new speed that has no margin.

4. RTL 1 — Reading What the Hardware Can Actually Do

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Stage 2: read the PHY's capability registers and decide whether the
// answer is plausible.
//
// WHY PLAUSIBILITY AND NOT JUST SUCCESS. An MDIO read of an absent
// device returns all ones or all zeros depending on the bus's pull;
// a device mid-reset returns its reset defaults; a different part
// returns a different part's capabilities. ALL THREE ARE SUCCESSFUL
// READS. The transaction completed; the value is wrong.
//
// So this module checks the value against what a real capability set
// looks like:
//   - the PHY identifier must not be all zeros or all ones
//   - a device claiming 1000BASE-T must also claim 100BASE-TX, because
//     no real part supports gigabit and not fast ethernet
//   - a device claiming full duplex at a speed must claim that speed
//   - claiming NOTHING is legal and worth reporting, because it is
//     almost always a read that landed on the wrong device
package link_bringup_pkg;
 
  typedef enum logic [3:0] {
    S_RESET,
    S_CAPABILITIES,
    S_DISCOVERY,
    S_EXCHANGE,
    S_RESOLUTION,
    S_ESTABLISH,
    S_INTERFACE,
    S_ENABLED,
    S_FAILED
  } stage_e;
 
  typedef enum logic [3:0] {
    F_NONE,
    F_NO_DEVICE,
    F_IMPLAUSIBLE_CAPS,
    F_NO_CLASSIFICATION,
    F_NO_ADVERTISEMENT,
    F_NO_COMMON_TECH,
    F_ESTABLISH_TIMEOUT,
    F_INTERFACE_TIMEOUT,
    F_PRECONDITION
  } failure_e;
 
  // Per-stage timeouts, in milliseconds. Each is derived from what the
  // stage is waiting for rather than chosen uniformly:
  //   reset        -- a device answering an MDIO read
  //   capabilities -- two register reads
  //   discovery    -- Chapter 11.1's detection window, which is long
  //   exchange     -- Chapter 11.2's 88..176 ms acknowledge arithmetic
  //   establish    -- canceller convergence, a physical measurement
  //   interface    -- a phase sweep
  localparam int unsigned T_RESET_MS      = 10;
  localparam int unsigned T_CAPS_MS       = 5;
  localparam int unsigned T_DISCOVERY_MS  = 1000;
  localparam int unsigned T_EXCHANGE_MS   = 500;
  localparam int unsigned T_RESOLUTION_MS = 5;
  localparam int unsigned T_ESTABLISH_MS  = 2000;
  localparam int unsigned T_INTERFACE_MS  = 100;
 
endpackage
 
module capability_register_reader
  import link_bringup_pkg::*;
#(
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic start,
 
  // MDIO transaction interface (Chapter 4.5 owns the protocol).
  output logic        mdio_read,
  output logic [4:0]  mdio_reg,
  input  logic        mdio_ack,
  input  logic [15:0] mdio_data,
 
  output logic [15:0] phy_id_hi,
  output logic [15:0] phy_id_lo,
  output logic [7:0]  base_abilities,
  output logic        cap_1000t_hd,
  output logic        cap_1000t_fd,
  output logic        caps_valid,
 
  // The three ways a successful read returns a wrong answer.
  output logic        bus_stuck_high,      // all ones: no device pulling
  output logic        bus_stuck_low,       // all zeros: held in reset
  output logic        implausible_caps,    // a set no real part has
  output logic [2:0]  implausibility_kind,
 
  output logic        read_timeout,
  output logic [CNT_W-1:0] c_reads,
  output logic [CNT_W-1:0] c_implausible,
  output logic             ever_implausible
);
 
  typedef enum logic [2:0] {
    R_IDLE, R_ID_HI, R_ID_LO, R_BASE, R_GIG, R_CHECK
  } rstate_e;
 
  rstate_e     st_q;
  logic [15:0] timer_q;
  logic [15:0] base_q, gig_q;
 
  logic imp_c;
  logic [2:0] kind_c;
 
  always_comb begin
    imp_c  = 1'b0;
    kind_c = 3'd0;
 
    // 1. A device claiming gigabit but not fast ethernet. No real part
    //    exists, and it is what a read landing on the wrong register
    //    offset produces.
    if ((gig_q[9] || gig_q[10]) && !base_abilities[2] && !base_abilities[3]) begin
      imp_c = 1'b1; kind_c = 3'd1;
    end
    // 2. Full duplex claimed at a speed whose half duplex is not.
    //    Every real part that does one does the other.
    else if (base_abilities[1] && !base_abilities[0]) begin
      imp_c = 1'b1; kind_c = 3'd2;
    end
    else if (base_abilities[3] && !base_abilities[2]) begin
      imp_c = 1'b1; kind_c = 3'd3;
    end
    // 3. No technologies at all. Legal, and almost always a read that
    //    landed on a device that is not a PHY.
    else if (base_abilities[4:0] == 5'd0) begin
      imp_c = 1'b1; kind_c = 3'd4;
    end
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st_q <= R_IDLE; timer_q <= 16'd0;
      mdio_read <= 1'b0; mdio_reg <= 5'd0;
      phy_id_hi <= 16'd0; phy_id_lo <= 16'd0;
      base_abilities <= 8'd0; base_q <= 16'd0; gig_q <= 16'd0;
      cap_1000t_hd <= 1'b0; cap_1000t_fd <= 1'b0; caps_valid <= 1'b0;
      bus_stuck_high <= 1'b0; bus_stuck_low <= 1'b0;
      implausible_caps <= 1'b0; implausibility_kind <= 3'd0;
      read_timeout <= 1'b0;
      c_reads <= '0; c_implausible <= '0; ever_implausible <= 1'b0;
    end else begin
      read_timeout <= 1'b0;
 
      if (start) begin
        st_q <= R_ID_HI; timer_q <= 16'd0; caps_valid <= 1'b0;
        bus_stuck_high <= 1'b0; bus_stuck_low <= 1'b0;
        implausible_caps <= 1'b0;
        mdio_read <= 1'b1; mdio_reg <= 5'd2;
      end else begin
        unique case (st_q)
          R_IDLE: mdio_read <= 1'b0;
 
          R_ID_HI: if (mdio_ack) begin
            phy_id_hi <= mdio_data;
            // A bus with no device on it reads all ones or all zeros
            // depending on its termination -- and BOTH are successful
            // transactions returning a wrong answer.
            if (mdio_data == 16'hFFFF) bus_stuck_high <= 1'b1;
            if (mdio_data == 16'h0000) bus_stuck_low  <= 1'b1;
            mdio_reg <= 5'd3;
            st_q     <= R_ID_LO;
            timer_q  <= 16'd0;
            if (!(&c_reads)) c_reads <= c_reads + 1'b1;
          end else if (timer_q == 16'd10000) begin
            read_timeout <= 1'b1; st_q <= R_IDLE; mdio_read <= 1'b0;
          end else timer_q <= timer_q + 16'd1;
 
          R_ID_LO: if (mdio_ack) begin
            phy_id_lo <= mdio_data;
            mdio_reg  <= 5'd4;
            st_q      <= R_BASE;
          end
 
          R_BASE: if (mdio_ack) begin
            base_q         <= mdio_data;
            base_abilities <= mdio_data[12:5];
            mdio_reg       <= 5'd9;
            st_q           <= R_GIG;
          end
 
          R_GIG: if (mdio_ack) begin
            gig_q        <= mdio_data;
            cap_1000t_hd <= mdio_data[8];
            cap_1000t_fd <= mdio_data[9];
            mdio_read    <= 1'b0;
            st_q         <= R_CHECK;
          end
 
          R_CHECK: begin
            // THE PLAUSIBILITY GATE. caps_valid is asserted only when
            // the answer looks like a real part's capabilities -- not
            // merely when the reads completed.
            implausible_caps    <= imp_c;
            implausibility_kind <= kind_c;
            caps_valid          <= !imp_c && !bus_stuck_high && !bus_stuck_low;
 
            if (imp_c) begin
              ever_implausible <= 1'b1;
              if (!(&c_implausible)) c_implausible <= c_implausible + 1'b1;
            end
            st_q <= R_IDLE;
          end
 
          default: st_q <= R_IDLE;
        endcase
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that caps_valid gates on plausibility and not on transaction success, and the distinction is the module. An MDIO read of an absent device completes successfully and returns 0xFFFF or 0x0000 depending on the bus's termination. A device mid-reset completes successfully and returns its reset defaults. A read landing on the wrong register offset completes successfully and returns a different register. All three are successful transactions with wrong answers, and a design that checks only mdio_ack accepts all of them.

Deliberately simplified: four register reads with fixed offsets. A real reader also handles clause 45's indirect addressing for the extended register space and retries a read that returns an implausible value before condemning it.

Production implication: the gigabit-implies-fast-ethernet check catches the specific failure of a read landing one register off. No real part supports 1000BASE-T and not 100BASE-TX — so a capability set claiming the first without the second is a register offset error, not an unusual device — and it is exactly what a software driver written against a different part's register map produces. Advertising that set would negotiate to gigabit and fail to establish, which Chapter 11.2 §4 identified as a configuration error that looks like a cabling fault.

5. RTL 2 — The Supervisor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// The bring-up supervisor: sequences the eight stages, times each one,
// and latches WHICH stage failed.
//
// TWO DESIGN DECISIONS CARRY THE MODULE.
//
// 1. PER-STAGE TIMEOUTS, derived from what each stage waits for rather
//    than chosen uniformly. Discovery waits on a partner that may not
//    exist (1000 ms); the exchange waits on Chapter 11.2's 88..176 ms
//    acknowledge arithmetic (500 ms); establishment waits on adaptive
//    loops converging against a physical channel (2000 ms); the
//    interface waits on a phase sweep (100 ms). A single timeout long
//    enough for establishment makes a missing partner take two seconds
//    to report, and one short enough for discovery aborts every
//    gigabit link.
//
// 2. THE FAILED STAGE IS LATCHED. By the time software reads the
//    status, the state machine is back at S_RESET retrying -- so the
//    stage register must hold the FIRST failure of this attempt, not
//    the current state.
module bringup_supervisor
  import link_bringup_pkg::*;
#(
  parameter int unsigned CLK_MHZ = 25,
  parameter int unsigned MAX_ATTEMPTS = 8,
  parameter int unsigned CNT_W = 20
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic restart,
  input  logic link_signal_present,
 
  // Per-stage completion, each from the block that owns that stage.
  input  logic reset_complete,
  input  logic caps_valid,
  input  logic classification_valid,
  input  logic advertisement_valid,
  input  logic resolution_valid,
  input  logic no_common_technology,
  input  logic establishment_complete,
  input  logic interface_ready,
 
  // Preconditions, from Section 6's matrix checker.
  input  logic precondition_ok,
  input  logic [3:0] precondition_failed_stage,
 
  output stage_e   stage,
  output logic     stage_entered,
  output logic     bringup_complete,
  output logic     bringup_failed,
  output failure_e failure,
 
  // The FIRST failure of this attempt, latched and held until the next
  // restart -- because by the time anybody reads it, `stage` is back
  // at the beginning.
  output stage_e   first_failed_stage,
  output failure_e first_failure,
  output logic     first_failure_valid,
 
  output logic [15:0]      stage_elapsed_ms,
  output logic [3:0]       attempts,
  output logic [CNT_W-1:0] c_attempts,
  output logic [CNT_W-1:0] c_completions,
  output logic [CNT_W-1:0] c_failures,
  output logic             ever_failed
);
 
  localparam int unsigned MS_TICKS = 1000 * CLK_MHZ;
 
  stage_e      st_q;
  logic [31:0] tick_q;
  logic [15:0] ms_q;
  logic [3:0]  attempts_q;
 
  assign stage            = st_q;
  assign stage_elapsed_ms = ms_q;
  assign attempts         = attempts_q;
 
  // The timeout for the CURRENT stage. One function, so a stage's
  // budget lives beside its name rather than in a scattered set of
  // parameters.
  function automatic logic [15:0] stage_timeout_ms (input stage_e s);
    unique case (s)
      S_RESET:        stage_timeout_ms = 16'(T_RESET_MS);
      S_CAPABILITIES: stage_timeout_ms = 16'(T_CAPS_MS);
      S_DISCOVERY:    stage_timeout_ms = 16'(T_DISCOVERY_MS);
      S_EXCHANGE:     stage_timeout_ms = 16'(T_EXCHANGE_MS);
      S_RESOLUTION:   stage_timeout_ms = 16'(T_RESOLUTION_MS);
      S_ESTABLISH:    stage_timeout_ms = 16'(T_ESTABLISH_MS);
      S_INTERFACE:    stage_timeout_ms = 16'(T_INTERFACE_MS);
      default:        stage_timeout_ms = 16'hFFFF;
    endcase
  endfunction
 
  function automatic failure_e failure_for (input stage_e s);
    unique case (s)
      S_RESET:        failure_for = F_NO_DEVICE;
      S_CAPABILITIES: failure_for = F_IMPLAUSIBLE_CAPS;
      S_DISCOVERY:    failure_for = F_NO_CLASSIFICATION;
      S_EXCHANGE:     failure_for = F_NO_ADVERTISEMENT;
      S_RESOLUTION:   failure_for = F_NO_COMMON_TECH;
      S_ESTABLISH:    failure_for = F_ESTABLISH_TIMEOUT;
      S_INTERFACE:    failure_for = F_INTERFACE_TIMEOUT;
      default:        failure_for = F_NONE;
    endcase
  endfunction
 
  task automatic advance (input stage_e next_stage);
    st_q          <= next_stage;
    ms_q          <= 16'd0;
    tick_q        <= '0;
    stage_entered <= 1'b1;
  endtask
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st_q <= S_RESET; tick_q <= '0; ms_q <= 16'd0; attempts_q <= 4'd0;
      stage_entered <= 1'b0; bringup_complete <= 1'b0;
      bringup_failed <= 1'b0; failure <= F_NONE;
      first_failed_stage <= S_RESET; first_failure <= F_NONE;
      first_failure_valid <= 1'b0;
      c_attempts <= '0; c_completions <= '0; c_failures <= '0;
      ever_failed <= 1'b0;
    end else begin
      stage_entered    <= 1'b0;
      bringup_failed   <= 1'b0;
 
      if (tick_q == 32'(MS_TICKS - 1)) begin
        tick_q <= '0;
        if (ms_q != 16'hFFFF) ms_q <= ms_q + 16'd1;
      end else begin
        tick_q <= tick_q + 1'b1;
      end
 
      if (restart) begin
        st_q <= S_RESET; ms_q <= 16'd0; tick_q <= '0;
        bringup_complete <= 1'b0; first_failure_valid <= 1'b0;
        attempts_q <= 4'd0;
        if (!(&c_attempts)) c_attempts <= c_attempts + 1'b1;
 
      // A LINK DROP invalidates everything from discovery onward,
      // because the cable may have changed. Not a restart from stage 7.
      end else if (!link_signal_present && (st_q > S_CAPABILITIES) &&
                   (st_q != S_FAILED)) begin
        advance(S_DISCOVERY);
        bringup_complete <= 1'b0;
 
      // THE PRECONDITION GATE. Section 6's matrix says whether this
      // stage's inputs are valid; a stage may not be entered on
      // measurements that were never taken.
      end else if (!precondition_ok && (st_q != S_RESET) &&
                   (st_q != S_FAILED)) begin
        failure            <= F_PRECONDITION;
        bringup_failed     <= 1'b1;
        ever_failed        <= 1'b1;
        if (!first_failure_valid) begin
          first_failure_valid <= 1'b1;
          first_failed_stage  <= stage_e'(precondition_failed_stage);
          first_failure       <= F_PRECONDITION;
        end
        if (!(&c_failures)) c_failures <= c_failures + 1'b1;
        st_q <= S_FAILED;
 
      end else if ((st_q != S_ENABLED) && (st_q != S_FAILED) &&
                   (ms_q >= stage_timeout_ms(st_q))) begin
        // TIMEOUT, with the stage's own budget rather than a global one.
        failure        <= failure_for(st_q);
        bringup_failed <= 1'b1;
        ever_failed    <= 1'b1;
        if (!first_failure_valid) begin
          first_failure_valid <= 1'b1;
          first_failed_stage  <= st_q;
          first_failure       <= failure_for(st_q);
        end
        if (!(&c_failures)) c_failures <= c_failures + 1'b1;
        st_q <= S_FAILED;
 
      end else begin
        unique case (st_q)
          S_RESET:        if (reset_complete)       advance(S_CAPABILITIES);
          S_CAPABILITIES: if (caps_valid)           advance(S_DISCOVERY);
          S_DISCOVERY:    if (classification_valid) advance(S_EXCHANGE);
          S_EXCHANGE:     if (advertisement_valid)  advance(S_RESOLUTION);
 
          S_RESOLUTION: begin
            if (resolution_valid) begin
              // NO COMMON TECHNOLOGY is a failure and not a resolution.
              // Chapter 11.2 §7 keeps the two distinct precisely so
              // this branch can exist.
              if (no_common_technology) begin
                failure        <= F_NO_COMMON_TECH;
                bringup_failed <= 1'b1;
                ever_failed    <= 1'b1;
                if (!first_failure_valid) begin
                  first_failure_valid <= 1'b1;
                  first_failed_stage  <= S_RESOLUTION;
                  first_failure       <= F_NO_COMMON_TECH;
                end
                if (!(&c_failures)) c_failures <= c_failures + 1'b1;
                st_q <= S_FAILED;
              end else begin
                advance(S_ESTABLISH);
              end
            end
          end
 
          S_ESTABLISH: if (establishment_complete) advance(S_INTERFACE);
          S_INTERFACE: if (interface_ready) begin
            advance(S_ENABLED);
            bringup_complete <= 1'b1;
            if (!(&c_completions)) c_completions <= c_completions + 1'b1;
          end
 
          S_ENABLED: ;   // steady state
 
          S_FAILED: begin
            if (attempts_q != 4'(MAX_ATTEMPTS)) begin
              attempts_q <= attempts_q + 4'd1;
              advance(S_RESET);
            end
          end
 
          default: st_q <= S_RESET;
        endcase
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that each stage needs its own timeout, derived from what it is waiting for, and a uniform one is wrong in both directions. Discovery waits on a partner that may not existChapter 11.1 §13's window is long by design. Establishment waits on adaptive loops converging against a physical channelChapter 9.3 §6's budget is longer still. A single timeout sized for establishment makes an absent partner take two seconds to report; one sized for discovery aborts every gigabit link mid-convergence.

Deliberately simplified: the timers are a millisecond counter and a lookup. Production supervisors keep the budgets in software-writable registers, because the right value for T_ESTABLISH_MS depends on the PHY.

Production implication: the link-drop branch restarts from S_DISCOVERY rather than from S_RESET or S_INTERFACE, and both bounds matter. Restarting from S_RESET re-reads capabilities that cannot have changed and costs the better part of a second. Restarting from S_INTERFACE reuses a channel characterisation that may be invalid, because a link that dropped may have dropped because the cable changed — and Chapter 9.3's canceller coefficients are a measurement of that cable.

6. RTL 3 — The Precondition Matrix

Each bring up stage validates its own behaviour and cannot validate its inputs, because doing so would duplicate the previous stage's work all the way back to reset. So a separate precondition matrix holds, for each stage, the set of measurements that must be valid before it may be entered, together with a freshness marker showing that each was taken during this bring up attempt rather than inherited from a previous one. The matrix refuses entry to a stage whose inputs are stale, and names the stage whose measurement is missing, which converts a link that comes up and does not work into a link that refuses to come up and says why.A stagechecks its own behaviourCannot check inputsthat is recursivePrecondition matrixchecks the orderFreshness markermeasured THIS attemptRefuse and namewhich measurement ismissingOtherwiseup, and broken12
Figure 2 — the stages check themselves; the matrix checks that their inputs were measured this attempt rather than left over from the last.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// The precondition matrix: for each stage, which measurements must be
// valid before it may be entered -- and, crucially, whether each was
// taken THIS attempt rather than inherited from the last one.
//
// WHY FRESHNESS AND NOT JUST VALIDITY. Every measurement in bring-up
// leaves a residue: a resolved technology, a set of canceller
// coefficients, a chosen sampling tap. All of them are still valid
// SIGNALS after a link drop -- they hold their last value. So a stage
// checking "is the technology resolved?" gets YES from the previous
// link-up, and runs its procedure against a measurement of a cable
// that may since have been replaced.
//
// The fix is a per-attempt epoch. Each measurement records the attempt
// number it was taken in; a precondition is satisfied only when the
// measurement is valid AND its epoch is the current one.
module precondition_matrix_checker
  import link_bringup_pkg::*;
#(
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
 
  input  stage_e     stage,
  input  logic       stage_entered,
  input  logic [3:0] attempt_epoch,
 
  // Each measurement, with the epoch it was taken in.
  input  logic       reset_done,      input logic [3:0] reset_epoch,
  input  logic       caps_valid,      input logic [3:0] caps_epoch,
  input  logic       class_valid,     input logic [3:0] class_epoch,
  input  logic       advert_valid,    input logic [3:0] advert_epoch,
  input  logic       resolution_valid,input logic [3:0] resolution_epoch,
  input  logic       establish_valid, input logic [3:0] establish_epoch,
  input  logic       interface_valid, input logic [3:0] interface_epoch,
 
  output logic       precondition_ok,
  output logic [3:0] precondition_failed_stage,
  output logic [6:0] required_mask,
  output logic [6:0] satisfied_mask,
  output logic [6:0] stale_mask,
 
  // A measurement that is VALID but from a previous attempt. The
  // failure this module exists for, and the one a validity-only check
  // cannot see.
  output logic       stale_measurement_used,
  output logic [CNT_W-1:0] c_precondition_failures,
  output logic [CNT_W-1:0] c_stale_blocked,
  output logic             ever_stale_blocked
);
 
  // Bit n of a mask corresponds to measurement n:
  //   0 reset  1 caps  2 class  3 advert  4 resolution
  //   5 establish  6 interface
  logic [6:0] valid_c, fresh_c, req_c;
 
  always_comb begin
    valid_c = {interface_valid, establish_valid, resolution_valid,
               advert_valid, class_valid, caps_valid, reset_done};
 
    // FRESHNESS. A measurement counts only if it was taken during THIS
    // attempt. Without this, a stale coefficient set from the previous
    // link satisfies a precondition perfectly.
    fresh_c[0] = (reset_epoch      == attempt_epoch);
    fresh_c[1] = (caps_epoch       == attempt_epoch);
    fresh_c[2] = (class_epoch      == attempt_epoch);
    fresh_c[3] = (advert_epoch     == attempt_epoch);
    fresh_c[4] = (resolution_epoch == attempt_epoch);
    fresh_c[5] = (establish_epoch  == attempt_epoch);
    fresh_c[6] = (interface_epoch  == attempt_epoch);
 
    // THE MATRIX. Each stage's required measurements, cumulative --
    // because a stage depends transitively on everything before it.
    unique case (stage)
      S_RESET:        req_c = 7'b0000000;
      S_CAPABILITIES: req_c = 7'b0000001;
      S_DISCOVERY:    req_c = 7'b0000011;
      S_EXCHANGE:     req_c = 7'b0000111;
      S_RESOLUTION:   req_c = 7'b0001111;
      S_ESTABLISH:    req_c = 7'b0011111;
      S_INTERFACE:    req_c = 7'b0111111;
      S_ENABLED:      req_c = 7'b1111111;
      default:        req_c = 7'b0000000;
    endcase
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      precondition_ok <= 1'b1; precondition_failed_stage <= 4'd0;
      required_mask <= 7'd0; satisfied_mask <= 7'd0; stale_mask <= 7'd0;
      stale_measurement_used <= 1'b0;
      c_precondition_failures <= '0; c_stale_blocked <= '0;
      ever_stale_blocked <= 1'b0;
    end else begin
      stale_measurement_used <= 1'b0;
 
      required_mask  <= req_c;
      satisfied_mask <= valid_c & fresh_c;
      // VALID BUT NOT FRESH: the residue of a previous attempt.
      stale_mask     <= req_c & valid_c & ~fresh_c;
 
      precondition_ok <= ((req_c & valid_c & fresh_c) == req_c);
 
      if (stage_entered && ((req_c & valid_c & fresh_c) != req_c)) begin
        if (!(&c_precondition_failures))
          c_precondition_failures <= c_precondition_failures + 1'b1;
 
        // Name the FIRST missing measurement -- which is the stage that
        // should have produced it, and therefore the stage to restart
        // from rather than starting over.
        precondition_failed_stage <= 4'd0;
        for (int i = 6; i >= 0; i = i - 1)
          if (req_c[i] && !(valid_c[i] && fresh_c[i]))
            precondition_failed_stage <= 4'(i);
 
        // And distinguish MISSING from STALE. A measurement that was
        // never taken is a stage that has not run; one that is valid
        // but old is a stage whose result is being reused illegally,
        // and the second is far harder to see.
        if ((req_c & valid_c & ~fresh_c) != 7'd0) begin
          stale_measurement_used <= 1'b1;
          ever_stale_blocked     <= 1'b1;
          if (!(&c_stale_blocked)) c_stale_blocked <= c_stale_blocked + 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that validity is not enough and freshness is the real precondition. Every measurement in bring-up leaves a residue — a resolved technology, a set of canceller coefficients, a chosen sampling tap — and all of them remain valid signals after a link drops, because registers hold their last value. So a stage asking "is the technology resolved?" gets yes from the previous link-up and runs its procedure against a measurement of a cable that may since have been unplugged.

Deliberately simplified: a four-bit epoch and a seven-bit measurement space. Production supervisors use a wider epoch and often a per-measurement timestamp, so that a partially stale set can be diagnosed rather than merely rejected.

Production implication: stale_measurement_used separates two failures that a validity-only check reports identically. A measurement that was never taken means a stage has not run — visible, and usually a sequencing bug. A measurement that is valid but from a previous attempt means a stage's result is being reused illegally — invisible, because every signal reads correct, and it is exactly what produces a link that comes up on a new cable using the old cable's canceller coefficients.

7. The Matrix, Enumerated

Write the dependencies out and two things become visible: they are cumulative, and one of them is not.

StageRequiresNew requirement
1 reset
2 capabilitiesresetreset
3 discoveryreset, capscapabilities
4 exchange+ classificationclassification
5 resolution+ advertisementadvertisement
6 establishment+ resolutionresolution
7 interface+ establishmentestablishment
8 enable+ interfaceinterface

The requirements are cumulative because the dependencies are transitive. Stage 7 needs establishment; establishment needed a resolution; the resolution needed an advertisement. A stage cannot depend on stage n without depending on everything stage n depended on, so the mask is a prefix and not a set.

And the exception is stage 4. Discovery may conclude that the partner does not negotiateChapter 11.1 §8's parallel detection — in which case stage 4 is skipped entirely and stage 5 resolves from a detected speed and an assumed duplex.

PathStages runWhat stage 5 receives
negotiating partner1 2 3 4 5 6 7 8an advertisement, with duplex agreed
non-negotiating partner1 2 3 — 5 6 7 8a detection, with duplex assumed

Which is why the matrix's bit 3 is conditional and every other bit is not — and why a design that hard-codes the eight-stage sequence cannot bring up a link against a 10BASE-T device from 1993.

8. RTL 4 — Technology-Dependent Establishment

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Stage 6: run the establishment procedure for the RESOLVED technology.
//
// THE POINT OF THE MODULE is that there is no single procedure. Each
// technology establishes differently, and the differences are not
// parameters -- they are different sequences of different measurements:
//
//   10BASE-T    -- nothing. Link integrity pulses are the whole of it.
//   100BASE-TX  -- descrambler lock and idle detection.
//   1000BASE-T  -- Chapter 9.3's full sequence: master/slave role,
//        loop timing, pair skew measurement, and FOUR independent
//        canceller convergences, in that order, each depending on the
//        previous.
//
// So a design that begins establishment before stage 5 has committed is
// not merely early -- it is running the WRONG PROCEDURE, and the wrong
// procedure completes successfully.
module tech_establishment_sequencer
  import link_bringup_pkg::*;
#(
  parameter int unsigned CLK_MHZ = 25,
  parameter int unsigned CNT_W   = 20
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic       start,
  input  logic [3:0] resolved_tech,        // from Chapter 11.2's resolver
  input  logic [3:0] resolution_epoch,
  input  logic [3:0] attempt_epoch,
 
  // 1000BASE-T sub-stages (Chapter 9.3 owns each of them).
  input  logic role_resolved,
  input  logic role_conflict,
  input  logic timing_locked,
  input  logic skew_measured,
  input  logic skew_out_of_range,
  input  logic [3:0] canceller_converged,
  input  logic [3:0] canceller_failed,
 
  // 100BASE-TX sub-stages.
  input  logic descrambler_locked,
  input  logic idle_detected,
 
  // 10BASE-T.
  input  logic link_pulses_seen,
 
  output logic       establishment_complete,
  output logic [3:0] establish_epoch,
  output logic [2:0] sub_stage,
  output logic       establishment_failed,
  output logic [2:0] failed_sub_stage,
 
  // The resolved technology changed while establishment was running.
  // Reported and the sequence abandoned -- continuing would finish a
  // procedure for a technology that is no longer selected.
  output logic       tech_changed_mid_establish,
  // Establishment was started against a resolution from a PREVIOUS
  // attempt. The failure Section 6's freshness check exists for,
  // caught here too because this is where the damage would be done.
  output logic       stale_resolution_refused,
 
  output logic [15:0]      establish_ms,
  output logic [15:0]      worst_establish_ms,
  output logic [CNT_W-1:0] c_establishments,
  output logic [CNT_W-1:0] c_failures,
  output logic             ever_stale_refused
);
 
  localparam int unsigned MS_TICKS = 1000 * CLK_MHZ;
 
  // Sub-stages, shared across technologies; which ones are USED depends
  // on the technology.
  typedef enum logic [2:0] {
    E_IDLE, E_ROLE, E_TIMING, E_SKEW, E_CANCEL, E_SCRAMBLER, E_PULSES, E_DONE
  } estate_e;
 
  estate_e     st_q;
  logic [31:0] tick_q;
  logic [15:0] ms_q;
  logic [3:0]  tech_q;
 
  assign sub_stage    = st_q;
  assign establish_ms = ms_q;
 
  // Technology encodings, matching Chapter 11.2's tech_e ordering.
  localparam logic [3:0] T_10T_HD    = 4'd1;
  localparam logic [3:0] T_10T_FD    = 4'd2;
  localparam logic [3:0] T_100TX_HD  = 4'd3;
  localparam logic [3:0] T_100T4     = 4'd4;
  localparam logic [3:0] T_100TX_FD  = 4'd5;
  localparam logic [3:0] T_1000T_HD  = 4'd6;
  localparam logic [3:0] T_1000T_FD  = 4'd7;
 
  wire is_gigabit = (resolved_tech == T_1000T_HD) || (resolved_tech == T_1000T_FD);
  wire is_100tx   = (resolved_tech == T_100TX_HD) || (resolved_tech == T_100TX_FD);
  wire is_10t     = (resolved_tech == T_10T_HD)   || (resolved_tech == T_10T_FD);
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st_q <= E_IDLE; tick_q <= '0; ms_q <= 16'd0; tech_q <= 4'd0;
      establishment_complete <= 1'b0; establish_epoch <= 4'd0;
      establishment_failed <= 1'b0; failed_sub_stage <= 3'd0;
      tech_changed_mid_establish <= 1'b0;
      stale_resolution_refused <= 1'b0;
      worst_establish_ms <= 16'd0;
      c_establishments <= '0; c_failures <= '0;
      ever_stale_refused <= 1'b0;
    end else begin
      establishment_failed       <= 1'b0;
      tech_changed_mid_establish <= 1'b0;
      stale_resolution_refused   <= 1'b0;
 
      if (tick_q == 32'(MS_TICKS - 1)) begin
        tick_q <= '0;
        if (ms_q != 16'hFFFF) ms_q <= ms_q + 16'd1;
      end else begin
        tick_q <= tick_q + 1'b1;
      end
 
      if (start) begin
        // REFUSE A STALE RESOLUTION. Section 6's matrix should have
        // caught it; checking again here is deliberate, because this is
        // the module that would DO the damage -- it would converge four
        // cancellers against a cable that may have been replaced.
        if (resolution_epoch != attempt_epoch) begin
          stale_resolution_refused <= 1'b1;
          ever_stale_refused       <= 1'b1;
          establishment_failed     <= 1'b1;
          st_q                     <= E_IDLE;
        end else begin
          tech_q                 <= resolved_tech;
          ms_q                   <= 16'd0;
          tick_q                 <= '0;
          establishment_complete <= 1'b0;
          // THE BRANCH. Not a parameter -- a different sequence.
          if      (is_gigabit) st_q <= E_ROLE;
          else if (is_100tx)   st_q <= E_SCRAMBLER;
          else if (is_10t)     st_q <= E_PULSES;
          else                 st_q <= E_PULSES;   // T4 and others
        end
      end else if (st_q != E_IDLE) begin
 
        // The resolution changing under us means stage 5 re-ran, and
        // finishing this procedure would establish a technology that is
        // no longer selected.
        if (resolved_tech != tech_q) begin
          tech_changed_mid_establish <= 1'b1;
          st_q                       <= E_IDLE;
          if (!(&c_failures)) c_failures <= c_failures + 1'b1;
        end else begin
          unique case (st_q)
            // ---- 1000BASE-T: Chapter 9.3's ordered sequence ----
            E_ROLE: if (role_conflict) begin
              establishment_failed <= 1'b1;
              failed_sub_stage     <= 3'(E_ROLE);
              st_q                 <= E_IDLE;
              if (!(&c_failures)) c_failures <= c_failures + 1'b1;
            end else if (role_resolved) begin
              st_q <= E_TIMING;
            end
 
            E_TIMING: if (timing_locked) st_q <= E_SKEW;
 
            E_SKEW: if (skew_measured && skew_out_of_range) begin
              establishment_failed <= 1'b1;
              failed_sub_stage     <= 3'(E_SKEW);
              st_q                 <= E_IDLE;
              if (!(&c_failures)) c_failures <= c_failures + 1'b1;
            end else if (skew_measured) begin
              st_q <= E_CANCEL;
            end
 
            E_CANCEL: if (|canceller_failed) begin
              establishment_failed <= 1'b1;
              failed_sub_stage     <= 3'(E_CANCEL);
              st_q                 <= E_IDLE;
              if (!(&c_failures)) c_failures <= c_failures + 1'b1;
            end else if (&canceller_converged) begin
              // ALL FOUR. Chapter 9.3 §9 established that three is not
              // 75% of a link -- 8B1Q4 spreads every octet across all
              // four pairs.
              st_q <= E_DONE;
            end
 
            // ---- 100BASE-TX ----
            E_SCRAMBLER: if (descrambler_locked && idle_detected) st_q <= E_DONE;
 
            // ---- 10BASE-T ----
            E_PULSES: if (link_pulses_seen) st_q <= E_DONE;
 
            E_DONE: begin
              establishment_complete <= 1'b1;
              establish_epoch        <= attempt_epoch;
              if (ms_q > worst_establish_ms) worst_establish_ms <= ms_q;
              if (!(&c_establishments)) c_establishments <= c_establishments + 1'b1;
              st_q <= E_IDLE;
            end
 
            default: st_q <= E_IDLE;
          endcase
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that establishment is a different sequence per technology and not a parameterised one. 10BASE-T establishes by seeing link pulses. 100BASE-TX establishes by locking a descrambler and detecting idle. 1000BASE-T runs Chapter 9.3's ordered chain — role, then timing, then skew, then four cancellers — each depending on the one before, and there is no way to express those three as one procedure with a speed input.

Deliberately simplified: the sub-stages take completion signals from the blocks that own them. Each of role_resolved, timing_locked, skew_measured and canceller_converged is a full module in Chapter 9.3.

Production implication: stale_resolution_refused duplicates Section 6's freshness check deliberately, because this is the module that would do the damage. Establishing against a stale resolution means converging four adaptive cancellers against a channel measurement of a cable that may have been unplugged — and the cancellers will converge, on whatever cable is now there, to coefficients selected for a technology nobody chose this time. A check at the point of damage costs a comparator and catches the case where the supervisor's own sequencing has a bug.

9. RTL 5 — The Gate That Refuses

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Stage 8: the transmit enable gate.
//
// THIS MODULE MEASURES NOTHING. Every other stage takes a measurement;
// this one asserts that all seven were taken, this attempt, and grants
// permission. It is the single point at which the conjunction is
// evaluated -- which is why Section 15's rejected property is about
// asserting the conjunction rather than its terms.
//
// AND IT REFUSES BY DEFAULT. tx_permitted resets LOW and is raised only
// when every precondition holds. A gate that defaults open is not a
// gate; it is a signal that happens to go low sometimes.
//
// THE SEVEN CONDITIONS, each with its own definition of success:
//   1 the device answered           5 a technology was resolved
//   2 capabilities were plausible   6 the channel was established
//   3 the partner was classified    7 the interface is ready
//   4 abilities were exchanged OR parallel detection completed
module transmit_enable_gate
  import link_bringup_pkg::*;
#(
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic [3:0] attempt_epoch,
 
  input  logic reset_done,       input logic [3:0] reset_epoch,
  input  logic caps_valid,       input logic [3:0] caps_epoch,
  input  logic class_valid,      input logic [3:0] class_epoch,
  input  logic advert_valid,     input logic [3:0] advert_epoch,
  input  logic detection_valid,  input logic [3:0] detection_epoch,
  input  logic resolution_valid, input logic [3:0] resolution_epoch,
  input  logic establish_valid,  input logic [3:0] establish_epoch,
  input  logic interface_valid,  input logic [3:0] interface_epoch,
 
  input  logic link_signal_present,
  // Provenance carried forward from Chapter 11.1 §8. Not a gate --
  // a link with an assumed duplex is legal -- but it must reach here,
  // because this is the last point at which anybody could act on it.
  input  logic duplex_is_assumed,
 
  output logic tx_permitted,
  output logic tx_permitted_rose,
 
  // Which condition is blocking. One signal, and it turns "the link is
  // not up" into a named stage.
  output logic [3:0] blocking_condition,
  output logic [7:0] condition_mask,
 
  // TRUE when permission was granted on a link whose duplex was never
  // agreed. Published, not blocked.
  output logic granted_with_assumed_duplex,
 
  output logic [CNT_W-1:0] c_grants,
  output logic [CNT_W-1:0] c_revocations,
  output logic [15:0]      time_permitted_ms,
  output logic             ever_assumed_duplex_grant
);
 
  logic [7:0] cond_c;
  logic       all_c;
  logic       prev_q;
 
  always_comb begin
    // Each condition is VALID and FRESH. The epoch comparison is what
    // stops a previous attempt's residue from satisfying a term.
    cond_c[0] = reset_done       && (reset_epoch      == attempt_epoch);
    cond_c[1] = caps_valid       && (caps_epoch       == attempt_epoch);
    cond_c[2] = class_valid      && (class_epoch      == attempt_epoch);
    // EITHER an exchange OR a parallel detection -- Section 7's one
    // conditional term, and the only place the sequence branches.
    cond_c[3] = (advert_valid    && (advert_epoch     == attempt_epoch)) ||
                (detection_valid && (detection_epoch  == attempt_epoch));
    cond_c[4] = resolution_valid && (resolution_epoch == attempt_epoch);
    cond_c[5] = establish_valid  && (establish_epoch  == attempt_epoch);
    cond_c[6] = interface_valid  && (interface_epoch  == attempt_epoch);
    cond_c[7] = link_signal_present;
 
    all_c = &cond_c;
  end
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      // REFUSED BY DEFAULT. A gate that resets open permits
      // transmission before anything has been measured.
      tx_permitted <= 1'b0; tx_permitted_rose <= 1'b0;
      blocking_condition <= 4'd0; condition_mask <= 8'd0;
      granted_with_assumed_duplex <= 1'b0;
      c_grants <= '0; c_revocations <= '0; time_permitted_ms <= 16'd0;
      ever_assumed_duplex_grant <= 1'b0; prev_q <= 1'b0;
    end else begin
      tx_permitted_rose <= 1'b0;
      condition_mask    <= cond_c;
      prev_q            <= all_c;
 
      // NAME THE BLOCKER. The lowest unsatisfied condition is the stage
      // that has not completed, which turns "the link is not up" into
      // an instruction.
      blocking_condition <= 4'd8;
      for (int i = 7; i >= 0; i = i - 1)
        if (!cond_c[i]) blocking_condition <= 4'(i);
 
      if (all_c && !prev_q) begin
        tx_permitted      <= 1'b1;
        tx_permitted_rose <= 1'b1;
        time_permitted_ms <= 16'd0;
        if (!(&c_grants)) c_grants <= c_grants + 1'b1;
 
        // PUBLISHED, NOT BLOCKED. A link whose duplex was assumed is
        // legal and works until load arrives -- so permission is
        // granted and the provenance is reported, which is the last
        // moment anybody can act on it.
        granted_with_assumed_duplex <= duplex_is_assumed;
        if (duplex_is_assumed) ever_assumed_duplex_grant <= 1'b1;
 
      end else if (!all_c && prev_q) begin
        tx_permitted <= 1'b0;
        if (!(&c_revocations)) c_revocations <= c_revocations + 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the gate refuses by default and grants on a conjunction, and blocking_condition is what makes the conjunction debuggable. A design with a single link_up signal reports one bit. This one reports eight bits and an index — so the link is not up becomes condition 5 is unsatisfied, which is the resolution, which means stage 5 has not completed — and that is an instruction rather than an observation.

Deliberately simplified: eight conditions and one branch. A real gate also considers administrative state, spanning-tree port state and any MAC-level configuration that must be applied before traffic may flow.

Production implication: granted_with_assumed_duplex publishes rather than blocks, and the choice is deliberate. A link whose duplex was assumed is legal and worksChapter 11.1 §9 established why the assumption is made — so refusing to transmit would break interoperability with every device that predates autonegotiation. But this is the last moment at which the fact is available before it becomes Chapter 9.2's mismatch under load, and a bit that costs nothing is the difference between an operator seeing it at bring-up and seeing it in an incident report.

10. The Moment TX_EN May Be Asserted

Stage 8 is unlike the other seven and it is worth being explicit about how.

Stages 1–7Stage 8
takes a measurementyesno
can time outyesno — it waits indefinitely
can failyes, with a named failureno — it grants or does not
what it producesa facta permission
what it checksits own behavioureverything else's

Which is why the gate is the right place for the conjunction and the wrong place for a timer. A stage that has not completed is that stage's timeout, not the gate's — and a gate with its own timeout would report F_GATE_TIMEOUT, which names nothing.

And the ordering has one more consequence worth stating. tx_permitted is revocable. If any condition falls — the link signal disappears, the interface loses alignment, a canceller diverges — the gate drops permission immediately, without waiting for the supervisor to notice and restart.

That immediacy matters more than it looks. Chapter 9.3 §6's canceller can diverge in microseconds; the supervisor's stage timer is in milliseconds. A MAC transmitting into a link whose cancellers have diverged is putting frames on a wire that cannot carry them, and the gate dropping in one cycle rather than in one timeout is the difference between losing a frame and losing a hundred.

11. RTL 6 — Timing Every Stage

Bring up duration is dominated by whichever stage waited longest, and the total alone cannot say which. A bring up that takes fourteen hundred milliseconds may have spent almost all of it in discovery, which means the partner does not negotiate and parallel detection ran its full window, or almost all of it in establishment, which means adaptive cancellation converged slowly against a marginal cable. Those are completely different findings with completely different remedies, and they produce the same total. Recording each stage's elapsed time separately, along with the worst ever seen for each, turns one number into a breakdown that names the stage responsible and shows how close each stage came to its own timeout.Total: 1400 msone numberMostly discoverypartner does not negotiateMostly establishmentmarginal cablePer-stage breakdowneight numbersCheck the partnerone remedyCheck the cablea different one12
Figure 3 — a bring-up that always takes 1.4 seconds and one that always takes 180 ms both succeed, and only the per-stage breakdown says which stage owns the difference.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Records how long each stage took, and the worst ever seen for each.
//
// WHY PER STAGE AND NOT A TOTAL. Two bring-ups both taking 1400 ms
// can have spent that time in completely different places:
//   almost all in DISCOVERY  -> the partner does not negotiate and
//        Chapter 11.1's detection window ran to completion
//   almost all in ESTABLISH  -> Chapter 9.3's cancellers converged
//        slowly against a marginal cable
// Same total, opposite findings, opposite remedies.
//
// AND THE WORST-EVER FIGURES ARE THE MARGIN. A stage that consistently
// completes at 90% of its own timeout is a stage one disturbance from
// failing -- and the link works perfectly today, so nothing else says
// so. This is the same discipline as Chapter 10.4's eye width and
// Chapter 10.5's convergence margin.
module stage_timing_telemetry
  import link_bringup_pkg::*;
#(
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  stage_e   stage,
  input  logic     stage_entered,
  input  logic [15:0] stage_elapsed_ms,
  input  logic     bringup_complete,
 
  // Per-stage elapsed time from the most recent successful bring-up.
  output logic [15:0] ms_reset,
  output logic [15:0] ms_caps,
  output logic [15:0] ms_discovery,
  output logic [15:0] ms_exchange,
  output logic [15:0] ms_resolution,
  output logic [15:0] ms_establish,
  output logic [15:0] ms_interface,
  output logic [15:0] ms_total,
 
  // The worst ever seen for each, which is the margin.
  output logic [15:0] worst_discovery,
  output logic [15:0] worst_exchange,
  output logic [15:0] worst_establish,
  output logic [15:0] worst_interface,
 
  // Which stage dominated the most recent bring-up, and by how much.
  output stage_e      dominant_stage,
  output logic [6:0]  dominant_percent,
 
  // A stage that completed at or above this fraction of its own
  // timeout. Not a failure, and the only forward-looking signal here.
  output logic        near_timeout,
  output stage_e      near_timeout_stage,
  output logic        ever_near_timeout,
 
  output logic [CNT_W-1:0] c_bringups
);
 
  // 80% of a stage's budget is close enough that ordinary variation
  // will reach the rest of it.
  localparam int unsigned NEAR_PERCENT = 80;
 
  stage_e prev_stage_q;
 
  function automatic logic [15:0] budget_for (input stage_e s);
    unique case (s)
      S_RESET:        budget_for = 16'(T_RESET_MS);
      S_CAPABILITIES: budget_for = 16'(T_CAPS_MS);
      S_DISCOVERY:    budget_for = 16'(T_DISCOVERY_MS);
      S_EXCHANGE:     budget_for = 16'(T_EXCHANGE_MS);
      S_RESOLUTION:   budget_for = 16'(T_RESOLUTION_MS);
      S_ESTABLISH:    budget_for = 16'(T_ESTABLISH_MS);
      S_INTERFACE:    budget_for = 16'(T_INTERFACE_MS);
      default:        budget_for = 16'hFFFF;
    endcase
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ms_reset <= '0; ms_caps <= '0; ms_discovery <= '0;
      ms_exchange <= '0; ms_resolution <= '0; ms_establish <= '0;
      ms_interface <= '0; ms_total <= '0;
      worst_discovery <= '0; worst_exchange <= '0;
      worst_establish <= '0; worst_interface <= '0;
      dominant_stage <= S_RESET; dominant_percent <= 7'd0;
      near_timeout <= 1'b0; near_timeout_stage <= S_RESET;
      ever_near_timeout <= 1'b0; c_bringups <= '0;
      prev_stage_q <= S_RESET;
    end else if (clear) begin
      worst_discovery <= '0; worst_exchange <= '0;
      worst_establish <= '0; worst_interface <= '0;
      c_bringups <= '0; near_timeout <= 1'b0;
      // ever_near_timeout survives -- a stage that has ever run close
      // to its budget is a stage worth watching, and a counter clear
      // is what happens just before somebody investigates.
    end else begin
      near_timeout <= 1'b0;
      prev_stage_q <= stage;
 
      // A stage transition records the OUTGOING stage's elapsed time.
      if (stage_entered && (prev_stage_q != stage)) begin
        unique case (prev_stage_q)
          S_RESET:        ms_reset      <= stage_elapsed_ms;
          S_CAPABILITIES: ms_caps       <= stage_elapsed_ms;
          S_DISCOVERY: begin
            ms_discovery <= stage_elapsed_ms;
            if (stage_elapsed_ms > worst_discovery)
              worst_discovery <= stage_elapsed_ms;
          end
          S_EXCHANGE: begin
            ms_exchange <= stage_elapsed_ms;
            if (stage_elapsed_ms > worst_exchange)
              worst_exchange <= stage_elapsed_ms;
          end
          S_RESOLUTION:   ms_resolution <= stage_elapsed_ms;
          S_ESTABLISH: begin
            ms_establish <= stage_elapsed_ms;
            if (stage_elapsed_ms > worst_establish)
              worst_establish <= stage_elapsed_ms;
          end
          S_INTERFACE: begin
            ms_interface <= stage_elapsed_ms;
            if (stage_elapsed_ms > worst_interface)
              worst_interface <= stage_elapsed_ms;
          end
          default: ;
        endcase
 
        // NEAR TIMEOUT. Not a failure -- the stage completed -- and the
        // only signal that predicts the failure before it happens.
        if ((stage_elapsed_ms * 16'd100) >
            (budget_for(prev_stage_q) * 16'(NEAR_PERCENT))) begin
          near_timeout       <= 1'b1;
          near_timeout_stage <= prev_stage_q;
          ever_near_timeout  <= 1'b1;
        end
      end
 
      if (bringup_complete) begin
        ms_total <= ms_reset + ms_caps + ms_discovery + ms_exchange +
                    ms_resolution + ms_establish + ms_interface;
        if (!(&c_bringups)) c_bringups <= c_bringups + 1'b1;
 
        // Which stage dominated, and by how much. The single most
        // useful number in this module, because it converts a duration
        // into a subsystem.
        if ((ms_discovery >= ms_establish) && (ms_discovery >= ms_exchange)) begin
          dominant_stage   <= S_DISCOVERY;
          dominant_percent <= 7'((ms_discovery * 100) /
                                 (ms_total == 0 ? 16'd1 : ms_total));
        end else if (ms_establish >= ms_exchange) begin
          dominant_stage   <= S_ESTABLISH;
          dominant_percent <= 7'((ms_establish * 100) /
                                 (ms_total == 0 ? 16'd1 : ms_total));
        end else begin
          dominant_stage   <= S_EXCHANGE;
          dominant_percent <= 7'((ms_exchange * 100) /
                                 (ms_total == 0 ? 16'd1 : ms_total));
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that dominant_stage converts a duration into a subsystem, which is the only thing that makes a bring-up time actionable. Two links both taking 1400 ms are two different findings — one spent it in discovery, meaning the partner does not negotiate and Chapter 11.1's detection window ran to completion; the other spent it in establishment, meaning Chapter 9.3's cancellers converged slowly against a marginal cable. Same total, opposite remedies.

Deliberately simplified: the percentage divisions are in RTL. Production telemetry exports the raw millisecond figures and lets software divide, keeping dividers out of the design — the same argument Chapter 9.4 §10 makes about the deficit-idle average.

Production implication: near_timeout is this chapter's margin signal, in the family of Chapter 10.4's narrow eye and Chapter 10.5's convergence margin. A stage completing at 90% of its own budget is not a failure — the link came upand it is one disturbance from becoming one. A stage completing at 10% has room for a cable that ages, a partner that reboots more slowly, and a colder morning.

12. The Timing Budget, Computed

Add the stages up, and the shape of the total is more interesting than its value.

StageBudgetTypical, negotiating gigabitTypical, 10BASE-T partner
1 reset10 ms~2 ms~2 ms
2 capabilities5 ms~0.2 ms~0.2 ms
3 discovery1000 ms~50 ms~500 ms
4 exchange500 ms88–176 msskipped
5 resolution5 ms~0.01 ms~0.01 ms
6 establishment2000 ms200–1500 ms~30 ms
7 interface100 ms~5 ms~5 ms
total3620 ms≈ 350–1750 ms≈ 540 ms

Three observations.

The budget is dominated by two stages — discovery and establishment — and they are the two that wait on something physical. Discovery waits on a partner that may not exist; establishment waits on adaptive loops measuring a cable. Everything else is register reads and arithmetic.

The gigabit path and the legacy path spend their time in opposite places. A gigabit link negotiates quickly and establishes slowly; a 10BASE-T partner is detected slowly — Chapter 11.1 §8's window is deliberately long — and establishes almost instantly. So a bring-up time alone cannot even distinguish a fast link from a slow one, and dominant_stage is what does.

And the exchange's 88 to 176 ms is Chapter 11.2 §9's acknowledge arithmetic, arriving unchanged: three identical link code words received plus six to eight sent, at an 8 to 16 ms burst spacing.

13. RTL 7 — Recording the Bring-Up for Replay

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Records each bring-up attempt as a compact, replayable trace.
//
// WHY. A bring-up failure is often INTERMITTENT -- it happens once in
// fifty power cycles, in a cold cabinet, against one particular
// partner. By the time anybody attaches a debugger, the link is up and
// the evidence is gone: every counter has been overwritten by the
// successful attempt that followed.
//
// So the design records, per attempt: which stages ran, how long each
// took, which failed, and the handful of provenance bits that decide
// whether the resulting link is trustworthy. A ring of these is a few
// hundred bits and answers the question a scope cannot: WHAT HAPPENED
// THE TIME IT WENT WRONG.
module bringup_replay_recorder
  import link_bringup_pkg::*;
#(
  parameter int unsigned DEPTH = 8,
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic     attempt_start,
  input  logic     bringup_complete,
  input  logic     bringup_failed,
  input  stage_e   first_failed_stage,
  input  failure_e first_failure,
  input  logic [15:0] ms_total,
  input  stage_e      dominant_stage,
  input  logic        near_timeout,
  input  logic [3:0]  resolved_tech,
  input  logic        duplex_is_assumed,
  input  logic        stale_measurement_used,
  input  logic        detection_used,
 
  output logic [2:0]  entries,
  output logic        recorder_full,
 
  // The most recent FAILED attempt, held separately from the ring --
  // because a ring of eight fills with successes and evicts the one
  // entry anybody wanted.
  output logic        last_failure_valid,
  output stage_e      last_failure_stage,
  output failure_e    last_failure_kind,
  output logic [15:0] last_failure_ms,
  output logic        last_failure_was_stale,
 
  // Aggregate history across every attempt since reset.
  output logic [CNT_W-1:0] c_attempts,
  output logic [CNT_W-1:0] c_successes,
  output logic [CNT_W-1:0] c_failures,
  output logic [7:0]       failures_by_stage [9],
  output logic             ever_detection_used,
  output logic             ever_assumed_duplex,
  output logic             ever_near_timeout
);
 
  // One ring entry, packed. Small enough that eight of them cost less
  // than a kilobit and answer a question nothing else can.
  typedef struct packed {
    logic        valid;
    logic        succeeded;
    logic [3:0]  failed_stage;
    logic [3:0]  failure_kind;
    logic [15:0] total_ms;
    logic [3:0]  dominant;
    logic [3:0]  tech;
    logic        assumed_duplex;
    logic        detected;
    logic        stale;
    logic        near_timeout;
  } attempt_rec_t;
 
  attempt_rec_t ring_q [DEPTH];
  logic [2:0]   wr_q;
  logic         in_attempt_q;
  integer i;
 
  assign entries       = wr_q;
  assign recorder_full = (wr_q == 3'(DEPTH - 1));
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      wr_q <= 3'd0; in_attempt_q <= 1'b0;
      last_failure_valid <= 1'b0; last_failure_stage <= S_RESET;
      last_failure_kind <= F_NONE; last_failure_ms <= 16'd0;
      last_failure_was_stale <= 1'b0;
      c_attempts <= '0; c_successes <= '0; c_failures <= '0;
      ever_detection_used <= 1'b0; ever_assumed_duplex <= 1'b0;
      ever_near_timeout <= 1'b0;
      for (i = 0; i < DEPTH; i = i + 1) ring_q[i] <= '0;
      for (i = 0; i < 9; i = i + 1)     failures_by_stage[i] <= 8'd0;
    end else if (clear) begin
      wr_q <= 3'd0;
      c_attempts <= '0; c_successes <= '0; c_failures <= '0;
      for (i = 0; i < DEPTH; i = i + 1) ring_q[i] <= '0;
      for (i = 0; i < 9; i = i + 1)     failures_by_stage[i] <= 8'd0;
      // last_failure_* and the three ever_* flags SURVIVE. They are
      // precisely what a clear-then-test workflow destroys, and the
      // reason this module exists.
    end else begin
      if (attempt_start) begin
        in_attempt_q <= 1'b1;
        if (!(&c_attempts)) c_attempts <= c_attempts + 1'b1;
      end
 
      if (in_attempt_q && (bringup_complete || bringup_failed)) begin
        in_attempt_q <= 1'b0;
 
        ring_q[wr_q].valid          <= 1'b1;
        ring_q[wr_q].succeeded      <= bringup_complete;
        ring_q[wr_q].failed_stage   <= 4'(first_failed_stage);
        ring_q[wr_q].failure_kind   <= 4'(first_failure);
        ring_q[wr_q].total_ms       <= ms_total;
        ring_q[wr_q].dominant       <= 4'(dominant_stage);
        ring_q[wr_q].tech           <= resolved_tech;
        ring_q[wr_q].assumed_duplex <= duplex_is_assumed;
        ring_q[wr_q].detected       <= detection_used;
        ring_q[wr_q].stale          <= stale_measurement_used;
        ring_q[wr_q].near_timeout   <= near_timeout;
 
        wr_q <= (wr_q == 3'(DEPTH - 1)) ? 3'd0 : wr_q + 3'd1;
 
        if (bringup_complete) begin
          if (!(&c_successes)) c_successes <= c_successes + 1'b1;
        end else begin
          if (!(&c_failures)) c_failures <= c_failures + 1'b1;
          if (failures_by_stage[first_failed_stage] != 8'hFF)
            failures_by_stage[first_failed_stage] <=
              failures_by_stage[first_failed_stage] + 8'd1;
 
          // THE LAST FAILURE, held OUTSIDE the ring. A ring of eight
          // fills with the successful retries that follow a failure and
          // evicts the one entry anybody wanted.
          last_failure_valid     <= 1'b1;
          last_failure_stage     <= first_failed_stage;
          last_failure_kind      <= first_failure;
          last_failure_ms        <= ms_total;
          last_failure_was_stale <= stale_measurement_used;
        end
 
        if (detection_used)   ever_detection_used <= 1'b1;
        if (duplex_is_assumed) ever_assumed_duplex <= 1'b1;
        if (near_timeout)     ever_near_timeout   <= 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the last failure must be held outside the ring, and this is the module's one non-obvious structural decision. A bring-up failure is followed by retries, and the retries usually succeed — so a ring of eight fills with successes and evicts the single entry anybody wanted. Holding the last failure separately costs a handful of registers and is the difference between a recorded fault and a recorded recovery.

Deliberately simplified: eight entries and one packed record. Production recorders carry a timestamp per entry and often a partner identifier, so that a failure can be correlated with which device was plugged in at the time.

Production implication: failures_by_stage is a nine-element histogram and it answers a question no single failure can. One failure at S_ESTABLISH is an event; forty out of fifty at S_ESTABLISH is a marginal cable, and forty at S_DISCOVERY is a partner that boots more slowly than this device's detection window. The distribution is the finding, and it is invisible to anything that records only the most recent attempt.

14. Which Stages May Be Skipped

Seven stages, and exactly one of them is conditional. Being precise about which is what makes a supervisor portable.

StageSkippableWhen
1 resetno
2 capabilitiesno
3 discoveryno
4 exchangeyesthe partner does not negotiate — Chapter 11.1 §8
5 resolutionno— but its input differs by path
6 establishmentno— but its procedure differs by technology
7 interfaceno
8 enableno

Rows 5 and 6 are the ones that make a naive implementation wrong in a subtle way.

Stage 5 always runs — something must select a technology — but its input is an advertisement on one path and a detection on the other. A resolver that requires an advertisement cannot bring up a link against a 1993 device.

Stage 6 always runs — something must establish the channel — but Section 8 established that its procedure is a different sequence per technology, not a parameterised one.

So a supervisor is portable if and only if it treats stage 4 as conditional, stage 5's input as polymorphic, and stage 6's body as dispatched. A design that hard-codes eight stages with fixed bodies works against exactly the partners it was tested with.

15. Properties Worth Asserting, and One Worth Refusing

This chapter's properties are about ordering and freshness, which no earlier chapter needed — and its rejected property is the one everybody writes about a link.

Capability reading

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P1. caps_valid gates on plausibility, not on transaction success. An
// MDIO read of an absent device COMPLETES and returns nonsense.
property p_caps_valid_needs_plausible;
  @(posedge clk) disable iff (!rst_n)
  caps_valid |-> (!implausible_caps && !bus_stuck_high && !bus_stuck_low);
endproperty
a_caps_need_plausible: assert property (p_caps_valid_needs_plausible);
 
// P2. Gigabit without fast ethernet is refused. No real part does it,
// and it is what a register-offset error produces.
property p_gigabit_implies_fast;
  @(posedge clk) disable iff (!rst_n)
  (caps_valid && (cap_1000t_hd || cap_1000t_fd))
    |-> (base_abilities[2] || base_abilities[3]);
endproperty
a_gigabit_implies_fast: assert property (p_gigabit_implies_fast);
 
// P3. An all-ones or all-zeros identifier is reported, not accepted.
property p_stuck_bus_reported;
  @(posedge clk) disable iff (!rst_n)
  ((phy_id_hi == 16'hFFFF) && (st_q == R_CHECK)) |-> bus_stuck_high;
endproperty
a_stuck_bus_reported: assert property (p_stuck_bus_reported);

Ordering — the properties this chapter is about

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P4. THE ORDERING PROPERTY. A stage is entered only when every
// measurement it consumes is valid AND fresh.
property p_stage_entry_needs_preconditions;
  @(posedge clk) disable iff (!rst_n)
  stage_entered |-> precondition_ok;
endproperty
a_stage_entry_preconditions: assert property (p_stage_entry_needs_preconditions);
 
// P5. Stages advance in order. There is no path from discovery to
// establishment that skips the resolution.
property p_stages_advance_in_order;
  @(posedge clk) disable iff (!rst_n)
  (stage_entered && (stage == S_ESTABLISH))
    |-> ($past(stage) == S_RESOLUTION);
endproperty
a_stages_in_order: assert property (p_stages_advance_in_order);
 
// P6. THE ONE CONDITIONAL EDGE. The exchange may be skipped, and only
// when discovery classified a non-negotiating partner.
property p_exchange_skipped_only_on_detection;
  @(posedge clk) disable iff (!rst_n)
  (stage_entered && (stage == S_RESOLUTION) && ($past(stage) == S_DISCOVERY))
    |-> detection_valid;
endproperty
a_exchange_skip_needs_detection: assert property (p_exchange_skipped_only_on_detection);
 
// P7. THE FRESHNESS PROPERTY. A measurement satisfies a precondition
// only when its epoch is the current attempt's -- valid is not enough,
// because every measurement holds its value across a link drop.
property p_preconditions_require_freshness;
  @(posedge clk) disable iff (!rst_n)
  precondition_ok |-> ((required_mask & stale_mask) == 7'd0);
endproperty
a_preconditions_fresh: assert property (p_preconditions_require_freshness);
 
// P8. A stale measurement is REPORTED distinctly from a missing one.
// One is a stage that has not run; the other is a stage's result being
// reused illegally, and only the second is invisible.
property p_stale_distinct_from_missing;
  @(posedge clk) disable iff (!rst_n)
  ((required_mask & satisfied_mask) != required_mask) &&
  ((required_mask & stale_mask) != 7'd0) |=> stale_measurement_used;
endproperty
a_stale_distinct: assert property (p_stale_distinct_from_missing);
 
// P9. A link drop restarts from DISCOVERY -- not from reset, which
// wastes half a second, and not from the interface, which reuses a
// channel measurement of a cable that may have changed.
property p_drop_restarts_at_discovery;
  @(posedge clk) disable iff (!rst_n)
  (!link_signal_present && (st_q > S_CAPABILITIES) && (st_q != S_FAILED))
    |=> (stage == S_DISCOVERY);
endproperty
a_drop_restarts_discovery: assert property (p_drop_restarts_at_discovery);

Timers and failure attribution

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P10. Each stage times out against ITS OWN budget. A uniform timeout
// is wrong by two orders of magnitude in one direction or the other.
property p_per_stage_timeout;
  @(posedge clk) disable iff (!rst_n)
  (bringup_failed && (failure != F_PRECONDITION) &&
   (failure != F_NO_COMMON_TECH))
    |-> (stage_elapsed_ms >= stage_timeout_ms($past(stage)));
endproperty
a_per_stage_timeout: assert property (p_per_stage_timeout);
 
// P11. Every failure names its stage. "The link did not come up" with
// no stage is eight investigations.
property p_failure_names_stage;
  @(posedge clk) disable iff (!rst_n)
  bringup_failed |-> (failure != F_NONE);
endproperty
a_failure_names_stage: assert property (p_failure_names_stage);
 
// P12. The FIRST failure is latched and never overwritten -- by the
// time software reads it, the supervisor is back at S_RESET retrying.
property p_first_failure_latched;
  @(posedge clk) disable iff (!rst_n)
  (first_failure_valid && !restart) |=> $stable({first_failed_stage,
                                                 first_failure});
endproperty
a_first_failure_latched: assert property (p_first_failure_latched);
 
// P13. Every attempt terminates: complete or failed, never neither.
property p_attempt_terminates;
  @(posedge clk) disable iff (!rst_n)
  (stage_entered && (stage == S_RESET))
    |-> ##[1:$] (bringup_complete || bringup_failed);
endproperty
a_attempt_terminates: assert property (p_attempt_terminates);
 
// P14. No common technology is a FAILURE, not a resolution to the
// lowest -- Chapter 11.2 §7 keeps them distinct so this can exist.
property p_no_common_is_failure;
  @(posedge clk) disable iff (!rst_n)
  (resolution_valid && no_common_technology) |=> (failure == F_NO_COMMON_TECH);
endproperty
a_no_common_is_failure: assert property (p_no_common_is_failure);

Establishment

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P15. Establishment refuses a stale resolution -- checked at the point
// of DAMAGE, because this is the module that would converge cancellers
// against a cable that may have been replaced.
property p_establish_refuses_stale;
  @(posedge clk) disable iff (!rst_n)
  (start && (resolution_epoch != attempt_epoch)) |=> stale_resolution_refused;
endproperty
a_establish_refuses_stale: assert property (p_establish_refuses_stale);
 
// P16. The gigabit sequence is ORDERED: role, then timing, then skew,
// then cancellers. Chapter 9.3's chain, enforced.
property p_gigabit_sequence_ordered;
  @(posedge clk) disable iff (!rst_n)
  (sub_stage == 3'(E_CANCEL)) |-> ($past(sub_stage) == 3'(E_SKEW));
endproperty
a_gigabit_ordered: assert property (p_gigabit_sequence_ordered);
 
// P17. ALL FOUR cancellers. Chapter 9.3 §9: three converged and one
// adapting is not 75% of a link, because 8B1Q4 needs every pair.
property p_all_four_cancellers;
  @(posedge clk) disable iff (!rst_n)
  ((sub_stage == 3'(E_DONE)) && is_gigabit) |-> (&canceller_converged);
endproperty
a_all_four_cancellers: assert property (p_all_four_cancellers);
 
// P18. A technology change mid-establishment abandons the sequence
// rather than finishing a procedure for a technology no longer chosen.
property p_tech_change_abandons;
  @(posedge clk) disable iff (!rst_n)
  tech_changed_mid_establish |=> (sub_stage == 3'(E_IDLE));
endproperty
a_tech_change_abandons: assert property (p_tech_change_abandons);

The gate

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P19. REFUSED BY DEFAULT. A gate that resets open permits
// transmission before anything has been measured.
property p_gate_refuses_after_reset;
  @(posedge clk) disable iff (!rst_n)
  $rose(rst_n) |=> !tx_permitted;
endproperty
a_gate_refuses_after_reset: assert property (p_gate_refuses_after_reset);
 
// P20. Permission requires EVERY condition, fresh.
property p_grant_needs_all_conditions;
  @(posedge clk) disable iff (!rst_n)
  tx_permitted |-> (&condition_mask);
endproperty
a_grant_needs_all: assert property (p_grant_needs_all_conditions);
 
// P21. Permission is REVOCABLE, immediately -- a canceller can diverge
// in microseconds and the supervisor's timer is in milliseconds.
property p_permission_revocable;
  @(posedge clk) disable iff (!rst_n)
  !(&condition_mask) |=> !tx_permitted;
endproperty
a_permission_revocable: assert property (p_permission_revocable);
 
// P22. THE ATTRIBUTION PROPERTY. When permission is withheld, the
// blocking condition names which term is false -- which is what makes
// the conjunction debuggable.
property p_blocker_named;
  @(posedge clk) disable iff (!rst_n)
  !tx_permitted |-> (blocking_condition < 4'd8);
endproperty
a_blocker_named: assert property (p_blocker_named);
 
// P23. Assumed duplex is PUBLISHED, not blocked. A link whose duplex
// was assumed is legal, and this is the last moment anybody can act.
property p_assumed_duplex_published;
  @(posedge clk) disable iff (!rst_n)
  (tx_permitted_rose && duplex_is_assumed) |-> granted_with_assumed_duplex;
endproperty
a_assumed_duplex_published: assert property (p_assumed_duplex_published);
 
// P24. A stage completing near its budget is reported even though it
// succeeded -- the only forward-looking signal in the chapter.
property p_near_timeout_reported;
  @(posedge clk) disable iff (!rst_n)
  (stage_entered && ((stage_elapsed_ms * 16'd100) >
                     (budget_for($past(stage)) * 16'd80))) |=> near_timeout;
endproperty
a_near_timeout_reported: assert property (p_near_timeout_reported);
 
// P25. The last FAILURE survives the successes that follow it.
property p_last_failure_survives;
  @(posedge clk) disable iff (!rst_n)
  (last_failure_valid && bringup_complete) |=> last_failure_valid;
endproperty
a_last_failure_survives: assert property (p_last_failure_survives);

16. Verification Scenarios

Capabilities

  1. A plausible capability setcaps_valid, no flags.
  2. PHY identifier 0xFFFFbus_stuck_high, caps_valid low. A successful read of an absent device.
  3. PHY identifier 0x0000bus_stuck_low. The other termination.
  4. Gigabit claimed, no fast ethernetimplausible_caps, kind 1. A register-offset error, not an unusual part.
  5. 10BASE-T full duplex claimed, half not — kind 2.
  6. 100BASE-TX full duplex claimed, half not — kind 3.
  7. No technologies at all — kind 4; legal, and almost always a read landing on a non-PHY device.
  8. An MDIO read that never acknowledgesread_timeout, caps_valid low.

Ordering and preconditions

  1. A clean eight-stage bring-up — stages advance in order, precondition_ok throughout, tx_permitted rises once.
  2. Discovery entered with caps_valid lowprecondition_ok falls, F_PRECONDITION, precondition_failed_stage = 1.
  3. Establishment entered with the resolution from a previous attemptstale_measurement_used, blocked. The failure the epoch exists for.
  4. The same, with the resolution never taken at all — blocked, and stale_measurement_used low. Missing and stale, reported distinctly.
  5. A link drop during establishment — restart at S_DISCOVERY, not S_RESET and not S_INTERFACE.
  6. A link drop during capabilities — no restart; the stage is below the drop threshold.
  7. Every stage's required mask — cumulative and a prefix; stage 7 requires everything stages 1–6 produced.
  8. A non-negotiating partner — stage 4 skipped, stage 5 entered from S_DISCOVERY, detection_valid required for the edge.
  9. A negotiating partner — stage 4 runs; the S_DISCOVERY → S_RESOLUTION edge is not taken.
  10. A supervisor mutated to allow the skip without a detection — P6 fires.

Timers and failure attribution

  1. Discovery taking 999 ms — completes; no timeout.
  2. Discovery taking 1001 msF_NO_CLASSIFICATION, first_failed_stage = S_DISCOVERY.
  3. Establishment taking 1999 ms — completes. The budget that a uniform timeout would have aborted.
  4. A uniform 500 ms timeout applied to every stage (deliberate mutation) — every gigabit link fails at S_ESTABLISH; P10 identifies it.
  5. Capabilities taking 6 msF_IMPLAUSIBLE_CAPS by timeout, distinct from the plausibility failure.
  6. Two failures in one attemptfirst_failed_stage holds the first; the second does not overwrite it.
  7. A failure followed by a successful retryfirst_failure_valid clears on restart; last_failure_* in the recorder survives.
  8. No common technologyF_NO_COMMON_TECH, and it is a failure rather than a resolution to 10BASE-T.

Establishment

  1. A gigabit resolution — role, timing, skew, cancellers, in that order.
  2. A 100BASE-TX resolution — descrambler and idle only; none of the gigabit sub-stages run.
  3. A 10BASE-T resolution — link pulses only.
  4. A role conflictestablishment_failed, failed_sub_stage = E_ROLE, immediately rather than on a timeout.
  5. Skew out of rangefailed_sub_stage = E_SKEW.
  6. Three cancellers converged, one adapting — establishment does not complete. P17.
  7. The resolution changing mid-establishmenttech_changed_mid_establish, sequence abandoned.
  8. Establishment started with a stale resolution epochstale_resolution_refused at the point of damage. P15.

The gate

  1. Out of reset with every condition already satisfiedtx_permitted low for at least one cycle. P19.
  2. Seven of eight conditions — permission withheld, blocking_condition naming the eighth.
  3. All eighttx_permitted_rose once, c_grants increments.
  4. The interface losing alignment while permitted — permission drops in one cycle, not at a timeout. P21.
  5. A canceller diverging while permitted — the same; the gate is faster than the supervisor.
  6. Permission granted on a detected pathgranted_with_assumed_duplex, ever_assumed_duplex_grant sticky, and permission is still granted.
  7. A gate mutated to reset high — P19 fires; transmission before any measurement.

Telemetry and replay

  1. A bring-up dominated by discoverydominant_stage = S_DISCOVERY, percentage above 60.
  2. One dominated by establishment with the same totaldominant_stage = S_ESTABLISH. Same number, opposite finding.
  3. A stage completing at 81% of its budgetnear_timeout, near_timeout_stage naming it, and the link comes up.
  4. Eight successful attempts after one failure — the ring fills with successes; last_failure_* still holds the failure.
  5. clear after a failure — counters clear; last_failure_* and the three ever_* flags survive.

17. Debugging: Which Stage, and Was Its Input Fresh

ObservationLikely causeThe distinguishing check
link never comes up, no stage reportedthe supervisor is not running, or reset is not releasingstage; c_attempts at zero
first_failed_stage = S_RESETno device on the MDIO busbus_stuck_high or bus_stuck_low
first_failed_stage = S_CAPABILITIESa wrong register map, or a device mid-resetimplausibility_kind names which check failed
first_failed_stage = S_DISCOVERYno partner, or one that boots slower than the windowc_attempts; try a longer T_DISCOVERY_MS
first_failed_stage = S_EXCHANGEthe partner does not acknowledgeChapter 11.2 §9's ACK-masking bug is the first suspect
first_failed_stage = S_RESOLUTIONno common technologycommon_count at zero; check both advertisements
first_failed_stage = S_ESTABLISHthe channelfailed_sub_stage: role, skew or cancellers
first_failed_stage = S_INTERFACEthe boardChapter 10.4's delay, or a lane
link comes up and corrupts dataa stage ran on a stale measurementstale_measurement_used, ever_stale_blocked
link comes up slowly, alwaysone stage dominatingdominant_stage and dominant_percent
link works, near_timeout seta stage with no marginnear_timeout_stage; not a failure, and a prediction
link works at the wrong duplex under loadstage 4 was skippedgranted_with_assumed_duplex

Four habits.

First, read first_failed_stage and stop. It is one register, latched at the moment of failure and held across the retries that follow, and it turns "the link did not come up" into one of eight investigations — a missing device, a wrong register map, an absent partner, a silent partner, no common technology, a bad cable, a bad board, or a stale input.

Second, when the link does come up and does not work, suspect freshness before suspecting hardware. Every measurement holds its value across a drop. A stage running on last attempt's resolution or last cable's canceller coefficients completes successfully — the mechanisms converge on whatever is there — and produces a link that reports itself perfect. stale_measurement_used and ever_stale_blocked are the only signals that see it.

Third, read dominant_stage before reading the total. Two bring-ups of 1400 ms are two different findings; the total cannot distinguish "the partner does not negotiate" from "the cable is marginal", and the dominant stage does it in one register.

Fourth, treat near_timeout on a working link as an open item. A stage completing at 90% of its budget came up today. It is the same signal as Chapter 10.4's narrow eye and Chapter 10.5's convergence margin — a success with no room left.

18. Common Misconceptions

"The bring-up stages are a convention, so they can be run concurrently to save time."

The wrong model: an ordering chosen for tidiness.

What it costs: every stage still completes, and five of them complete against garbage.

The corrected model: the ordering is a data dependency. Discovery consumes the capability set; the exchange consumes the classification; the resolution consumes the advertisement; establishment consumes the technology; the interface consumes the speed. A stage running early does not fail — it succeeds against a reset default or a previous attempt's residue — because a stage's success criterion is about its own behaviour and never about the validity of its input. The failure surfaces two stages later, or at the frame layer, or under load a week after commissioning.

"A measurement that reads valid is valid."

The wrong model: a _valid signal means the value can be used.

What it costs: the chapter's hardest failure — a link that comes up on a new cable using the old cable's canceller coefficients.

The corrected model: every measurement holds its value across a link drop, because registers hold their last value. So resolution_valid reads 1 after a drop, and it is last attempt's resolution. Validity must be paired with freshness — an epoch saying which attempt the measurement was taken in — and a precondition is satisfied only when both hold. stale_measurement_used is what separates this stage has not run from this stage's result is being reused illegally.

"One timeout for the whole bring-up is simpler and just as good."

The wrong model: a single generous budget.

What it costs: it is wrong by two orders of magnitude in one direction or the other.

The corrected model: stages 1, 2, 5 and 7 are computation and together take under 10 ms. Stages 3 and 6 are measurement of the physical world — a partner that may not exist, a channel nobody has characterised — and take hundreds of milliseconds to seconds. A timeout sized for establishment makes a missing partner take two seconds to report; one sized for discovery aborts every gigabit link mid-convergence. Each stage's budget must come from what that stage is waiting for.

"Establishment is the same procedure at a different speed."

The wrong model: one sequence, parameterised.

What it costs: a design that runs the wrong procedure and completes.

The corrected model: each technology establishes by a different sequence of different measurements. 10BASE-T establishes by seeing link pulses. 100BASE-TX locks a descrambler and detects idle. 1000BASE-T runs Chapter 9.3's ordered chain — master/slave role, then loop timing, then pair skew, then four independent canceller convergences — each depending on the one before. There is no parameter that turns the first into the third, which is why stage 6 dispatches rather than configures.

"'The link is up' is the property to assert."

The wrong model: one signal, one assertion.

What it costs: a red assertion that identifies nothing, and three blocks that mean three different things by the same name.

The corrected model: "up" is a conjunction of eight independent conditions, each with its own success criterion. A property over the conjunction fires when any one of them is false and names none of them — so its failure sends an engineer to look at all eight. And the name is overloaded: the PHY's link_up means its line side is established, the interface layer's means clocks and alignment, the MAC's means it may transmit. Assert the terms and the ordering between them, and publish a blocking_condition that says which term is false.

19. Interview Reasoning

"Why can't the bring-up stages run concurrently?"

Because five of the seven consume a measurement the previous stage produced, and a measurement that has not been taken still reads as something. Discovery consumes the capability set — run it early and it advertises a reset default. The exchange consumes the classification — run it early and it decodes link pulses as bursts, or waits forever for bursts that will never come. Establishment consumes the technology — run it early and it runs 100BASE-TX's procedure on a gigabit link. The interface consumes the speed — run it early and Chapter 10.4's sampling tap is calibrated against a UI five times too wide. The strong answer names why none of this shows up as an error: a stage's success criterion is about its own behaviour and never about the validity of its input — and it cannot be, because validating an input would recursively require the input's inputs, all the way back to reset. So the ordering has to be enforced between the stages, by a supervisor holding a precondition matrix.

"What is the difference between a valid measurement and a fresh one, and why does it matter?"

Every measurement in bring-up holds its value across a link drop, because registers hold their last value. So after a drop, resolution_valid still reads 1, the canceller coefficients are still there, the sampling tap is still chosen — and every one of them describes the previous link. A precondition checked on validity alone is satisfied by residue. The fix is a per-attempt epoch: each measurement records which attempt it was taken in, and a precondition is satisfied only when the measurement is valid and its epoch is current. The strong answer gives the concrete damage: a link that drops when the cable is swapped, and comes back using the old cable's canceller coefficients — which Chapter 9.3 established are a model of one specific cable's echo and crosstalk. They converge on whatever is now there, report success, and put errors on the wire. The finishing point: stale_measurement_used must be distinct from missing, because a stage that has not run is visible and a stage reusing an old result is not.

"Where in the sequence may TX_EN first be asserted, and why is that the right place?"

After all seven measurement stages, and the gate measures nothing itself. It is a permission, not a status: it takes eight conditions — device answered, capabilities plausible, partner classified, abilities exchanged or parallel detection completed, technology resolved, channel established, interface ready, signal present — and grants only when every one is valid and fresh. The strong answer names three design properties. It refuses by default, because a gate that resets open permits transmission before anything has been measured. It is revocable immediately — a canceller can diverge in microseconds while the supervisor's timer is in milliseconds, so the gate drops in one cycle rather than one timeout. And it publishes blocking_condition, which names the failing term. The finishing point: the gate publishes rather than blocks on an assumed duplex, because such a link is legal and works — but this is the last moment anybody can act on it before it becomes Chapter 9.2's mismatch under load.

"Would you assert that the link is up?"

No — and unusually, the objection is not that the property is wrong. It is true, well-clocked, well-scoped and about a real signal. The objection is that it is a conjunction of eight independent conditions, so its failure identifies none of them. A green result tells you eight things, which is useful; a red result tells you that one of eight went wrong, which you already knew from the link not working, and sends an engineer to check all eight. And there is a second problem about naming: three blocks in the same design mean three different things by link_up — the PHY means its line side is established, the interface layer means clocks and alignment, the MAC means it may transmit — so a property connecting any two asserts an equivalence no module defines. Assert the terms instead: a stage is entered only when its preconditions are valid and fresh; stages advance in order; the one conditional edge is taken only on a detection; the gate refuses by default, requires every condition, revokes immediately, and names the blocking one. The test: if this property fails, do I know which of its terms was false?

20. Understanding Check

Because five of the seven stages consume a measurement the previous one produced.

StageConsumesIf run early it gets
discoverythe capability seta reset default
exchangethe classificationan unclassified partner
resolutionthe advertisementa stale or zero word
establishmentthe technologythe previous technology
interfacethe speedthe previous speed

And every one of those still completes. Nothing hangs, nothing errors, every stage reports success — because a stage's success criterion is about its own behaviour and not about the validity of its input.

Which is correct design, not an oversight. A stage that validated its inputs would duplicate the previous stage's work and need its inputs to do so, recursively back to reset.

So the ordering has to be enforced between the stages, by a supervisor holding a precondition matrix. The stages check themselves; the supervisor checks the order.

The failure this prevents is the one the whole track keeps meeting: a link that comes up, reports success at every stage, and does not work.

21. What's Next

The claim this chapter defended: bring-up ordering is a data dependency, and each stage measures something the next one assumes.

Eight stages: reset, capabilities, discovery, exchange, resolution, establishment, interface, enable. Five of them consume a measurement the previous stage produced, and every one of them still completes when run early — against a reset default, a previous technology, or last cable's coefficients — because a stage's success criterion is about its own behaviour and never about the validity of its input.

Which means the order has to be enforced between the stages. That is a precondition matrix, and its non-obvious requirement is freshness rather than validity: every measurement holds its value across a link drop, so resolution_valid reads 1 after the cable is swapped and describes the cable that is gone. A per-attempt epoch is what separates this stage has not run from this stage's result is being reused illegally — and only the second is invisible.

Each stage carries its own budget, because four of them are register reads taking under 10 ms and two are measurements of the physical world taking hundreds of milliseconds to seconds. A uniform timeout is wrong by two orders of magnitude in one direction or the other. And each carries its own failure, latched per attempt, because from above all eight failures present identically as the link is not up.

Stage 8 measures nothing. It is a permission: refused by default, granted only on eight fresh conditions, revocable in one cycle, and publishing which condition blocks — because "the link is up" is a conjunction, and a property over a conjunction fires when any term is false and names none of them.

Chapter 11.4 — Negotiation Failures and Duplex Mismatch takes the failures this chapter named and builds the diagnosis.

Three chapters have now each contributed a way for a link to come up and be wrong. Chapter 11.1's lost pulse removes an ability and negotiates one step down, cleanly, with both ends agreeing. Chapter 11.2's table disagreement has two devices resolve differently against tables that are never transmitted. And this chapter's skipped stage 4 leaves duplex assumed rather than agreed, which is Chapter 9.2's mismatch waiting for load. Every one of them reports success at every stage, and none of them increments a counter. 11.4 owns the taxonomy and the decision procedure that takes an observable symptom to a named cause — including the cases, and there are several, where no single end has enough evidence and the two must be compared.

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.