Skip to content
VLSI Mentor

Ethernet · Module 9

10 Mbps — The Original Shared-Medium MAC

Manchester cost 100% overhead and bought a clock a 1980 receiver could recover; the shared coax made CSMA/CD necessary and slot time sized the frame. Every constant later modules called inherited was rational here.

Eight modules have described Ethernet as it is, and repeatedly found constants that arrived from somewhere else. The 64-octet floor is a round-trip propagation time (Chapter 8.2). The 96-bit gap is a receiver recovery interval (Chapter 5.9). The preamble is seven octets of alternating bits (Chapter 5.2). Each was described as inherited, and each was left slightly unexplained.

This chapter is where they came from, and the point is that every one of them was rational.

Not "acceptable for the time" in the apologetic sense. Correct, given a receiver built from 1980 components, a shared coaxial cable, and no way for two stations to coordinate. The constraints were real, the arithmetic was done properly, and the answers have survived four decades largely because they were right rather than because nobody dared change them.

Chapter 1.2 owns the derivations — why slot time is a round trip, why a station must still be transmitting when a collision returns. This chapter owns the implementation: what was actually built, what each mechanism cost, and what the hardware had to do that the description leaves out.

1. Scope — What This Chapter Owns

Chapter 1.1 and Chapter 1.2 own the problem and the derivations. Why a shared medium needs arbitration, why the arbitration is listen-then-transmit-then-listen, and why slot time comes out at 512 bit times. None of that is repeated here.

This chapter owns the implementation: Manchester encoding and its decoder, the carrier-sense and collision-detect front end as built, the transceiver boundary and the heartbeat that tests it, and the link integrity pulse that 10BASE-T added when the coax became twisted pair.

Chapter 3.5 owns block coding and treats Manchester as a member of a family; this chapter treats it as the specific thing that was built and asks what it cost.

Chapter 9.2 owns Fast Ethernet and the move to switches, which is where most of this chapter's mechanisms stopped mattering — and its argument is that the switch mattered more than the tenfold rate.

The question this chapter answers that its neighbours do not: what did the original Ethernet actually build, and why was each expensive-looking choice the right one?

2. Why Manchester Was Worth Doubling the Bandwidth

Manchester coding represents each bit as a transition in the middle of the bit period rather than as a level. This guarantees at least one transition every bit time, from which a receiver with no shared clock can recover timing. It also makes every bit exactly balanced between the two levels, so the signal has no direct current component and can pass through a transformer. The cost is that ten million bits per second requires twenty million signal transitions per second, doubling the bandwidth.One data bit100 ns at 10 Mb/sMid-bit transitionthe bit's valueClock recoverablea transition every bitExact DC balancepasses a transformer20 Mbaud for 10 Mb/s100% overheadWorth it in 1980no other clock existed12
Figure 1 — Manchester spends a transition per bit and buys a clock the receiver can recover without one being sent.

Manchester encodes each bit as a transition in the middle of its bit period rather than as a level. By the 802.3 convention, a high-to-low mid-bit transition is a zero and a low-to-high transition is a one.

Which produces the property everything else rests on: there is a transition in every single bit time, guaranteed, whatever the data. A run of a thousand zeros has a thousand transitions.

And that is what a receiver needed, because in 1980 there was no other way to know where the bits were. No shared clock line, no reference, no preamble long enough to matter on its own — the signal had to carry its own timing, and a self-clocking code is a code where it does.

The cost, computed:

Value
data rate10 Mb/s
bit period100 ns
signal transitionsup to 20 million per second
fundamental frequency10 MHz
bandwidth overhead100%

Doubling the required bandwidth to carry the same data is an enormous price, and it looks absurd against later codes — Chapter 3.5's 4B/5B costs 25% and 64B/66B costs about 3%.

But those codes need something Manchester does not: a receiver that can hold timing across a long run without a transition. 4B/5B guarantees a transition within a few bit times, not within one, and recovering a clock from that requires a phase-locked loop with enough loop bandwidth and enough stability — circuitry that was not economic in 1980 and is a rounding error today.

So Manchester is not a bad code that got replaced by good ones. It is the code that requires the least of the receiver, and it was chosen when the receiver was the expensive part.

3. RTL 1 — The Encoder

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Manchester encoder. The whole transformation is one XOR.
//
// A data bit occupies one bit time and is emitted as two half-bit
// levels: the first is the bit's complement and the second is the bit,
// so the mid-bit transition carries the value.
//
// IEEE 802.3 convention: a HIGH-to-LOW mid-bit transition is a ZERO and
// a LOW-to-HIGH transition is a ONE. Getting this backwards produces a
// perfectly working encoder that every conforming receiver decodes
// inverted -- and Section 4's decoder can recover from exactly that,
// which is why the error survives bring-up.
package mac10_pkg;
 
  // 10 Mb/s: 100 ns bit period, 50 ns half-bit.
  localparam int unsigned BIT_PERIOD_NS  = 100;
  localparam int unsigned HALF_BIT_NS    = 50;
  // Chapter 1.2's slot time, in bit times.
  localparam int unsigned SLOT_BIT_TIMES = 512;
  // Jam emitted on collision, in bits.
  localparam int unsigned JAM_BITS       = 32;
  // Attempts before a frame is abandoned.
  localparam int unsigned MAX_ATTEMPTS   = 16;
  // Backoff exponent stops growing here -- "truncated" binary
  // exponential backoff.
  localparam int unsigned BACKOFF_LIMIT  = 10;
 
endpackage
 
module manchester_encoder
  import mac10_pkg::*;
(
  input  logic clk_2x,       // 20 MHz: one edge per half-bit
  input  logic rst_n,
 
  input  logic bit_valid,
  input  logic bit_in,
  output logic bit_taken,
 
  output logic tx_level,
  output logic tx_active
);
 
  logic phase_q;             // 0 = first half-bit, 1 = second
  logic bit_q;
  logic active_q;
 
  assign bit_taken = bit_valid && !phase_q;
  assign tx_active = active_q;
 
  always_ff @(posedge clk_2x or negedge rst_n) begin
    if (!rst_n) begin
      phase_q  <= 1'b0;
      bit_q    <= 1'b0;
      active_q <= 1'b0;
      tx_level <= 1'b0;
    end else begin
      if (!phase_q) begin
        if (bit_valid) begin
          bit_q    <= bit_in;
          active_q <= 1'b1;
          // First half-bit: the COMPLEMENT. So a one starts low and
          // rises at mid-bit; a zero starts high and falls.
          tx_level <= ~bit_in;
          phase_q  <= 1'b1;
        end else begin
          active_q <= 1'b0;
          tx_level <= 1'b0;
        end
      end else begin
        // Second half-bit: the bit itself. The transition between the
        // two halves IS the encoded value.
        tx_level <= bit_q;
        phase_q  <= 1'b0;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that tx_level is exactly bit XOR phase, and every property of the code follows from that one expression. A transition occurs at every mid-bit because the two halves always differ. The DC balance is exact because each bit emits one high half and one low half. Nothing is tracked, nothing accumulates, and there is no state beyond the phase — which is the difference between this and every later code.

