Ethernet · Module 9
100 Mbps — Fast Ethernet and the Switch Transition
4B/5B and MLT-3 got ten times the data through about three times the spectrum — but the switch was the larger change, because a collision domain of one made full duplex possible and left CSMA/CD unreached.
Chapter 9.1 built the original: Manchester coding, a shared coax, and a MAC whose every mechanism existed to arbitrate access to one conductor.
Fast Ethernet is remembered for being ten times faster. That was the smaller change.
The rate increase was real and it was engineered well — a new code got ten times the data through about three times the spectrum, which is the kind of result that looks impossible until you see the arithmetic. And it changed almost nothing about how Ethernet works.
The change that mattered happened at the same time and is usually described as a deployment detail: hubs were replaced by switches.
A hub is a repeater — Chapter 9.1's shared medium, in a box. A switch gives every station its own collision domain, and a collision domain with one station in it has no collisions. Which means the station can transmit and receive simultaneously, which means full duplex, which means CSMA/CD — carrier sense, collision detection, jam, backoff, slot time — is not used at all.
Everything Chapter 9.1 implemented became dead code on a switched port, and the frame format it produced did not change by a single bit.
1. Scope — What This Chapter Owns
Chapter 3.5 owns 4B/5B — the code, its DC balance, its control symbols. This chapter uses it and does not re-derive it; what it adds is the spectral consequence of pairing it with MLT-3.
Chapter 10.1 owns MII as an interface. This chapter mentions it only as the boundary that appeared.
Chapter 9.1 owns the shared-medium MAC, whose mechanisms this chapter shows becoming unused.
This chapter owns the transition: what the coding change bought, why the switch mattered more, how autonegotiation works and what it cannot guarantee, and the duplex mismatch that is its defining failure.
It does not own switching architecture — forwarding, learning and buffering are a switching subject. It owns the consequence of switching for the MAC, which is that half of the MAC stopped being used.
The question this chapter answers that its neighbours do not: what actually changed between 10 and 100 Mb/s, and why is the answer not "the speed"?
2. Ten Times the Data for Three Times the Spectrum
Two decisions in series, and each one does part of the work.
4B/5B replaces Manchester's factor of two with a factor of 1.25. Five symbols carry four bits, so 100 Mb/s of data becomes 125 Mbaud — and Chapter 3.5 established what the spare code space buys: enough transitions for clock recovery, plus control symbols that are not data.
MLT-3 then divides the frequency by four. Instead of two levels it uses three, and it advances through them in a cycle — so a run of ones walks +, 0, −, 0, +, … and a full electrical cycle takes four symbol periods rather than two.
Which gives the arithmetic:
100 Mb/s data
/ (4/5) -> 125 Mbaud
/ 4 -> 31.25 MHz fundamentalAgainst 10BASE-T's 10 MHz for 10 Mb/s. Ten times the data through 3.125 times the spectrum.
And that ratio is why 100BASE-TX ran on the cabling that was already installed. A tenfold spectrum increase would have needed better cable; a threefold one fitted inside what Category 5 already provided.
3. The Switch, and What It Made Unnecessary
A hub is a repeater. It takes a signal arriving on one port and reproduces it on every other, which makes every attached station part of one collision domain — Chapter 9.1's shared coax with a different physical arrangement and identical semantics.
A switch is not. It receives a frame, decides where it goes (Chapter 2.7's forwarding), and transmits it only there. Each port is its own collision domain, containing exactly one station.
And a collision domain with one station has no collisions. Not "few" — none, structurally, because a collision requires two transmitters and there is one.
From which everything follows:
| Mechanism | On a hub port | On a switch port |
|---|---|---|
| carrier sense | required before transmitting | unnecessary |
| collision detection | required throughout | nothing to detect |
| jam and backoff | required on collision | never invoked |
| slot time | constrains the segment's length | constrains nothing |
| the 64-octet floor | needed for detectability | still enforced (Chapter 5.6) |
| simultaneous transmit and receive | impossible | possible — full duplex |
Read the last two rows together, because they are the chapter's argument in miniature. The mechanism that produced the frame floor became unnecessary; the floor stayed. Chapter 5.6 §3 gave the reasons — frames cross links that do not share the property, and every receiver already enforces it — and this is the moment the divergence began.
And full duplex is the change that was worth having. A station that can transmit and receive simultaneously has, in effect, twice the capacity and no contention — and it never defers, never collides, and never backs off.
So the tenfold rate increase multiplied a link's capacity by ten, and the switch multiplied it again while removing the mechanism that had bounded a network's physical size since 1980. Chapter 1.2's slot time stopped constraining anything, and a segment's length became a question about signals rather than about round trips.
4. RTL 1 — Autonegotiation, Built From a Link Pulse
// SYNTHESIZABLE.
//
// Autonegotiation's arbitration: exchange capabilities in a fast link
// pulse burst and resolve them by priority.
//
// The FLP burst is Chapter 9.1's link pulse, multiplied:
//
// 33 pulse positions per burst
// 17 CLOCK positions, at 125 us spacing (+/- 14 us)
// 16 DATA positions, midway between clock pulses
// a pulse present in a data position is a 1; absent is a 0
// bursts repeat every 16 +/- 8 ms -- the same rate as a link pulse
//
// So a 10BASE-T partner sees pulses at the expected rate and brings its
// link up. It never learns what they encoded, and it does not need to.
package autoneg_pkg;
localparam int unsigned FLP_POSITIONS = 33;
localparam int unsigned FLP_DATA_BITS = 16;
localparam int unsigned CLOCK_SPACING_US = 125;
localparam int unsigned BURST_PERIOD_MS = 16;
// Technology bits within the 16-bit link code word, in priority order:
// higher speed wins, and at equal speed FULL duplex beats half.
typedef enum logic [2:0] {
TECH_10T_HALF,
TECH_10T_FULL,
TECH_100TX_HALF,
TECH_100TX_FULL,
TECH_NONE
} tech_e;
endpackage
module autoneg_arbitrator
import autoneg_pkg::*;
#(
parameter int unsigned CNT_W = 16
) (
input logic clk,
input logic rst_n,
// This end's advertised abilities, one bit per technology.
input logic [3:0] local_abilities,
// The partner's link code word, once a burst has been received.
input logic partner_valid,
input logic [15:0] partner_code_word,
// Parallel detection: the partner sent no FLP bursts at all, so its
// abilities are unknown and only its signalling can be observed.
input logic parallel_detect_valid,
input logic [1:0] parallel_detect_tech, // speed only
output logic resolved,
output tech_e resolution,
// TRUE when the resolution came from parallel detection, which cannot
// determine duplex. The single most important output of this module.
output logic duplex_assumed,
output logic [CNT_W-1:0] c_negotiations,
output logic [CNT_W-1:0] c_parallel_detects
);
// The partner's technology bits occupy a fixed field of the code word.
wire [3:0] partner_abilities = partner_code_word[8:5];
wire [3:0] common = local_abilities & partner_abilities;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
resolved <= 1'b0; resolution <= TECH_NONE; duplex_assumed <= 1'b0;
c_negotiations <= '0; c_parallel_detects <= '0;
end else begin
resolved <= 1'b0;
if (partner_valid) begin
// PRIORITY RESOLUTION. Highest common ability wins, and at equal
// speed full duplex outranks half -- because a full-duplex link
// is strictly better and both ends resolve the same table.
if (common[TECH_100TX_FULL]) resolution <= TECH_100TX_FULL;
else if (common[TECH_100TX_HALF]) resolution <= TECH_100TX_HALF;
else if (common[TECH_10T_FULL]) resolution <= TECH_10T_FULL;
else if (common[TECH_10T_HALF]) resolution <= TECH_10T_HALF;
else resolution <= TECH_NONE;
duplex_assumed <= 1'b0;
resolved <= 1'b1;
if (!(&c_negotiations)) c_negotiations <= c_negotiations + 1'b1;
end else if (parallel_detect_valid) begin
// PARALLEL DETECTION. The partner does not negotiate, so its
// duplex capability is UNKNOWABLE -- nothing in the signalling
// carries it. The standard's answer is to assume HALF, which is
// the safe choice against a partner that might collide.
//
// And that assumption is exactly how a duplex mismatch is born:
// a partner FORCED to full duplex sends no bursts, is parallel
// detected, and this end selects half.
resolution <= (parallel_detect_tech == 2'd1) ? TECH_100TX_HALF
: TECH_10T_HALF;
duplex_assumed <= 1'b1;
resolved <= 1'b1;
if (!(&c_parallel_detects)) c_parallel_detects <= c_parallel_detects + 1'b1;
end
end
end
endmoduleClassification: synthesizable.
What it teaches: that duplex_assumed is the output that matters and the one designs omit. A resolution reached by negotiation is an agreement; a resolution reached by parallel detection is a guess about duplex with the speed known — and the two are indistinguishable from resolution alone. A port whose duplex was assumed is a port one configuration away from a mismatch, and nothing else says so.
Deliberately simplified: four technologies and no next pages. Real autonegotiation carries an extensible next-page mechanism, which is how gigabit's master/slave preference (Chapter 9.3) is exchanged.
Production implication: the priority table must be identical at both ends, and nothing checks that it is. Each end resolves independently and there is no acknowledgement of the outcome — the protocol exchanges capabilities and never confirms conclusions. Two implementations with different priority tables will negotiate successfully and disagree, which is the same class of failure as Chapter 6.2's conventions: a shared mechanism with an unshared table.
5. The FLP Burst, and Why It Is Backward Compatible
The mechanism is a burst of pulses where 10BASE-T sent one, and the encoding is presence and absence.
Seventeen clock pulses, at 125 µs spacing with a ±14 µs tolerance, establish the timing. Sixteen data positions sit midway between consecutive clock pulses — at +62.5 µs — and a pulse present in a data position is a one while its absence is a zero.
Which gives a 16-bit link code word carrying a selector field, the technology abilities, a remote-fault indication, an acknowledge bit and a next-page bit.
And the whole thing repeats every 16 ± 8 ms — exactly the rate of a 10BASE-T link integrity pulse.
That last fact is the design. A 10BASE-T station attached to an autonegotiating partner sees pulses arriving at the rate it expects, concludes the link is up, and operates. It never decodes the burst, never knows a negotiation was attempted, and needs no changes whatsoever.
A new protocol, carried inside a signal the old device already understands, using only the fact that the old device counts pulses rather than interpreting them.
6. RTL 2 — Detecting a Duplex Mismatch
// SYNTHESIZABLE DIAGNOSTIC.
//
// Identifies a duplex mismatch from its signature, because nothing in
// the protocol reports one.
//
// The signature is ASYMMETRIC and that is what makes it identifiable:
//
// THE HALF-DUPLEX END defers, senses carrier, and collides -- because
// the full-duplex partner transmits whenever it likes, including
// while this end is transmitting. It sees collisions, and crucially
// LATE collisions (Chapter 9.1 §5), because the partner's frame can
// arrive at any point.
//
// THE FULL-DUPLEX END never collides, because it never looks. It sees
// the half-duplex end's jam and truncated transmissions as FCS
// errors and runts.
//
// So one end reports collisions and the other reports CRC errors, on the
// same link, at the same time. Neither symptom says "duplex" and both
// look like cabling.
module duplex_mismatch_detector
#(
parameter int unsigned CNT_W = 16,
parameter int unsigned WINDOW_BITS = 22
) (
input logic clk,
input logic rst_n,
input logic clear,
// This end's configuration.
input logic is_full_duplex,
input logic duplex_assumed, // from Section 4: parallel detected
// Local error events.
input logic collision,
input logic late_collision,
input logic fcs_error,
input logic runt_received,
input logic frame_done,
output logic mismatch_suspected,
output logic [1:0] suspected_role, // which end we are
output logic [CNT_W-1:0] c_suspicions,
output logic [CNT_W-1:0] win_collisions,
output logic [CNT_W-1:0] win_fcs_errors,
output logic [CNT_W-1:0] win_frames,
output logic evidence_valid,
output logic mismatch_ever_suspected
);
localparam logic [1:0] ROLE_UNKNOWN = 2'd0;
localparam logic [1:0] ROLE_HALF_END = 2'd1;
localparam logic [1:0] ROLE_FULL_END = 2'd2;
logic [WINDOW_BITS-1:0] win_q;
logic [CNT_W-1:0] col_q, late_q, fcs_q, runt_q, frm_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
win_q <= '0; col_q <= '0; late_q <= '0; fcs_q <= '0;
runt_q <= '0; frm_q <= '0;
mismatch_suspected <= 1'b0; suspected_role <= ROLE_UNKNOWN;
c_suspicions <= '0; win_collisions <= '0; win_fcs_errors <= '0;
win_frames <= '0; evidence_valid <= 1'b0;
mismatch_ever_suspected <= 1'b0;
end else begin
mismatch_suspected <= 1'b0;
evidence_valid <= 1'b0;
if (clear) begin
col_q <= '0; late_q <= '0; fcs_q <= '0; runt_q <= '0; frm_q <= '0;
c_suspicions <= '0;
// mismatch_ever_suspected deliberately survives.
end else begin
if (collision) col_q <= col_q + 1'b1;
if (late_collision) late_q <= late_q + 1'b1;
if (fcs_error) fcs_q <= fcs_q + 1'b1;
if (runt_received) runt_q <= runt_q + 1'b1;
if (frame_done) frm_q <= frm_q + 1'b1;
end
if (&win_q) begin
win_collisions <= col_q;
win_fcs_errors <= fcs_q;
win_frames <= frm_q;
evidence_valid <= 1'b1;
// THE HALF-DUPLEX END'S SIGNATURE. Late collisions are the key:
// ordinary contention produces collisions inside the slot time,
// and a full-duplex partner transmitting at will produces them
// at any point -- including after it.
if (!is_full_duplex && (late_q != '0) && (frm_q != '0)) begin
mismatch_suspected <= 1'b1;
suspected_role <= ROLE_HALF_END;
mismatch_ever_suspected <= 1'b1;
if (!(&c_suspicions)) c_suspicions <= c_suspicions + 1'b1;
end
// THE FULL-DUPLEX END'S SIGNATURE. This end never collides, so
// any collision indication is impossible -- and the partner's
// jam arrives as check-sequence errors and runts together.
// The COMBINATION is what distinguishes it from a bad cable,
// which produces check errors without runts in that proportion.
else if (is_full_duplex && (fcs_q != '0) && (runt_q != '0) &&
(fcs_q > (frm_q >> 6))) begin
mismatch_suspected <= 1'b1;
suspected_role <= ROLE_FULL_END;
mismatch_ever_suspected <= 1'b1;
if (!(&c_suspicions)) c_suspicions <= c_suspicions + 1'b1;
end
col_q <= '0; late_q <= '0; fcs_q <= '0; runt_q <= '0; frm_q <= '0;
win_q <= '0;
end else begin
win_q <= win_q + 1'b1;
end
end
end
endmoduleClassification: synthesizable diagnostic.
What it teaches: that the diagnosis needs evidence from both ends and each end can only see half of it. The half-duplex end sees collisions and cannot tell whether they are ordinary contention or a mismatch; the full-duplex end sees check-sequence errors and cannot tell whether they are a bad cable. Neither symptom names the cause, and the cause is obvious the moment the two are put side by side — which is why the module exports its window counts rather than only its verdict.
Deliberately simplified: fixed thresholds. A real detector correlates with load, because a mismatch's error rate rises with traffic in a way a cable fault's does not.
Production implication: late collisions are the strongest single indicator, and they are the reason Chapter 9.1 §5 separated them from ordinary ones. A full-duplex partner transmits whenever it wishes, including in the middle of this end's frame — so collisions arrive after the slot time, which cannot happen from ordinary contention on a conforming segment. A design that counts all collisions together throws away the one number that distinguishes a duplex mismatch from a busy segment.
7. RTL 3 — A Port That Can Behave As Either
// SYNTHESIZABLE.
//
// Models a port's behaviour under the two topologies, so a design can
// demonstrate to itself what changes.
//
// The point of writing both is that the MAC's code is IDENTICAL and only
// its enables differ:
//
// REPEATER (hub) port -- carrier sense active, collision detect
// active, half duplex, one collision domain shared with every
// other port.
// SWITCH port -- carrier sense and collision detect INERT,
// full duplex permitted, a collision domain of one.
//
// Nothing in the frame format, the CRC, the addressing or the padding
// changes between them. Half of Chapter 9.1's MAC simply stops being
// reached.
module hub_switch_port_model
import autoneg_pkg::*;
(
input logic clk,
input logic rst_n,
// 1 = this port is a switch port (own collision domain).
input logic is_switch_port,
input logic full_duplex_negotiated,
input logic tx_request,
input logic medium_busy, // carrier from the medium
input logic medium_collision,
output logic csma_enabled,
output logic full_duplex_active,
output logic tx_permitted,
output logic collision_possible,
// The count of mechanisms that are present in the RTL and never
// exercised on this port. Reported so the difference is visible rather
// than theoretical.
output logic [3:0] dormant_mechanisms
);
// Full duplex requires BOTH a switch port and a successful
// negotiation. A switch port with a half-duplex partner is still half
// duplex, and this is the conjunction designs get wrong by assuming a
// switch port implies full duplex.
assign full_duplex_active = is_switch_port && full_duplex_negotiated;
// CSMA/CD applies exactly when the port is NOT full duplex.
assign csma_enabled = !full_duplex_active;
assign collision_possible = csma_enabled;
// A full-duplex port transmits whenever it wants. A half-duplex port
// must defer -- which is Chapter 9.1's front end, still present and
// now conditional.
assign tx_permitted = tx_request && (full_duplex_active || !medium_busy);
always_comb begin
dormant_mechanisms = 4'd0;
if (full_duplex_active) begin
// Carrier sense, collision detect, jam/backoff, and the slot-time
// constraint. All four are in the RTL and none is reachable.
dormant_mechanisms = 4'd4;
end
end
// A collision on a full-duplex port is not a rare event, it is an
// impossible one -- so its occurrence means the two ends disagree
// about duplex (Section 6), not that a collision happened.
// synopsys translate_off
always_ff @(posedge clk) begin
if (rst_n && full_duplex_active && medium_collision)
$error("collision indicated on a full-duplex port -- the partner is half duplex");
end
// synopsys translate_on
endmoduleClassification: synthesizable.
What it teaches: that full_duplex_active is a conjunction and treating a switch port as implying full duplex is wrong. A switch port whose partner negotiated half duplex — or was parallel detected — is half duplex, and CSMA/CD is live on it. The port type permits full duplex; the negotiation decides it.
Deliberately simplified: the two topologies are modelled by enables rather than by separate designs, which is exactly the historical situation — the same MAC silicon served both, with mechanisms that stopped being reached.
Production implication: dormant_mechanisms makes a fact visible that is otherwise invisible: on a full-duplex port, four mechanisms are present in the netlist and unreachable. They cost area and they are never exercised — so they are also never verified in deployment, and a port that later negotiates half duplex activates logic that has not run since bring-up. A design that reports its dormant mechanisms knows which of its own paths are cold.
8. RTL 4 — Switching Duplex Safely
// SYNTHESIZABLE.
//
// Changes a port between half and full duplex, and the interlock is the
// module.
//
// A duplex change alters whether the MAC defers, whether it detects
// collisions, and whether it may transmit while receiving. Changing it
// mid-frame produces a frame that was half-duplex when it started and
// full-duplex when it ended -- which is not a frame either mode would
// have produced.
//
// So the change is deferred to a quiescent point, and the module refuses
// rather than queues if quiescence never arrives.
module duplex_mode_switch
(
input logic clk,
input logic rst_n,
input logic req_valid,
input logic req_full_duplex,
input logic tx_active,
input logic rx_active,
input logic collision_active,
output logic full_duplex,
output logic change_applied,
output logic change_pending,
// The request could not be applied within the bound. Reported rather
// than held indefinitely, because a pending change nobody knows about
// is worse than a refused one.
output logic change_timed_out,
output logic [15:0] c_changes,
output logic [15:0] c_timeouts
);
logic pending_q;
logic target_q;
logic [15:0] wait_q;
localparam int unsigned QUIESCE_TIMEOUT = 16'd50_000;
assign change_pending = pending_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
full_duplex <= 1'b0; pending_q <= 1'b0; target_q <= 1'b0;
wait_q <= '0; change_applied <= 1'b0; change_timed_out <= 1'b0;
c_changes <= '0; c_timeouts <= '0;
end else begin
change_applied <= 1'b0;
change_timed_out <= 1'b0;
if (req_valid && !pending_q) begin
pending_q <= 1'b1;
target_q <= req_full_duplex;
wait_q <= '0;
end else if (pending_q) begin
// THE INTERLOCK. Quiescent means no frame in either direction and
// no collision in progress. A duplex change mid-frame produces a
// frame that is neither mode's.
if (!tx_active && !rx_active && !collision_active) begin
full_duplex <= target_q;
pending_q <= 1'b0;
change_applied <= 1'b1;
if (!(&c_changes)) c_changes <= c_changes + 1'b1;
end else if (wait_q == QUIESCE_TIMEOUT) begin
// Refuse rather than hold. A pending change that never applies
// leaves the port in a state nobody asked for and nothing
// reports.
pending_q <= 1'b0;
change_timed_out <= 1'b1;
if (!(&c_timeouts)) c_timeouts <= c_timeouts + 1'b1;
end else begin
wait_q <= wait_q + 1'b1;
end
end
end
end
endmoduleClassification: synthesizable.
What it teaches: that a refused change is better than an indefinitely pending one. A port with a duplex change waiting forever is in the old mode while management believes it is in the new one — and nothing reports the discrepancy, because the request was accepted. change_timed_out converts a silent divergence into a visible failure.
Deliberately simplified: a single quiescence condition. A real port also has queued frames and a link state to consider, and the correct point is usually a link-down transition rather than a gap between frames.
Production implication: the interlock exists because a duplex change alters whether the MAC defers — and a frame that started under deferral and finished without it is not a frame either mode produces. On a link that is already mismatched, the natural remedy is to change duplex, and doing so while traffic flows produces one more malformed frame at exactly the moment somebody is watching the counters.
9. RTL 5 — Handling a Partner That Does Not Negotiate
// SYNTHESIZABLE.
//
// Parallel detection: determine what a non-negotiating partner is, from
// its signalling alone.
//
// What is observable and what is not:
//
// OBSERVABLE -- the speed. A 10BASE-T partner sends link pulses; a
// 100BASE-TX partner sends idle symbols continuously. The two are
// entirely different signals.
// NOT OBSERVABLE -- the duplex. Nothing in either idle signalling says
// how the partner intends to behave when it transmits.
//
// So this module reports the speed with confidence and the duplex as an
// ASSUMPTION, and the distinction is carried in a separate output rather
// than folded into the result.
module parallel_detection_handler
import autoneg_pkg::*;
#(
parameter int unsigned DETECT_MS = 100,
parameter int unsigned CYCLES_PER_MS = 25_000,
parameter int unsigned CNT_W = 16
) (
input logic clk,
input logic rst_n,
input logic flp_burst_seen, // partner negotiates
input logic link_pulse_seen, // 10BASE-T signalling
input logic idle_symbols_seen, // 100BASE-TX signalling
output logic detect_valid,
output logic [1:0] detected_speed, // 0 = 10, 1 = 100
output logic duplex_is_assumed,
output logic partner_negotiates,
output logic [CNT_W-1:0] c_parallel_detections,
// Sticky: a port that has EVER parallel detected is a port whose
// duplex was never agreed, and that fact outlives the link.
output logic ever_parallel_detected
);
logic [31:0] timer_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
timer_q <= '0; detect_valid <= 1'b0; detected_speed <= 2'd0;
duplex_is_assumed <= 1'b0; partner_negotiates <= 1'b0;
c_parallel_detections <= '0; ever_parallel_detected <= 1'b0;
end else begin
detect_valid <= 1'b0;
if (flp_burst_seen) begin
// The partner negotiates. Parallel detection is not used, and
// the duplex will be AGREED rather than assumed.
partner_negotiates <= 1'b1;
duplex_is_assumed <= 1'b0;
timer_q <= '0;
end else if (link_pulse_seen || idle_symbols_seen) begin
if (timer_q >= 32'(DETECT_MS * CYCLES_PER_MS)) begin
// Long enough with signalling and no bursts: the partner does
// not negotiate.
detect_valid <= 1'b1;
detected_speed <= idle_symbols_seen ? 2'd1 : 2'd0;
// THE ASSUMPTION. Half duplex, because duplex is unobservable
// and half is the choice that is safe against a partner which
// might collide.
duplex_is_assumed <= 1'b1;
partner_negotiates <= 1'b0;
ever_parallel_detected <= 1'b1;
if (!(&c_parallel_detections))
c_parallel_detections <= c_parallel_detections + 1'b1;
timer_q <= '0;
end else begin
timer_q <= timer_q + 1'b1;
end
end else begin
timer_q <= '0;
end
end
end
endmoduleClassification: synthesizable.
What it teaches: that the detector's confidence differs between its two outputs and the interface says so. detected_speed is a measurement — the two signallings are entirely different and distinguishing them is easy. duplex_is_assumed is a default, chosen for safety, with no evidence behind it at all. Folding them into one result would present a guess and a measurement as the same kind of thing.
Deliberately simplified: a fixed detection window. Real parallel detection interacts with the autonegotiation state machine's own timers, and the interaction is where a partner that negotiates slowly gets misdetected as one that does not.
Production implication: ever_parallel_detected is sticky because a port whose duplex was never agreed is permanently a mismatch candidate, and that remains true after the link bounces and comes up looking normal. The most useful thing a design can report about a working link is that its duplex was assumed rather than negotiated — because the link works, the counters are clean, and the mismatch appears only under load.
10. The Mismatch, End to End
Follow one frame through a mismatched link and the asymmetry appears immediately.
The half-duplex end begins transmitting. Partway through, the full-duplex partner starts a frame of its own — it has no reason not to; it never senses carrier. The half-duplex end detects the incoming energy as a collision, jams, and backs off.
And the collision arrives at an arbitrary point, which means it frequently arrives after the slot time — a late collision, which Chapter 9.1 §5 established cannot happen from ordinary contention on a conforming segment.
The full-duplex end sees none of this. It never collides, because it never looks. What it sees is the half-duplex end's frame stopping partway through and being replaced by a jam — which arrives as a truncated frame with a bad check sequence, or as a runt.
| End | Reports | Resembles |
|---|---|---|
| half duplex | collisions, late collisions, deferrals | a busy or over-length segment |
| full duplex | FCS errors, runts, no collisions at all | a bad cable |
Neither symptom says "duplex", and each has a plausible wrong explanation.
The tells are two, and both need the pair. First, collisions at one end and none at the other — on a genuinely shared segment both ends collide. Second, late collisions, which point at a physical impossibility that a mismatch explains and contention does not.
And the failure is load-dependent, which is why it survives commissioning. At low traffic the two ends rarely transmit simultaneously, so the link tests clean, passes acceptance, and degrades under production load — appearing weeks later as an intermittent performance problem with error counters that point at the cable.
11. Properties Worth Asserting, and One Worth Refusing
The properties below split into three groups, and the split is the lesson. Some are structural — they follow from the resolution table or the enable logic and hold unconditionally. Some are interlocks — they say a change happened only where it was safe. And a few are honesty properties, which assert that the design reports what it does not know.
Autonegotiation resolution
// P1. Resolution is a pulse. A resolution that stays asserted turns one
// negotiation into a stream of them downstream.
property p_resolved_is_pulse;
@(posedge clk) disable iff (!rst_n)
resolved |=> !resolved;
endproperty
a_resolved_is_pulse: assert property (p_resolved_is_pulse);
// P2. Resolution requires evidence. Neither source of evidence, no
// resolution -- the arbiter never invents a technology.
property p_resolution_needs_evidence;
@(posedge clk) disable iff (!rst_n)
resolved |-> $past(partner_valid) || $past(parallel_detect_valid);
endproperty
a_resolution_needs_evidence: assert property (p_resolution_needs_evidence);
// P3. Priority: if both ends can do 100 full, that is what is selected.
// This is the whole resolution table, in the one case that matters.
property p_priority_100_full;
@(posedge clk) disable iff (!rst_n)
(partner_valid && local_abilities[TECH_100TX_FULL] &&
partner_code_word[8:5][TECH_100TX_FULL])
|=> (resolution == TECH_100TX_FULL);
endproperty
a_priority_100_full: assert property (p_priority_100_full);
// P4. THE HONESTY PROPERTY. Parallel detection can never yield a
// resolution that claims full duplex, because duplex is not observable.
// A design that parallel detects into a full-duplex result has inferred
// something from nothing.
property p_parallel_detect_never_full;
@(posedge clk) disable iff (!rst_n)
(resolved && duplex_assumed)
|-> (resolution == TECH_100TX_HALF) || (resolution == TECH_10T_HALF);
endproperty
a_parallel_detect_never_full: assert property (p_parallel_detect_never_full);
// P5. And the converse: a negotiated resolution is never marked assumed.
property p_negotiated_not_assumed;
@(posedge clk) disable iff (!rst_n)
(resolved && $past(partner_valid)) |-> !duplex_assumed;
endproperty
a_negotiated_not_assumed: assert property (p_negotiated_not_assumed);P4 is the property this chapter exists to state. It does not say the resolution is correct — it says the design does not claim to know something it cannot observe. A parallel-detected link that comes up full duplex has not made a lucky guess; it has made an unfounded one.
The port model
// P6. Full duplex requires BOTH conditions. The conjunction, asserted,
// because assuming a switch port implies full duplex is the common bug.
property p_full_duplex_conjunction;
@(posedge clk) disable iff (!rst_n)
full_duplex_active == (is_switch_port && full_duplex_negotiated);
endproperty
a_full_duplex_conjunction: assert property (p_full_duplex_conjunction);
// P7. CSMA/CD is exactly the complement. There is no third mode: a port
// either arbitrates for a shared medium or it does not.
property p_csma_is_complement;
@(posedge clk) disable iff (!rst_n)
csma_enabled == !full_duplex_active;
endproperty
a_csma_is_complement: assert property (p_csma_is_complement);
// P8. A full-duplex port transmits on request, unconditionally. No
// deferral, ever -- which is the mechanism's entire absence, in one line.
property p_full_duplex_never_defers;
@(posedge clk) disable iff (!rst_n)
(full_duplex_active && tx_request) |-> tx_permitted;
endproperty
a_full_duplex_never_defers: assert property (p_full_duplex_never_defers);
// P9. A half-duplex port defers to carrier. Chapter 9.1's rule, still
// enforced, on the ports that still reach it.
property p_half_duplex_defers;
@(posedge clk) disable iff (!rst_n)
(!full_duplex_active && medium_busy) |-> !tx_permitted;
endproperty
a_half_duplex_defers: assert property (p_half_duplex_defers);The duplex switch interlock
// P10. THE INTERLOCK. A duplex change only ever lands in a quiescent
// cycle. A mid-frame change produces a frame that is neither mode's.
property p_change_only_when_quiet;
@(posedge clk) disable iff (!rst_n)
change_applied |-> (!tx_active && !rx_active && !collision_active);
endproperty
a_change_only_when_quiet: assert property (p_change_only_when_quiet);
// P11. Duplex is stable except at an applied change. Nothing else may
// move it -- a duplex that changes without a change event is a decode
// bug that will be diagnosed as a link problem.
property p_duplex_stable_between_changes;
@(posedge clk) disable iff (!rst_n)
!change_applied |=> $stable(full_duplex);
endproperty
a_duplex_stable: assert property (p_duplex_stable_between_changes);
// P12. Every request terminates: applied or timed out, never neither.
// This is the property that makes "pending" a bounded state.
property p_request_terminates;
@(posedge clk) disable iff (!rst_n)
(req_valid && !change_pending)
|=> ##[1:$] (change_applied || change_timed_out);
endproperty
a_request_terminates: assert property (p_request_terminates);
// P13. Applied and timed out are mutually exclusive -- one outcome per
// request, so the counters partition rather than overlap.
property p_outcomes_exclusive;
@(posedge clk) disable iff (!rst_n)
!(change_applied && change_timed_out);
endproperty
a_outcomes_exclusive: assert property (p_outcomes_exclusive);Parallel detection and mismatch evidence
// P14. An FLP burst suppresses parallel detection entirely. A partner
// that negotiates is never guessed about.
property p_flp_suppresses_detection;
@(posedge clk) disable iff (!rst_n)
flp_burst_seen |=> !detect_valid;
endproperty
a_flp_suppresses_detection: assert property (p_flp_suppresses_detection);
// P15. Detection always assumes duplex. There is no path by which this
// module reports a detected duplex, because there is no such signal.
property p_detection_always_assumes;
@(posedge clk) disable iff (!rst_n)
detect_valid |-> duplex_is_assumed;
endproperty
a_detection_always_assumes: assert property (p_detection_always_assumes);
// P16. The sticky flag survives. A port that ever parallel detected
// stays flagged across link bounces -- the fact outlives the link.
property p_sticky_survives;
@(posedge clk) disable iff (!rst_n)
ever_parallel_detected |=> ever_parallel_detected;
endproperty
a_sticky_survives: assert property (p_sticky_survives);
// P17. A suspicion is always backed by a full window of evidence. A
// mismatch called on three frames is noise given a name.
property p_suspicion_has_evidence;
@(posedge clk) disable iff (!rst_n)
mismatch_suspected |-> evidence_valid;
endproperty
a_suspicion_has_evidence: assert property (p_suspicion_has_evidence);12. Verification Scenarios
Group the scenarios by what they are trying to break, because a duplex bug hides in the cases nobody generates.
Negotiation and resolution
- Both ends 100 full capable — resolution is
TECH_100TX_FULL,duplex_assumedlow,c_negotiationsincrements once. - Both ends 10 half only — resolution is
TECH_10T_HALF, reached by negotiation,duplex_assumedlow. Confirms that half duplex negotiated is not the same as half duplex assumed. - No common ability — resolution is
TECH_NONE,resolvedstill pulses. The arbiter reports a failed negotiation rather than staying silent. - Local advertises 100 full, partner advertises 100 half — resolution is
TECH_100TX_HALF. The speed matches, the duplex is the lower of the two, and CSMA/CD is live on a 100 Mbps link. - Partner code word changes between bursts — the resolution follows the most recent burst. A partner that is reconfiguring produces a sequence of resolutions, and each is a legitimate one.
partner_validandparallel_detect_validasserted in the same cycle — negotiation wins,duplex_assumedstays low. Evidence beats assumption whenever both exist.resolvedpulse width — exactly one cycle under back-to-backpartner_valid. P1 under load.
Parallel detection
- Partner sends link pulses only, for the full detection window —
detect_valid, speed 10,duplex_is_assumedhigh. - Partner sends 100BASE-TX idle symbols only —
detect_valid, speed 100,duplex_is_assumedhigh. The speed is a measurement; the duplex is not. - An FLP burst arrives at cycle
DETECT_MS × CYCLES_PER_MS − 1— detection is suppressed. The boundary case where a slow negotiator is one cycle from being misjudged. - Signalling drops mid-window and returns — the timer restarts. Partial evidence does not accumulate across a gap.
- Link bounces after a parallel detection —
ever_parallel_detectedstays high. The port remains flagged although the link now looks new. - Alternating link pulses and idle symbols — the module must not oscillate between speeds within one window.
Port model and duplex switching
- Switch port, half-duplex negotiated —
full_duplex_activelow,csma_enabledhigh,dormant_mechanismszero. The case that disproves "switch port means full duplex." - Repeater port, full-duplex negotiated —
full_duplex_activelow. The port type vetoes the negotiation. - Full-duplex port,
tx_requestwhilemedium_busy—tx_permittedhigh. No deferral. - Half-duplex port,
tx_requestwhilemedium_busy—tx_permittedlow. Deferral, exactly as Chapter 9.1. - Duplex change requested mid-frame —
change_pendingholds,change_appliedstays low untiltx_activeandrx_activeboth fall. - Duplex change requested during a collision — held. A collision is a busy medium even though no frame is completing.
- Traffic that never quiesces for
QUIESCE_TIMEOUTcycles —change_timed_outpulses once,change_pendingclears,c_timeoutsincrements. The refusal path. - Second request while one is pending — ignored, not queued. One outstanding change at a time.
- Request for the duplex already in force — applies at the next quiescent point and counts. A no-op change is still a change event, and suppressing it would make the counter lie.
Mismatch evidence
- Half-duplex end under mismatch — collisions and late collisions accumulate,
fcs_errordoes not,suspected_rolereports the half-duplex side. - Full-duplex end under mismatch —
fcs_errorandrunt_receivedaccumulate, collisions stay at zero,suspected_rolereports the full-duplex side. - Evidence window closes with three frames in it —
evidence_validlow andmismatch_suspectedstays low. A rate computed over three frames is not a rate. clearasserted while a suspicion stands — the window counters clear,mismatch_ever_suspectedsurvives. The sticky flag is what a later reader needs.- A genuinely shared half-duplex segment under heavy load — collisions at both ends, no late collisions, no suspicion raised. The false-positive case that separates contention from mismatch.
13. The Cable Was the Constraint
Section 2 established that 100BASE-TX needs a 31.25 MHz fundamental. That number is why Fast Ethernet could not simply be deployed on the cable already in the walls.
Category 3 cable is characterised to 16 MHz. Category 5 is characterised to 100 MHz.
| Cat 3 | Cat 5 | |
|---|---|---|
| characterised bandwidth | 16 MHz | 100 MHz |
| 10BASE-T (10 MHz fundamental) | works | works |
| 100BASE-TX (31.25 MHz fundamental) | outside the rating | works |
So MLT-3 and 4B/5B together — the compression that took ten times the data into 3.125 times the spectrum — still landed roughly twice above what Cat 3 is specified to carry. The line code was not a way to avoid recabling; it was what made 100 Mbps reachable on a cable category that could be manufactured at all.
And the committee did try to avoid the recabling, which is the part worth knowing. 100BASE-T4 ran Fast Ethernet over four pairs of Cat 3, using 8B6T ternary coding: eight bits map to six ternary symbols, three pairs carry data in the transmit direction at
100 Mb/s ÷ 3 pairs = 33.33 Mb/s per pair, and 33.33 × 6/8 = 25 Mbaud per pair
— 25 Mbaud, comfortably inside Cat 3's rating.
And it is not the variant anyone deployed, for a structural reason. T4 uses three pairs in whichever direction is transmitting, which means the direction has to be switched — so T4 is half duplex only. It solved the cabling problem and forfeited the change that mattered.
14. Debugging: Reading the Two Signatures
A duplex mismatch is diagnosed by comparing two ends, and almost every wrong diagnosis comes from looking at one.
| Observation | Likely cause | The distinguishing check |
|---|---|---|
| collisions at one end, none at the other | duplex mismatch | the definitive tell; a shared segment collides at both ends |
| collisions at both ends, no late collisions | ordinary contention on a half-duplex segment | load correlates; performance degrades gracefully |
| late collisions on a conforming segment | duplex mismatch, or a segment over the round-trip budget | check the partner's collision count — zero means mismatch |
| FCS errors and runts, zero collisions, full duplex | the partner is half duplex and jamming | check the partner for collisions |
| FCS errors at both ends, no collisions | genuine cabling or connector fault | symmetric, and unrelated to load |
| clean at low load, degrades under load | mismatch, near-certainly | the symptom is simultaneity, which only load produces |
| link up, speed correct, throughput a fraction of it | mismatch | look for ever_parallel_detected before looking at the cable |
Three habits make this tractable.
First, always read both ends. A duplex mismatch is the canonical fault where each end's evidence is individually misleading and the pair is unambiguous.
Second, treat a late collision as a claim about physics. Chapter 9.1 §5 established that a late collision cannot arise from contention on a conforming segment. When one appears, either the segment violates its round-trip budget or the partner is not doing CSMA/CD at all — and the second is far more common on a switched link.
Third, look for the assumption before looking for the fault. ever_parallel_detected and duplex_assumed are one register read, and they identify a mismatch candidate before any traffic has run. A link whose duplex was never negotiated is a link nobody agreed about.
15. Common Misconceptions
"Fast Ethernet was a tenfold speed increase."
The wrong model: the same Ethernet, ten times faster.
What it costs: you cannot explain why full duplex arrived at the same moment, why CSMA/CD stopped mattering, or why a network's maximum physical size stopped being a function of its bit rate.
The corrected model: two independent changes shipped together and the smaller one got the name. The rate went up ten times. Simultaneously, hubs were replaced by switches — and a switch port is a collision domain of one, which makes full duplex possible and leaves Chapter 1.2's entire arbitration mechanism present in the silicon and never reached.
"A switch port is full duplex."
The wrong model: switch port implies full duplex.
What it costs: Section 7's full_duplex_active is a conjunction, and treating it as one term is exactly the bug. A switch port whose partner negotiated half — or was parallel detected — is half duplex, with CSMA/CD live on it.
The corrected model: the port type permits full duplex; the negotiation decides it. And the most common way a switch port ends up half duplex is a partner that does not negotiate at all, which the port then assumes about.
"Autonegotiation determines the duplex."
The wrong model: both ends negotiate, so both ends know the duplex.
What it costs: the entire duplex-mismatch failure becomes inexplicable — if the protocol determines duplex, mismatches should not exist.
The corrected model: autonegotiation determines duplex only when both ends negotiate. When one end is forced, it sends no bursts, the other end parallel detects, and parallel detection cannot observe duplex — nothing in either signalling carries it. So it assumes half, and a link comes up at the right speed with opposite duplex at each end.
"A duplex mismatch shows up as a broken link."
The wrong model: a serious configuration error produces a serious, obvious symptom.
What it costs: the fault passes commissioning cleanly and appears weeks later as an intermittent performance problem whose error counters point at the cable.
The corrected model: both ends report link up, at the correct speed, with clean counters at low load. The symptom requires simultaneous transmission, which requires load — and the symptoms that then appear are asymmetric, so each end alone tells a plausible and wrong physical-layer story.
"MLT-3 is how Fast Ethernet gets its speed."
The wrong model: one clever line code did all the work.
What it costs: you conflate two separate decisions and cannot reason about later generations, where the same two decisions are made independently again.
The corrected model: 4B/5B and MLT-3 solve different problems. 4B/5B is a block code guaranteeing transition density and control symbols, at 25% overhead — hence 125 Mbaud. MLT-3 is a modulation that cycles through three levels so a full electrical cycle spans four symbol periods, putting the fundamental at 31.25 MHz. Coding and modulation are independent choices, and Section 13 shows what happens when you vary them: 100BASE-T4 chose differently and got Cat 3 compatibility at the price of full duplex.
16. Interview Reasoning
"What was the most important change in Fast Ethernet?"
The weak answer is the tenfold rate. The answer that ends the topic is that the rate was the smaller change and the switch was the larger one: a switch port is a collision domain of one, which makes full duplex possible — and a full-duplex station never defers, never collides, and never backs off, so Chapter 1.2's arbitration is present in the design and unreachable. The rate multiplied a link's capacity by ten; the switch multiplied it again and removed the constraint that had bounded a network's physical extent since 1980. The extra credit is what did not change: the frame format, the 64-octet floor, and the interframe gap, none of which had a reason left.
"How does 100BASE-TX get 100 Mbps through about 31 MHz?"
Two independent steps, and the strong answer separates them. 4B/5B maps four data bits to five line bits, so 100 Mb/s becomes 125 Mbaud — 25% overhead, against Manchester's 100%, in exchange for a receiver that must hold timing across several bit times. MLT-3 then cycles through three levels, so a complete electrical cycle takes four symbol periods: 125 ÷ 4 = 31.25 MHz fundamental. Ten times the data for 3.125 times the spectrum — and the finishing observation is that the two decisions are separable, which is why 100BASE-T4 could pick a different pair and land inside Cat 3's rating.
"What is a duplex mismatch and how would you diagnose it?"
One end full duplex, one end half, both reporting link up at the same speed. The strong answer gives the mechanism: it arises when one end is forced — a forced port sends no FLP bursts — so the other end parallel detects, which resolves speed by observation and duplex by assumption, and the assumption is half. The diagnosis is the asymmetry: the half-duplex end reports collisions, including late collisions; the full-duplex end reports FCS errors and runts and zero collisions. Collisions at one end and none at the other is the tell, and it needs both ends — each alone looks like a cabling fault. The finishing point: it is load-dependent, so it passes acceptance and fails in production.
"Would you assert that both ends of a link agree on duplex?"
No, and the reason is stronger than "it is hard to observe." No mechanism in the protocol establishes it. Autonegotiation exchanges capabilities, each end resolves its own priority table independently, and there is no acknowledgement of the conclusion — and when one end does not negotiate, the other assumes. So the property has no cause, not merely no witness: it is false on a correctly built link with a forced partner. Assert instead that the design reports duplex as assumed whenever it is assumed — a property of this device, unconditionally true, and the one that catches every mismatch in the chapter.
17. Understanding Check
Because the rate made a link faster and the switch changed what a link is.
A hub is a repeater. Every port's traffic reaches every other port, so all ports share one collision domain — and every station must arbitrate for it, exactly as Chapter 1.2 describes. Ten times the rate makes that arbitration faster; it does not remove it.
A switch forwards. Each port is its own collision domain, containing exactly two devices — and with only one possible transmitter at each end of a dedicated pair, there is nothing to collide with. Which makes full duplex possible: both ends transmit whenever they like.
And a full-duplex station never defers, never senses carrier, never collides and never backs off. CSMA/CD is still in the silicon and is never reached — which is why Section 7 reports dormant_mechanisms, and why Chapter 5.6's frame floor outlived its own justification.
The arithmetic: the rate multiplied a link's capacity by ten. The switch multiplied it again — and removed the slot time's constraint on a network's physical size, which had bounded Ethernet since 1980.
18. What's Next
The claim this chapter defended: the tenfold rate increase was the smaller of Fast Ethernet's two changes.
The rate was engineered well — 4B/5B took 100 Mb/s to 125 Mbaud at 25% overhead, and MLT-3's four-symbol electrical cycle put the fundamental at 31.25 MHz, so ten times the data crossed about three times the spectrum. And it changed nothing about how Ethernet works.
The switch did. A switch port is a collision domain of one, which makes full duplex possible — and a full-duplex station never defers, never senses carrier, never collides and never backs off. CSMA/CD stayed in the silicon and stopped being reached, which is why the frame floor and the interframe gap now outlive the reasons that produced them.
Autonegotiation made the transition survivable by hiding a new protocol inside a signal older devices already counted. And it paid for that compatibility in the one dimension its trick could not carry: a partner that does not negotiate cannot be asked what duplex it intends, so the other end assumes — which is the duplex mismatch, a fault that reports link up at the correct speed, tests clean at commissioning, and presents each end with a plausible and wrong physical-layer story.
Chapter 9.3 — 1 Gigabit: Four Pairs, Echo Cancellation and GMII takes the next step, and its argument is that gigabit stopped trying to make the channel clean and started cancelling what it could not avoid.
1000BASE-T uses all four pairs simultaneously, in both directions at once — which means each receiver hears its own transmitter's echo and its neighbours' crosstalk, continuously, and must subtract them. The chapter covers the adaptive loops that do it, the master/slave timing relationship that makes the subtraction possible, the PAM-5 signalling that carries two bits per pair per symbol, and why asserting that an adaptive loop converges is the wrong property to write.
The full path is on the Ethernet curriculum index.
Continue learning
Related tutorials
- Related topic
Full Duplex and What It Removed from the MAC
Full duplex makes five of the half-duplex MAC's six blocks unreachable and collapses the transmit state machine from five states to two. It also removes a constraint that had been throttling senders by accident, which is why link-level flow control had to be invented to replace it.
- Related topic
Negotiation Failures and Duplex Mismatch
Seven ways a link comes up wrong and six of them report no error, because every device behaved correctly. Diagnosis is set narrowing over evidence, and three causes cannot be seen from one end at all.
- 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
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.
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.
