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 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 rate | 10 Mb/s |
| bit period | 100 ns |
| signal transitions | up to 20 million per second |
| fundamental frequency | 10 MHz |
| bandwidth overhead | 100% |
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
// 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
endmoduleClassification: 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
// 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
endmoduleClassification: 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:
| Detected | Meaning | Response | |
|---|---|---|---|
| normal | within the slot time | ordinary contention | jam, back off, retry |
| late | after the slot time | the network is over-length | report 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
// 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
endmoduleClassification: 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 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
// 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
endmoduleClassification: 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.
9. 10BASE-T, and the Pulse That Says the Link Exists
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.
10. RTL 5 — The Link Integrity Pulse
// 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
endmoduleClassification: 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:
| Mechanism | Introduced for | Still present because |
|---|---|---|
| the 64-octet floor | collision detectability | frames cross links that no longer share the reason |
| the 96-bit gap | receiver recovery | the physical layer's housekeeping window |
| the 7-octet preamble | Manchester clock lock | every code still needs an acquisition period |
| the frame format | the shared coax | changing it would break everything |
| CRC-32 | a noisy shared medium | the 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
// ---------------------------------------------------------------------
// 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
| # | Scenario | Stimulus | What must be observed |
|---|---|---|---|
| 1 | All-zeros data | 64 octets of 0x00 | a transition every bit time (P1) |
| 2 | All-ones data | 64 octets of 0xFF | a transition every bit time — the run-length case |
| 3 | DC balance | any data | each bit emits one high and one low half (P2) |
| 4 | Polarity convention | a known bit pattern | low-to-high mid-bit is a one (P3) |
| 5 | Encode then decode | random data, polarity known | the identity (P4) |
| 6 | Inverted line | swap the receive pair | the preamble decodes; the delimiter resolves polarity |
| 7 | Polarity persists | two frames on an inverted line | learned once, not relearned (P5) |
| 8 | End of frame | carrier drops mid-bit | code_violation, not a decoded bit (P6) |
| 9 | Deference | request while carrier is high | no transmission until carrier clears (P7) |
| 10 | Normal collision | collide at bit 200 | jam of 32 bits; backoff; retry (P8) |
| 11 | Collision at the boundary | collide at bit 511 | still a normal collision |
| 12 | Late collision | collide at bit 512 | late_collision; frame abandoned, not retried (P9, P10) |
| 13 | Backoff range grows | successive collisions | the range doubles per attempt |
| 14 | Backoff truncates | attempt 12 | the range is capped at attempt 10's (P11) |
| 15 | Sixteen attempts | persistent collisions | frame abandoned; c_abandoned increments (P12) |
| 16 | Worst attempts | a burst needing 7 attempts | worst_attempts = 7 while c_abandoned stays zero |
| 17 | Heartbeat present | SQE expected, transceiver healthy | heartbeat_seen after every frame (P13) |
| 18 | Heartbeat missing | SQE expected, none arrives | heartbeat_missing; detector_suspect sticky (P14) |
| 19 | SQE disabled correctly | SQE_EXPECTED = 0, attached to a repeater | no heartbeat expected; no suspicion raised |
| 20 | Spurious collision | collision asserted while idle | spurious_collision; detector suspect (P15) |
| 21 | Link pulses when idle | no traffic | a pulse every 16 ms (P16) |
| 22 | Pulses suppressed by traffic | continuous frames | no pulses; link stays up on data alone |
| 23 | One missed pulse | suppress a single pulse | link stays up (P17) |
| 24 | Link drops | silence past the timeout | link_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
Related tutorials
- Related topic
CSMA/CD, Collision Domains and Slot Time
Slot time is the parameter the whole half-duplex MAC hangs on: it bounds medium acquisition, bounds a collision fragment, and is the retransmission quantum. Deriving it from round-trip propagation plus jam is what fixes Ethernet's minimum frame size — a timing constant wearing a frame-format costume.
- Related topic
The Shared-Medium Problem
Why several independent transmitters on one medium is a distributed timing problem, not a formatting problem. Propagation delay makes every station's view of the medium stale, so two locally correct decisions can still collide — and that is the constraint the Ethernet MAC was built around.
- Related topic
Packet Switching
A circuit allocates capacity in advance and guarantees it; a packet network allocates on demand and guarantees nothing. The exchange is measurable in RTL — idle reserved slots against buffered, delayed and occasionally dropped packets — and it is why a packet must describe its own extent and destination.
- Related topic
From Coax to Twisted Pair to Switched Links
Coax, repeater, hub, bridge, switch — four steps, and only the last touched contention. A repeater reproduces a signal and cannot buffer, so it spends collision-domain budget and partitions nothing; a bridge holds the whole frame, and that buffer is what makes every other capability possible.
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.