Deliberately simplified: it assumes a 20 MHz clock and takes one bit per bit period. A real transmitter drives a line driver with controlled rise times, and the transceiver's analogue side is where most of the 10BASE5 and 10BASE2 engineering lived.

Production implication: the polarity convention is a comment rather than a parameter, and it should be. An inverted encoder produces a perfectly working signal that a conforming peer decodes inverted — and because Section 4's decoder can recover from an inverted line, the error can survive bring-up against a tolerant peer and appear only against a strict one. A convention that a receiver can compensate for is a convention a transmitter can get wrong for a long time, which is Chapter 6.2's convention argument arriving at the physical layer.

4. RTL 2 — The Decoder, and Recovering Polarity

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Manchester decoder with polarity recovery.
//
// Two jobs, and the second is the one that gets forgotten:
//
//   1. sample the mid-bit transition and recover the data
//   2. determine which POLARITY the line is in
//
// Twisted pair can be wired with a pair reversed, and a reversed pair
// inverts every transition -- turning every one into a zero and every
// zero into a one. The frame is otherwise perfect: the transitions are
// all present, the timing is all correct, and every bit is wrong.
//
// The preamble is what resolves it. Alternating ones and zeros are
// symmetric under inversion, so the preamble alone cannot -- but the
// START FRAME DELIMITER is not symmetric, and that is what pins the
// polarity down (Chapter 5.2's asymmetry, used for a second purpose).
module manchester_decoder
  import mac10_pkg::*;
(
  input  logic clk_4x,       // 40 MHz: oversample the half-bits
  input  logic rst_n,
 
  input  logic rx_level,
  input  logic carrier,
 
  output logic       bit_valid,
  output logic       bit_out,
  output logic       polarity_inverted,
  output logic       polarity_known,
  // A bit period with no mid-bit transition. Not a data value: a coding
  // violation, which is how the end of a frame is recognised.
  output logic       code_violation,
  output logic [15:0] c_violations
);
 
  logic [1:0] samp_q;
  logic [2:0] cnt_q;
  logic       last_q;
  logic       raw_bit;
 
  // The mid-bit transition's direction is the bit, before polarity is
  // applied. Section 3's convention: low-to-high is a one.
  assign raw_bit = (~last_q) & rx_level;
 
  always_ff @(posedge clk_4x or negedge rst_n) begin
    if (!rst_n) begin
      samp_q <= '0; cnt_q <= '0; last_q <= 1'b0;
      bit_valid <= 1'b0; bit_out <= 1'b0;
      polarity_inverted <= 1'b0; polarity_known <= 1'b0;
      code_violation <= 1'b0; c_violations <= '0;
    end else begin
      bit_valid      <= 1'b0;
      code_violation <= 1'b0;
 
      if (!carrier) begin
        cnt_q <= '0;
        // Polarity is NOT forgotten between frames. It is a property of
        // the cabling, which does not change while the link is up, and
        // relearning it every frame wastes the preamble on a question
        // that was already answered.
      end else begin
        samp_q <= {samp_q[0], rx_level};
 
        if (cnt_q == 3'd3) begin
          cnt_q <= '0;
          if (samp_q[1] == samp_q[0]) begin
            // No transition where one is guaranteed. Either the frame
            // has ended or the signal is corrupt.
            code_violation <= 1'b1;
            if (!(&c_violations)) c_violations <= c_violations + 1'b1;
          end else begin
            bit_valid <= 1'b1;
            bit_out   <= polarity_inverted ? ~raw_bit : raw_bit;
          end
          last_q <= rx_level;
        end else begin
          cnt_q <= cnt_q + 1'b1;
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the preamble cannot resolve polarity and the start delimiter can. An alternating pattern is symmetric under inversion — invert 1010… and it is still 1010…, offset by one bit — so a receiver seeing a perfect preamble has learned its timing and nothing about its polarity. The delimiter's asymmetric bit pattern is what settles it, which is Chapter 5.2's "break the pattern exactly once" serving a second purpose nobody designed it for.

Deliberately simplified: four-times oversampling with a fixed phase. A real decoder tracks the transition's position and adjusts, which is a digital phase-locked loop and is where the analogue-adjacent engineering sits.

Production implication: polarity_inverted is retained across frames, and that is not laziness. Polarity is a property of the cabling — which pair was wired to which pin — and it does not change while the link is up. Relearning it every frame spends the preamble answering a question already settled, and on a marginal link a per-frame decision can flip, producing a receiver that inverts occasional frames while decoding the rest perfectly — a failure that looks like data corruption and is a decoder state machine.

5. Carrier Sense and Collision Detect, As Built

Chapter 1.2 describes CSMA/CD as an algorithm: listen, transmit, listen while transmitting, and if a collision occurs, jam and back off. The hardware had two jobs the algorithm does not mention, and both are analogue.

Carrier sense is "is anybody transmitting", and it is a signal-presence measurement. On coax it is a threshold on the received level; on twisted pair it is the presence of transitions on the receive pair. It is a decision with a threshold, which means it has a margin and can be wrong in both directions — a weak signal missed, or noise mistaken for carrier.

Collision detect is harder, and on coax it is genuinely a measurement. Two stations transmitting simultaneously drive the same conductor, so the average DC level rises beyond what one transmitter can produce. The detector is a comparator on that average, and its threshold has to sit above one station's worst case and below two stations' best case — a window that narrows as the cable gets longer and the signals attenuate.

On 10BASE-T it is trivial and completely different. A station transmitting on one pair and simultaneously receiving on the other is, by definition, in a collision — no threshold, no measurement, no margin. The same logical event, detected by two mechanisms with nothing in common.

And the timing constraint is what ties it back to the frame. Chapter 1.2 established that a station must still be transmitting when a collision returns, which is why the frame has a floor. A collision detected after the slot time has expired is a late collision, and it means something is wrong with the network's physical extent rather than with the traffic — the segment is longer than the standard permits, or a repeater budget was exceeded.

Which produces two collision classes that need entirely different handling:

DetectedMeaningResponse
normalwithin the slot timeordinary contentionjam, back off, retry
lateafter the slot timethe network is over-lengthreport it; do not retry

Retrying a late collision is the wrong response and it is the default one. The frame will collide again, for the same physical reason, and the retry consumes capacity while hiding a configuration fault behind an error counter that says "collisions".

6. RTL 3 — The Front End

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Carrier sense, collision detect, jam and backoff.
//
// The part worth reading is the collision CLASSIFICATION. A collision
// inside the slot time is ordinary contention: the protocol working
// exactly as designed, and the correct response is to jam, back off and
// retry. A collision AFTER the slot time cannot happen on a conforming
// network, so it means the network is not conforming -- too long, too
// many repeaters, or a duplex mismatch (Chapter 9.2's classic fault).
//
// Retrying a late collision is the default behaviour and it is wrong.
// The frame will collide again for the same physical reason, and the
// retry hides a configuration fault behind an error counter.
module carrier_collision_frontend
  import mac10_pkg::*;
#(
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,          // 10 MHz bit clock
  input  logic rst_n,
  input  logic clear,
 
  // From the transceiver.
  input  logic carrier_sense,
  input  logic collision_in,
 
  input  logic tx_request,
  input  logic tx_last_bit,
 
  output logic tx_enable,
  output logic jam_active,
  output logic frame_abandoned,
 
  output logic             late_collision,
  output logic [CNT_W-1:0] c_collisions,
  output logic [CNT_W-1:0] c_late_collisions,
  output logic [CNT_W-1:0] c_abandoned,
  // Attempts on the frame that needed the most. A saturated network
  // shows in this long before frames start being abandoned.
  output logic [4:0]       worst_attempts,
  output logic             late_collision_seen   // sticky
);
 
  typedef enum logic [2:0] {
    C_IDLE, C_DEFER, C_XMIT, C_JAM, C_BACKOFF
  } c_state_e;
 
  c_state_e    state_q;
  logic [9:0]  bits_q;        // bit times into the current transmission
  logic [4:0]  attempts_q;
  logic [15:0] backoff_q;
  logic [4:0]  jam_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q <= C_IDLE; bits_q <= '0; attempts_q <= '0;
      backoff_q <= '0; jam_q <= '0;
      tx_enable <= 1'b0; jam_active <= 1'b0; frame_abandoned <= 1'b0;
      late_collision <= 1'b0; c_collisions <= '0; c_late_collisions <= '0;
      c_abandoned <= '0; worst_attempts <= '0; late_collision_seen <= 1'b0;
    end else begin
      late_collision  <= 1'b0;
      frame_abandoned <= 1'b0;
 
      if (clear) begin
        c_collisions <= '0; c_late_collisions <= '0; c_abandoned <= '0;
        // worst_attempts and late_collision_seen deliberately survive.
      end
 
      case (state_q)
        C_IDLE: if (tx_request) begin
          // Carrier sense: defer while the medium is busy. This is the
          // "CS" of CSMA/CD and it is a whole state because deferring is
          // the common case on a busy segment.
          state_q    <= carrier_sense ? C_DEFER : C_XMIT;
          bits_q     <= '0;
          attempts_q <= '0;
          tx_enable  <= !carrier_sense;
        end
 
        C_DEFER: if (!carrier_sense) begin
          // The interframe gap is enforced elsewhere (Chapter 5.9); this
          // state only waits for the medium to go quiet.
          state_q   <= C_XMIT;
          tx_enable <= 1'b1;
          bits_q    <= '0;
        end
 
        C_XMIT: begin
          bits_q <= bits_q + 1'b1;
 
          if (collision_in) begin
            tx_enable  <= 1'b0;
            jam_active <= 1'b1;
            jam_q      <= '0;
            state_q    <= C_JAM;
            if (!(&c_collisions)) c_collisions <= c_collisions + 1'b1;
 
            // THE CLASSIFICATION. Past the slot time, a collision cannot
            // happen on a conforming network.
            if (bits_q >= 10'(SLOT_BIT_TIMES)) begin
              late_collision      <= 1'b1;
              late_collision_seen <= 1'b1;
              if (!(&c_late_collisions)) c_late_collisions <= c_late_collisions + 1'b1;
            end
          end else if (tx_last_bit) begin
            tx_enable <= 1'b0;
            state_q   <= C_IDLE;
            if (attempts_q > worst_attempts) worst_attempts <= attempts_q;
          end
        end
 
        C_JAM: begin
          // A fixed 32 bits, so that every station on the segment sees
          // the collision for long enough to detect it.
          if (jam_q == 5'(JAM_BITS - 1)) begin
            jam_active <= 1'b0;
            attempts_q <= attempts_q + 1'b1;
 
            if (late_collision_seen && (bits_q >= 10'(SLOT_BIT_TIMES))) begin
              // A late collision is not retried. Retrying reproduces the
              // physical fault and hides it.
              state_q         <= C_IDLE;
              frame_abandoned <= 1'b1;
              if (!(&c_abandoned)) c_abandoned <= c_abandoned + 1'b1;
            end else if (attempts_q == 5'(MAX_ATTEMPTS - 1)) begin
              state_q         <= C_IDLE;
              frame_abandoned <= 1'b1;
              if (!(&c_abandoned)) c_abandoned <= c_abandoned + 1'b1;
              if (attempts_q > worst_attempts) worst_attempts <= attempts_q;
            end else begin
              state_q <= C_BACKOFF;
              // Truncated binary exponential: the range doubles with each
              // attempt and stops doubling at BACKOFF_LIMIT.
              backoff_q <= random_slots(attempts_q);
            end
          end else begin
            jam_q <= jam_q + 1'b1;
          end
        end
 
        C_BACKOFF: if (backoff_q == '0) begin
          state_q <= carrier_sense ? C_DEFER : C_XMIT;
          bits_q  <= '0;
        end else begin
          backoff_q <= backoff_q - 1'b1;
        end
 
        default: state_q <= C_IDLE;
      endcase
    end
  end
 
  // Uniform over 0 .. 2^k - 1 slot times, k capped at BACKOFF_LIMIT.
  // The cap is what makes it "truncated": without it the range doubles
  // sixteen times and the last attempts wait for minutes.
  function automatic logic [15:0] random_slots(input logic [4:0] attempt);
    automatic int unsigned k =
        (attempt > 5'(BACKOFF_LIMIT)) ? BACKOFF_LIMIT : int'(attempt);
    random_slots = 16'($urandom_range((1 << k) - 1, 0)) * 16'(SLOT_BIT_TIMES);
  endfunction
 
endmodule

Classification: synthesizable.

What it teaches: that worst_attempts reports congestion long before c_abandoned does. A frame is abandoned only after sixteen attempts, which on a busy segment is rare — but a segment where frames routinely need six or seven attempts is already in trouble, and the abandonment counter says nothing about it. The extreme is the leading indicator and the count is the trailing one.

Deliberately simplified: $urandom_range in a function is verification code, not synthesis. A real backoff uses a linear-feedback shift register seeded from something unique to the station — and the uniqueness matters, because two stations with identical seeds back off identically and collide again on every retry.

Production implication: the truncation in the backoff is the detail that gets omitted and it is load-bearing. Without the cap at ten, the range doubles sixteen times — up to 65 535 slot times, which at 51.2 µs per slot is 3.4 seconds for a single frame's final attempt. The cap bounds the worst-case delay at 1023 slots, about 52 ms, and the frames that would have waited longer are abandoned instead. A protocol that gives up is better than one that waits for seconds, and that judgement is encoded in one comparison.

7. The Transceiver Boundary, and Why It Has a Heartbeat

The attachment unit interface carries three signal paths between the station and the transceiver. Data out carries transmitted data to the medium. Data in carries received data back. Control in carries the collision indication. Because a failed collision detector is silent and silence is indistinguishable from no collision, the transceiver asserts the control in path briefly after every transmission as a self test, proving to the station that the collision circuitry is present and working.Stationthe MACData outto the mediumData infrom the mediumControl incollision indicationA failed detectoris silentHeartbeat afterevery frameproves the path works12
Figure 2 — three signal paths across the transceiver boundary, and a fourth thing the collision path must prove about itself.

The original Ethernet put the transceiver on the cable and the station somewhere else, connected by an attachment unit interface carrying three signal paths: data out, data in, and control in — the last of which is the collision indication.

Which creates a problem that has nothing to do with Ethernet and everything to do with detectors: a collision detector that has failed is silent, and silence is exactly what "no collision" looks like.

A station cannot distinguish a working detector on a quiet network from a broken detector on a busy one. Both report nothing, forever.

So the standard added a self-test. After every transmission, the transceiver briefly asserts the collision signal — not because a collision occurred, but to prove the collision circuitry is present, powered, and connected. It is called the SQE test, or the heartbeat, and the station expects it after each frame it sends.

And it comes with an exception that says a great deal. SQE test must be disabled when the transceiver is connected to a repeater — and a twisted-pair hub is a repeater. A repeater seeing the heartbeat would interpret it as a real collision and propagate one, so the self-test must be switched off precisely where the network is largest and detector failures matter most.

8. RTL 4 — Watching the Heartbeat

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Observes the SQE test and turns its presence or absence into a
// statement about the transceiver.
//
// Three states, and they are genuinely different findings:
//
//   heartbeat present after every frame -> the collision detector is
//        connected and working. The normal case for a station attached
//        directly to a medium.
//   heartbeat absent, consistently     -> either the transceiver has
//        SQE disabled (correct when attached to a repeater) or its
//        collision circuitry has failed. THESE ARE INDISTINGUISHABLE
//        from the signal alone, which is why the expected mode is a
//        configuration input rather than something inferred.
//   heartbeat intermittent             -> a fault. A working test is
//        deterministic; an unreliable one means a marginal connection.
module sqe_test_observer
  import mac10_pkg::*;
#(
  // Window after a transmission ends within which the heartbeat is
  // expected, in bit times.
  parameter int unsigned SQE_WINDOW_BITS = 15,
  // Whether SQE is expected at all. MUST be false when attached to a
  // repeater, and the design cannot work this out for itself.
  parameter bit SQE_EXPECTED = 1'b1,
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic tx_done,
  input  logic collision_in,
  input  logic tx_active,
 
  output logic             heartbeat_seen,
  output logic             heartbeat_missing,
  // A collision indication outside both a transmission and the SQE
  // window. On a half-duplex medium this should not happen, and it
  // usually means the transceiver is asserting collision spuriously.
  output logic             spurious_collision,
 
  output logic [CNT_W-1:0] c_frames_sent,
  output logic [CNT_W-1:0] c_heartbeats,
  output logic [CNT_W-1:0] c_missing,
  output logic [CNT_W-1:0] c_spurious,
 
  // Sticky: a transceiver that has ever failed its own self-test is a
  // transceiver whose collision reports cannot be trusted, and that
  // fact outlives the counter.
  output logic             detector_suspect
);
 
  logic [4:0] window_q;
  logic       armed_q;
  logic       seen_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      window_q <= '0; armed_q <= 1'b0; seen_q <= 1'b0;
      heartbeat_seen <= 1'b0; heartbeat_missing <= 1'b0;
      spurious_collision <= 1'b0;
      c_frames_sent <= '0; c_heartbeats <= '0; c_missing <= '0; c_spurious <= '0;
      detector_suspect <= 1'b0;
    end else begin
      heartbeat_seen     <= 1'b0;
      heartbeat_missing  <= 1'b0;
      spurious_collision <= 1'b0;
 
      if (clear) begin
        c_frames_sent <= '0; c_heartbeats <= '0;
        c_missing <= '0; c_spurious <= '0;
        // detector_suspect deliberately survives.
      end
 
      if (tx_done) begin
        if (!(&c_frames_sent)) c_frames_sent <= c_frames_sent + 1'b1;
        armed_q  <= SQE_EXPECTED;
        window_q <= '0;
        seen_q   <= 1'b0;
      end else if (armed_q) begin
        if (collision_in && !seen_q) begin
          seen_q         <= 1'b1;
          heartbeat_seen <= 1'b1;
          if (!(&c_heartbeats)) c_heartbeats <= c_heartbeats + 1'b1;
        end
 
        if (window_q == 5'(SQE_WINDOW_BITS)) begin
          armed_q <= 1'b0;
          if (!seen_q) begin
            // The self-test did not run. With SQE_EXPECTED set, that
            // means the transceiver cannot prove its collision detector
            // works -- so nothing it reports about collisions can be
            // relied on.
            heartbeat_missing <= 1'b1;
            detector_suspect  <= 1'b1;
            if (!(&c_missing)) c_missing <= c_missing + 1'b1;
          end
        end else begin
          window_q <= window_q + 1'b1;
        end
      end else if (collision_in && !tx_active) begin
        // Collision asserted while not transmitting and outside the SQE
        // window. On a half-duplex medium a station cannot collide with
        // traffic it is not part of.
        spurious_collision <= 1'b1;
        detector_suspect   <= 1'b1;
        if (!(&c_spurious)) c_spurious <= c_spurious + 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that SQE_EXPECTED cannot be inferred and must be configured. A transceiver with SQE disabled and a transceiver with a dead collision detector produce identical signals — nothing, after every frame. The design cannot distinguish them, and the only thing that can is knowing whether this port is attached to a repeater, which is a deployment fact.

Deliberately simplified: a fixed window in bit times. The real timing has a tolerance, and a transceiver whose heartbeat lands slightly outside a narrow window is reported as failing when it is merely at the edge of spec.

Production implication: detector_suspect is sticky and survives a counter clear, and it changes the meaning of every collision statistic on the port. A station whose transceiver never proved its collision detector is a station whose zero-collision reading means nothing — it may be a quiet segment or a blind station, and those are opposite conclusions from the same number. The flag is what stops a clean counter being read as good news.

On a coaxial network a station could infer the medium's presence from its electrical characteristics. On twisted pair an idle link is silent, and silence is indistinguishable from an unplugged cable, a powered off peer, or a broken pair. So an idle transmitter emits a link integrity pulse roughly every sixteen milliseconds, and a receiver declares the link down after a period with neither pulses nor data. Link state becomes a positive assertion rather than an inference from silence.Idle twisted pairno signal at allSilence is ambiguousunplugged? off? broken?A pulse every 16 ms±8 msLink is upa positive assertionNo pulse, no datalink down after a timeoutData also countsa busy link needs nopulses12
Figure 3 — on an idle twisted-pair link, silence is ambiguous; a periodic pulse removes the ambiguity.

When Ethernet moved from coax to twisted pair, one thing that had been free stopped being free: knowing the link existed.

On coax the medium was a shared conductor with electrical characteristics a transceiver could sense. On twisted pair an idle link is completely silent — and silence is indistinguishable from an unplugged cable, a powered-off peer, a broken pair, or a peer that simply has nothing to send.

So 10BASE-T added a link integrity pulse. An idle transmitter emits a single pulse roughly every 16 milliseconds, with a tolerance of ±8 ms, and a receiver that sees them declares the link up. A receiver that sees neither pulses nor data for long enough declares the link down.

Which converts link state from an inference into an assertion, and gives every twisted-pair Ethernet interface since the thing every network engineer looks at first: a link light.

Two details in that mechanism are worth having.

Data counts as well as pulses. A busy link does not need to interrupt itself to prove it is alive — the traffic proves it. The pulse is only sent when there is nothing else to send.

And the timeout is several pulse intervals rather than one. A single missed pulse is noise; a link that drops on one missed pulse flaps continuously. The timeout has to be long enough to tolerate a miss and short enough to be useful, which lands it in the range of tens to a hundred-odd milliseconds.

And this humble pulse is the ancestor of something much larger. Chapter 9.2 shows autonegotiation replacing the single pulse with a burst of them, encoding a capability word in their presence and absence, while remaining compatible with a 10BASE-T partner that sees only pulses and concludes the link is up.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Emits link integrity pulses when idle and maintains link state from
// pulses and data together.
//
// The subtleties are all in the timing constants, and each has a reason:
//
//   TX interval 16 ms   -- often enough that a real failure is noticed
//        quickly, rare enough to cost nothing on a busy link.
//   RX timeout > 2x     -- a single missed pulse is noise. A link that
//        drops on one missed pulse flaps continuously on a marginal
//        cable, which is worse than being slightly slow to notice.
//   data counts as life -- a busy link proves itself with traffic and
//        need not interrupt itself.
module link_integrity_pulse
  import mac10_pkg::*;
#(
  // Clock cycles per millisecond, so the timing constants can be stated
  // in the units the standard uses.
  parameter int unsigned CYCLES_PER_MS = 10_000,
  parameter int unsigned TX_INTERVAL_MS = 16,
  // Several intervals, so one missed pulse does not drop the link.
  parameter int unsigned RX_TIMEOUT_MS  = 100,
  parameter int unsigned CNT_W = 16
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic tx_active,          // a frame is being sent
  input  logic rx_pulse,           // a link pulse was received
  input  logic rx_data,            // data was received
 
  output logic tx_pulse,
  output logic link_up,
 
  output logic [CNT_W-1:0] c_pulses_sent,
  output logic [CNT_W-1:0] c_pulses_rcvd,
  output logic [CNT_W-1:0] c_link_transitions,
  // Longest silence ever observed while the link stayed up. A link whose
  // worst gap approaches the timeout is one flap away from dropping, and
  // no counter of transitions says so.
  output logic [CNT_W-1:0] worst_silence_ms
);
 
  logic [31:0] tx_ctr_q;
  logic [31:0] rx_ctr_q;
  logic [31:0] ms_ctr_q;
  logic [CNT_W-1:0] silence_ms_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      tx_ctr_q <= '0; rx_ctr_q <= '0; ms_ctr_q <= '0; silence_ms_q <= '0;
      tx_pulse <= 1'b0; link_up <= 1'b0;
      c_pulses_sent <= '0; c_pulses_rcvd <= '0;
      c_link_transitions <= '0; worst_silence_ms <= '0;
    end else begin
      tx_pulse <= 1'b0;
 
      if (clear) begin
        c_pulses_sent <= '0; c_pulses_rcvd <= '0; c_link_transitions <= '0;
        // worst_silence_ms deliberately survives: it characterises the
        // cable, not the measurement window.
      end
 
      // TRANSMIT. A frame in flight restarts the interval -- the traffic
      // is itself the proof, so the pulse would be redundant.
      if (tx_active) begin
        tx_ctr_q <= '0;
      end else if (tx_ctr_q >= 32'(TX_INTERVAL_MS * CYCLES_PER_MS)) begin
        tx_pulse <= 1'b1;
        tx_ctr_q <= '0;
        if (!(&c_pulses_sent)) c_pulses_sent <= c_pulses_sent + 1'b1;
      end else begin
        tx_ctr_q <= tx_ctr_q + 1'b1;
      end
 
      // RECEIVE. Either a pulse or data resets the timeout, and the
      // symmetry with the transmit side is deliberate.
      if (rx_pulse || rx_data) begin
        if (rx_pulse && !(&c_pulses_rcvd)) c_pulses_rcvd <= c_pulses_rcvd + 1'b1;
        rx_ctr_q <= '0;
        if (silence_ms_q > worst_silence_ms) worst_silence_ms <= silence_ms_q;
        silence_ms_q <= '0;
        if (!link_up) begin
          link_up <= 1'b1;
          if (!(&c_link_transitions)) c_link_transitions <= c_link_transitions + 1'b1;
        end
      end else if (rx_ctr_q >= 32'(RX_TIMEOUT_MS * CYCLES_PER_MS)) begin
        if (link_up) begin
          link_up <= 1'b0;
          if (!(&c_link_transitions)) c_link_transitions <= c_link_transitions + 1'b1;
        end
      end else begin
        rx_ctr_q <= rx_ctr_q + 1'b1;
      end
 
      // Millisecond tick for the silence measurement.
      if (ms_ctr_q >= 32'(CYCLES_PER_MS - 1)) begin
        ms_ctr_q <= '0;
        if (!(&silence_ms_q)) silence_ms_q <= silence_ms_q + 1'b1;
      end else begin
        ms_ctr_q <= ms_ctr_q + 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the receive timeout must be several transmit intervals and not one. A pulse arriving at 16 ± 8 ms means consecutive pulses can legitimately be 24 ms apart, and a timeout of 20 ms would drop a perfectly good link. A timeout at 100 ms tolerates several missed pulses, which is the difference between a link light that means something and one that flickers.

Deliberately simplified: pulse reception arrives as a clean signal. Detecting a single pulse on a real receiver is an analogue problem with a threshold and a width qualification, and a receiver that accepts anything as a pulse will hold a link up on noise.

Production implication: worst_silence_ms is the number that predicts a flap. A link whose worst observed silence is 30 ms against a 100 ms timeout has ample margin; one whose worst is 85 ms is a single disturbance away from dropping — and c_link_transitions says nothing about it, because the link has not dropped yet. The extreme leads and the transition count trails, which is the same pairing this track has used for broadcast rate, frame size and queueing delay.

11. What Every Later Generation Kept

The chapter's closing argument, and it is worth stating as a list because the through-line is the whole point of Module 9.

Kept, unchanged, in every generation since:

MechanismIntroduced forStill present because
the 64-octet floorcollision detectabilityframes cross links that no longer share the reason
the 96-bit gapreceiver recoverythe physical layer's housekeeping window
the 7-octet preambleManchester clock lockevery code still needs an acquisition period
the frame formatthe shared coaxchanging it would break everything
CRC-32a noisy shared mediumthe error model got better, not different

Dropped, and cleanly:

CSMA/CD itself. Chapter 1.5 removed it, and Chapter 9.4 shows the standard eventually removing half duplex entirely.

Manchester coding, replaced at 100 Mb/s by a code that costs 25% instead of 100% — because by then the receiver could afford to hold timing across several bit times.

The separate transceiver and its heartbeat, once the transceiver moved onto the same die as the MAC and there was no boundary left to test.

And the pattern in what survived is consistent: the mechanisms that were about the frame survived, and the mechanisms that were about the medium did not. A frame crosses many media; a medium's mechanism belongs to one.

12. Assertions — Coding, Timing and a Detector That Must Not Be Assumed

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// P1 -- THE CODING PROPERTY. A transition in every bit time, whatever
// the data. Everything Manchester buys follows from this.
// ---------------------------------------------------------------------
property p_transition_every_bit;
  @(posedge clk_2x) disable iff (!rst_n)
    (tx_active && phase_q) |-> (tx_level != $past(tx_level));
endproperty
a_transition_every_bit: assert property (p_transition_every_bit)
  else $error("no mid-bit transition -- the code is not self-clocking");
 
// ---------------------------------------------------------------------
// P2 -- EXACT DC BALANCE. Each bit emits one high half and one low half,
// which is what lets the signal cross a transformer.
// ---------------------------------------------------------------------
property p_dc_balanced_per_bit;
  @(posedge clk_2x) disable iff (!rst_n)
    (tx_active && phase_q) |-> ($past(tx_level) == ~tx_level);
endproperty
a_dc_balanced_per_bit: assert property (p_dc_balanced_per_bit);
 
// ---------------------------------------------------------------------
// P3 -- The encoding is the polarity convention. A low-to-high mid-bit
// transition is a one.
// ---------------------------------------------------------------------
property p_polarity_convention;
  @(posedge clk_2x) disable iff (!rst_n)
    (tx_active && phase_q) |-> (tx_level == bit_q);
endproperty
a_polarity_convention: assert property (p_polarity_convention);
 
// ---------------------------------------------------------------------
// P4 -- Encode then decode is the identity, WHEN the polarity is known.
// The qualifier is the point: without it the round trip inverts.
// ---------------------------------------------------------------------
property p_encode_decode_identity;
  @(posedge clk) disable iff (!rst_n)
    (bit_valid && polarity_known) |-> (bit_out == $past(bit_in, ENC_LATENCY));
endproperty
a_encode_decode_identity: assert property (p_encode_decode_identity);
 
// ---------------------------------------------------------------------
// P5 -- Polarity survives between frames: it is a property of the
// cabling, not of the frame.
// ---------------------------------------------------------------------
property p_polarity_persists;
  @(posedge clk) disable iff (!rst_n)
    (polarity_known && !carrier) |=> $stable(polarity_inverted);
endproperty
a_polarity_persists: assert property (p_polarity_persists);
 
// ---------------------------------------------------------------------
// P6 -- A missing mid-bit transition is a CODE VIOLATION, never a data
// value. Decoding it as data invents bits the transmitter never sent.
// ---------------------------------------------------------------------
property p_violation_not_decoded;
  @(posedge clk) disable iff (!rst_n)
    code_violation |-> !bit_valid;
endproperty
a_violation_not_decoded: assert property (p_violation_not_decoded);
 
// ---------------------------------------------------------------------
// P7 -- Deference: a transmission never starts while carrier is sensed.
// The "CS" of CSMA/CD, as a property.
// ---------------------------------------------------------------------
property p_defers_to_carrier;
  @(posedge clk) disable iff (!rst_n)
    ($rose(tx_enable)) |-> !$past(carrier_sense);
endproperty
a_defers_to_carrier: assert property (p_defers_to_carrier);
 
// ---------------------------------------------------------------------
// P8 -- A collision always produces a jam of exactly the specified
// length, so every station on the segment sees it long enough.
// ---------------------------------------------------------------------
property p_jam_length;
  @(posedge clk) disable iff (!rst_n)
    $rose(jam_active) |-> ##JAM_BITS $fell(jam_active);
endproperty
a_jam_length: assert property (p_jam_length);
 
// ---------------------------------------------------------------------
// P9 -- THE CLASSIFICATION. A collision after the slot time is a LATE
// collision, and the two classes are decided by the bit count alone.
// ---------------------------------------------------------------------
property p_late_collision_classified;
  @(posedge clk) disable iff (!rst_n)
    (collision_in && (state == C_XMIT) && (bits_q >= 10'(SLOT_BIT_TIMES)))
      |-> late_collision;
endproperty
a_late_collision_classified: assert property (p_late_collision_classified);
 
// ---------------------------------------------------------------------
// P10 -- A late collision is NOT retried. Retrying reproduces a physical
// fault and hides it behind a collision counter.
// ---------------------------------------------------------------------
property p_late_collision_not_retried;
  @(posedge clk) disable iff (!rst_n)
    late_collision |-> ##[1:JAM_BITS+2] frame_abandoned;
endproperty
a_late_collision_not_retried: assert property (p_late_collision_not_retried)
  else $error("a late collision was retried -- the network is over-length and this hides it");
 
// ---------------------------------------------------------------------
// P11 -- The backoff is truncated. Without the cap the final attempts
// wait for seconds.
// ---------------------------------------------------------------------
property p_backoff_truncated;
  @(posedge clk) disable iff (!rst_n)
    (state == C_BACKOFF) |-> (backoff_q <= 16'((1 << BACKOFF_LIMIT) - 1) * 16'(SLOT_BIT_TIMES));
endproperty
a_backoff_truncated: assert property (p_backoff_truncated);
 
// ---------------------------------------------------------------------
// P12 -- A frame is abandoned after at most MAX_ATTEMPTS.
// ---------------------------------------------------------------------
property p_attempts_bounded;
  @(posedge clk) disable iff (!rst_n)
    (attempts_q <= 5'(MAX_ATTEMPTS));
endproperty
a_attempts_bounded: assert property (p_attempts_bounded);
 
// ---------------------------------------------------------------------
// P13 -- The heartbeat, when expected, arrives within its window after
// every transmission. This is what makes the detector CHECKED rather
// than assumed -- compare the rejected property below.
// ---------------------------------------------------------------------
property p_heartbeat_follows_transmission;
  @(posedge clk) disable iff (!rst_n)
    (SQE_EXPECTED && tx_done) |-> ##[1:SQE_WINDOW_BITS] heartbeat_seen;
endproperty
a_heartbeat_follows_transmission: assert property (p_heartbeat_follows_transmission);
 
// ---------------------------------------------------------------------
// P14 -- A missing heartbeat makes the detector suspect, and the flag is
// sticky: a transceiver that once failed its self-test cannot be trusted
// about collisions afterwards.
// ---------------------------------------------------------------------
property p_missing_heartbeat_is_sticky;
  @(posedge clk) disable iff (!rst_n)
    heartbeat_missing |=> detector_suspect;
endproperty
a_missing_heartbeat_is_sticky: assert property (p_missing_heartbeat_is_sticky);
 
// ---------------------------------------------------------------------
// P15 -- A collision indication outside a transmission and outside the
// SQE window is spurious. A half-duplex station cannot collide with
// traffic it is not part of.
// ---------------------------------------------------------------------
property p_no_collision_while_idle;
  @(posedge clk) disable iff (!rst_n)
    (collision_in && !tx_active && !armed_q) |-> spurious_collision;
endproperty
a_no_collision_while_idle: assert property (p_no_collision_while_idle);
 
// ---------------------------------------------------------------------
// P16 -- Link pulses are suppressed while transmitting: the traffic is
// itself the proof of life.
// ---------------------------------------------------------------------
property p_no_pulse_during_traffic;
  @(posedge clk) disable iff (!rst_n)
    tx_active |-> !tx_pulse;
endproperty
a_no_pulse_during_traffic: assert property (p_no_pulse_during_traffic);
 
// ---------------------------------------------------------------------
// P17 -- The receive timeout is longer than several transmit intervals,
// so one missed pulse cannot drop the link.
// ---------------------------------------------------------------------
// synopsys translate_off
a_timeout_tolerates_a_miss: assert final (RX_TIMEOUT_MS >= 3 * TX_INTERVAL_MS)
  else $fatal(1, "link timeout is too short -- one missed pulse will drop the link");
// synopsys translate_on
 
// ---------------------------------------------------------------------
// P18 -- COVERAGE. A late collision, a missing heartbeat, a polarity
// inversion and an abandoned frame -- four states a healthy network
// never reaches and a verification run must.
// ---------------------------------------------------------------------
c_late_collision:   cover property (@(posedge clk) disable iff (!rst_n) late_collision);
c_missing_heartbeat:cover property (@(posedge clk) disable iff (!rst_n) heartbeat_missing);
c_polarity_flip:    cover property (@(posedge clk) disable iff (!rst_n) polarity_inverted);
c_abandoned:        cover property (@(posedge clk) disable iff (!rst_n) frame_abandoned);

13. Verification — Twenty-Four Scenarios and a Collision That Arrives Too Late

#ScenarioStimulusWhat must be observed
1All-zeros data64 octets of 0x00a transition every bit time (P1)
2All-ones data64 octets of 0xFFa transition every bit time — the run-length case
3DC balanceany dataeach bit emits one high and one low half (P2)
4Polarity conventiona known bit patternlow-to-high mid-bit is a one (P3)
5Encode then decoderandom data, polarity knownthe identity (P4)
6Inverted lineswap the receive pairthe preamble decodes; the delimiter resolves polarity
7Polarity persiststwo frames on an inverted linelearned once, not relearned (P5)
8End of framecarrier drops mid-bitcode_violation, not a decoded bit (P6)
9Deferencerequest while carrier is highno transmission until carrier clears (P7)
10Normal collisioncollide at bit 200jam of 32 bits; backoff; retry (P8)
11Collision at the boundarycollide at bit 511still a normal collision
12Late collisioncollide at bit 512late_collision; frame abandoned, not retried (P9, P10)
13Backoff range growssuccessive collisionsthe range doubles per attempt
14Backoff truncatesattempt 12the range is capped at attempt 10's (P11)
15Sixteen attemptspersistent collisionsframe abandoned; c_abandoned increments (P12)
16Worst attemptsa burst needing 7 attemptsworst_attempts = 7 while c_abandoned stays zero
17Heartbeat presentSQE expected, transceiver healthyheartbeat_seen after every frame (P13)
18Heartbeat missingSQE expected, none arrivesheartbeat_missing; detector_suspect sticky (P14)
19SQE disabled correctlySQE_EXPECTED = 0, attached to a repeaterno heartbeat expected; no suspicion raised
20Spurious collisioncollision asserted while idlespurious_collision; detector suspect (P15)
21Link pulses when idleno traffica pulse every 16 ms (P16)
22Pulses suppressed by trafficcontinuous framesno pulses; link stays up on data alone
23One missed pulsesuppress a single pulselink stays up (P17)
24Link dropssilence past the timeoutlink_up low; c_link_transitions increments

14. Debugging — Reading a 10 Mb/s Segment

Symptom — late collisions, and the cable tests fine.

Not a cable fault. A late collision means the round trip exceeded the slot time, so the network's physical extent is over budget — too long a segment, too many repeaters in the path, or a diameter that grew when somebody added a hub. Cable testing checks one link and the fault is a property of the path, which is why the tests pass.

Symptom — collisions rising and no frames being lost.

Read worst_attempts rather than c_abandoned. A frame is abandoned only after sixteen attempts, which is rare even on a congested segment — but a segment where frames routinely need six or seven attempts is already near its useful limit. The extreme leads and the abandonment count trails.

Symptom — a station reports zero collisions on a busy shared segment.

Suspicious rather than reassuring. Check detector_suspect and the heartbeat counters: a station whose transceiver never proved its collision detector produces exactly this reading, and so does a genuinely quiet segment. The two are indistinguishable from the collision count alone, which is the whole reason the SQE test exists.

Symptom — a hub reporting collisions on a port with one station attached.

The transceiver's SQE test is enabled and the port is a repeater port. A repeater interprets the heartbeat as a real collision and propagates one — so the fix is to disable SQE on that transceiver, and the symptom is one collision per transmitted frame, which is a ratio no real contention produces.

Symptom — occasional frames arrive with every bit inverted.

The decoder is relearning polarity per frame and getting it wrong on marginal frames. Polarity is a property of the cabling and does not change while the link is up — a decoder that re-decides each frame will eventually decide wrongly, producing frames that are structurally perfect and semantically inverted. The check sequence catches them, so the symptom is a check-failure rate rather than corrupt data, and it correlates with signal quality rather than with content.

Symptom — a link light that flickers.

The receive timeout is too close to the transmit interval. Pulses arrive at 16 ± 8 ms, so consecutive pulses can legitimately be 24 ms apart — a timeout below about 50 ms drops the link on ordinary jitter. Read worst_silence_ms: if it approaches the timeout on a link that is nominally up, the margin is gone and the next disturbance drops it.

Symptom — a code-violation counter climbing with no frame errors.

Expected in small numbers: the end of every frame is a code violation, because carrier stops and the guaranteed transition does not arrive. A violation counter that counts frame ends is measuring frames. A rate far above the frame rate is a real signal-integrity finding — and it is available before any frame fails, because most violations fall in the parts of a frame the check sequence would have caught anyway.

15. Common Misconceptions

"Manchester was a primitive code they replaced with better ones."

The wrong model: 100% overhead is obviously bad, so it was a mistake corrected later.

What it costs: you cannot explain why it was chosen by people who could count, and you miss what the overhead bought.

The corrected model: it is the code that requires the least of the receiver — a guaranteed transition every single bit time, so a 1980 receiver could recover a clock without a phase-locked loop. Later codes cost less bandwidth and demand more circuitry, which became the right trade when circuitry got cheap and bandwidth did not. It also delivers exact DC balance for free, which is what made transformer-coupled twisted pair straightforward later.

"A collision detector either works or is obviously broken."

The wrong model: a detector's failure is visible.

What it costs: Section 12's rejected property, and a station that transmits into collisions indefinitely while reporting none. A zero-collision reading from an unverified detector is not good news — it is the same reading a blind station gives.

The corrected model: a failed detector is silent, and silence is what "no collision" looks like. The standard mandates a self-test — the SQE heartbeat — precisely because the failure mode is indistinguishable from success from the outside.

"A collision is a collision."

The wrong model: one event, one response: jam, back off, retry.

What it costs: a late collision is retried, the retry collides again for the same physical reason, and a network that is over-length is hidden behind a counter labelled "collisions" that also counts ordinary contention.

The corrected model: the slot time separates two classes. Inside it, the protocol is working. Outside it, a collision cannot happen on a conforming network — so the network is not conforming, and the correct response is to report and abandon, never to retry.

"The link light means the cable is good."

The wrong model: link up implies a healthy connection.

What it costs: a link whose worst observed silence is close to the timeout appears identical to one with ample margin, and drops on the next disturbance with no warning.

The corrected model: the link light means a pulse or a frame arrived recently enough — it is a positive assertion of life with a coarse timeout, and nothing more. worst_silence_ms against the timeout is the margin, and only that number predicts a flap.

"The heartbeat should always be on — it is a safety feature."

The wrong model: more checking is always better.

What it costs: a transceiver with SQE enabled on a repeater port makes the repeater see one collision per transmitted frame, because a repeater cannot distinguish a heartbeat from a real collision.

The corrected model: SQE must be disabled when attached to a repeater — which means the check is required to be off in exactly the deployments where a detector failure matters most. The design's own SQE observer is what remains, and the mode has to be configured because a design cannot tell a disabled self-test from a dead detector.

16. Interview Reasoning

"Why did the original Ethernet use Manchester coding when it doubles the bandwidth?"

The weak answer says it was self-clocking and stops. The answer that ends the topic gives what the overhead bought and why the trade has since reversed: a transition in every bit time, so a receiver with no shared clock and no phase-locked loop could recover timing from any data — including a thousand consecutive zeros. Later codes cost 25% or 3% and demand a receiver that can hold timing across several bit times, which was not economic in 1980 and is free now. The extra credit is the second property: exact DC balance, per bit, which is what made transformer-coupled twisted pair trivial when 10BASE-T arrived.

"What is a late collision and why does it matter?"

A collision detected after the slot time, which cannot happen on a conforming network — so it means the network's physical extent is over budget: too long, too many repeaters, or a duplex mismatch. The strong answer gives the consequence: it must not be retried. The frame will collide again for the same physical reason, the retry consumes capacity, and the fault hides behind a counter that also counts ordinary contention. Two collision classes, two counters, two entirely different findings.

"What is the SQE test for, and why must it sometimes be off?"

Because a failed collision detector is silent, and silence is exactly what "no collision" looks like — so a station could transmit into collisions forever without knowing. The heartbeat is the transceiver proving, after every frame, that its collision circuitry is present and working. And it must be disabled when the transceiver attaches to a repeater, because a repeater sees the heartbeat as a real collision and propagates one. So the check is required to be off in the deployments where detector failure matters most, which is the finishing observation.

"Would you assert that a collision is always detected?"

No, and the strongest argument is that the standard provides a self-test for exactly this mechanism — which is a statement, by the committee, that it can fail. Collision detection on coax is a comparator on an average level with a margin that narrows as the cable attenuates; a late collision is detected after the frame is gone; and a dead detector is silent. Assert the heartbeat instead, and treat a detector that never proved itself as one whose clean counters mean nothing.

17. Understanding Check

Two things, and the second outlived the first.

A transition in every bit time, guaranteed. Each bit is encoded as a mid-bit transition rather than a level, so a run of a thousand zeros has a thousand transitions. A receiver with no shared clock can recover one from any data whatsoever — which is what a 1980 receiver needed, because there was no other timing reference and no economic way to hold a clock across a long run without transitions.

Exact DC balance, per bit. Every Manchester bit is half high and half low, whatever its value, so any sequence has precisely zero DC component. That is what lets the signal cross a transformer, and transformer isolation is what every twisted-pair Ethernet interface has used since 10BASE-T.

And the trade has reversed rather than been corrected. Chapter 3.5's 4B/5B costs 25% and 64B/66B about 3% — and both demand a receiver that can hold timing across several bit times, which needs a phase-locked loop. Manchester asks the least of the receiver, and the receiver was the expensive part.

18. What's Next

The claim this chapter defended: every constant later modules called inherited was rational when it was made, and each was a receiver's problem solved by a transmitter's expenditure.

Manchester spent 100% of the bandwidth and bought a transition in every bit time — a clock a 1980 receiver could recover without a phase-locked loop — plus exact DC balance, which is what made transformer-coupled twisted pair straightforward a decade later. The preamble spent seven octets buying time for that clock to settle. The 64-octet floor spent padding buying collisions that are always detectable inside the slot time. And the gap spent idle time buying a receiver's recovery.

And one mechanism went the other way. The SQE heartbeat costs the transceiver a signal after every frame and buys the station proof that the collision detector still works — because a failed detector is silent, and silence is what "no collision" looks like. Its existence in the standard is the strongest possible argument against asserting that a collision is always detected.

Chapter 9.2 — 100 Mbps: Fast Ethernet and the Switch Transition takes the next generation, and its argument is that the tenfold rate increase was the smaller change.

100BASE-TX replaced Manchester with Chapter 3.5's 4B/5B, and the resulting spectrum went up by only about three times for ten times the data. But the change that mattered was elsewhere: the move from hubs to switches, which is where full duplex became possible and where CSMA/CD — everything this chapter's front end implements — quietly stopped being used at all. And 9.2 covers the mechanism that made the transition survivable: autonegotiation, built from this chapter's link pulse, and the duplex mismatch that is its classic failure.

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.