Ethernet · Module 25
"VLANs Improve Performance Automatically"
Segmentation moves no bits: the same 6.4 Tb/s before and after, a trunk carrying 320 broadcasts a second at every VLAN count, and a tag that costs 4.76% of the wire.
Chapter 13.1 said what VLANs are for and Chapter 13.4 built the datapath. Neither chapter claimed a bandwidth improvement, and this one derives why no such claim is available.
The myth, stated as the people who hold it would state it. A flat network is slow because every station hears every other. Dividing it into VLANs cuts the traffic each station sees, so each VLAN gets a share of the switch and the network goes faster.
The first clause is true, the second is true about one specific quantity, and the third does not follow from either.
The kill is conservation and it fits on one line.
a switch with aggregate capacity C, offered load L
divide its ports into V VLANs
the capacity is still C and the offered load is still L
Δ throughput = 0And the tag is not free.
| Wire slot | Payload efficiency | The tag's cost | |
|---|---|---|---|
| 64-octet frame, untagged | 84 octets | 54.76% | — |
| 64-octet frame, tagged | 88 octets | 52.27% | 4.762% of the wire |
| 1 518-octet frame, tagged | 1 542 octets | 97.28% | 0.260% |
| 9 018-octet frame, tagged | 9 042 octets | 99.54% | 0.044% |
The only effect a VLAN has on bandwidth is negative, and it is 4.76% of the wire at minimum frame size — which is Chapter 24.3 §1's framing-tax arithmetic with four more octets in it.
What a VLAN genuinely does change is Chapter 12.4's flood width, and that change is large. Sections 4 and 6 derive it, Section 8 finds where it stops helping, and Section 10 finds the case it makes worse.
1. Scope — Conservation, and the One Quantity That Moves
This chapter owns four derivations.
| What is derived | |
|---|---|
| Sections 2 to 3 | conservation: Δ throughput is zero, and the tag costs 4.76% at minimum frame size |
| Sections 4 to 7 | flood width and domain rate at 64 ports — 63 copies against 7, and 315 broadcasts per second against 35 |
| Sections 8 to 9 | the trunk, which carries every VLAN's broadcasts and is unchanged by segmentation |
| Sections 10 to 13 | three cases where VLANs make throughput worse, priced |
What this chapter does not own. Chapter 13.1 owns why VLANs exist; Chapter 13.2 owns the four octets and their fields; Chapter 13.3 owns access and trunk port behaviour; Chapter 13.4 owns the datapath and its cost at a 24-port switch. This chapter uses all four, re-derives none, and scales Chapter 13.4 §8's key-widening result to Chapter 23.3's table rather than repeating it at 8 192 entries.
2. Conservation, Stated as Arithmetic
The myth's central claim is that dividing a switch gives each part more. Write the conservation law down and it disappears.
Let the switch have N ports each at rate R, so aggregate capacity is C = N × R. Divide the ports into V VLANs of N ÷ V ports each.
| Before | After | |
|---|---|---|
| ports | N | N |
| port rate | R | R |
| aggregate capacity | N × R | N × R |
| offered load | L | L |
| fabric bandwidth | unchanged | unchanged |
| buffer | unchanged | unchanged, and now partitioned by policy |
Nothing in the second column differs from the first, and that is the whole of it. A VLAN is a membership relation evaluated during forwarding — Chapter 13.4 §6's key construction — and evaluating a relation does not create a link.
Two objections are worth answering because they are the ones people raise.
Objection one: "but each VLAN now has the switch to itself." It does not. Chapter 23.3 §9's packet buffer is shared and dynamically thresholded, the fabric is shared, and the egress scheduler serves all queues. A VLAN is not a partition of the hardware; it is a constraint on which egress ports a frame may reach.
Objection two: "but the traffic is more local now." Segmenting does not move a station. If two stations that talked to each other end up in different VLANs, their traffic now goes through a router, which is strictly more hops and strictly more latency. Segmentation can make the traffic pattern worse and never makes it better, because it does not choose who talks to whom.
3. RTL 1 — The VLAN Package and the Tag Cost Model
// ---------------------------------------------------------------------
// vlanperf_pkg -- the constants a conservation argument needs, with
// every derived figure computed here rather than written as a literal.
//
// Unit: Chapter 23.3 Section 2's bitcell equivalent.
// 1 BCE = 0.35 GE = one bit of usable on-die SRAM
// 1 flip-flop = 20 BCE
// Chapter 19.7 Section 19's MAC receive datapath = 283 320 BCE
// Chapter 23.3's 64-port switch = 5.62e8 BCE, table 128k x 96 b
// ---------------------------------------------------------------------
package vlanperf_pkg;
localparam int unsigned DATAPATH_BCE = 283_320;
localparam int unsigned BCE_PER_FLOP = 20;
localparam int unsigned SWITCH_BCE_E4 = 56_200; // 5.62e8 / 1e4
localparam int unsigned TBL_ENTRIES = 128 * 1024;
localparam int unsigned TBL_WIDTH = 96;
// ---- Chapter 24.3 Section 1's framing tax ---------------------------
localparam int unsigned OCT_FIXED = 38; // preamble 8, gap 12,
// addresses 12, FCS 4, type 2
// ---- Chapter 13.2's tag ----------------------------------------------
localparam int unsigned OCT_TAG = 4; // TPID 2 + TCI 2
localparam int unsigned VID_BITS = 12;
localparam int unsigned VID_USABLE = 4094; // 0 and 4095 reserved
// ---- Chapter 12.4 Section 10's domain arithmetic ---------------------
localparam int unsigned DEFAULT_B_PER_S = 5; // broadcasts per station
typedef enum logic [1:0] {
PORT_ACCESS = 2'd0, // Chapter 13.3
PORT_TRUNK = 2'd1,
PORT_HYBRID = 2'd2
} portmode_e;
// ---- derived: wire cost ----------------------------------------------
function automatic int unsigned wire_slot(int unsigned payload,
bit tagged);
return payload + OCT_FIXED + (tagged ? OCT_TAG : 0);
endfunction
function automatic int unsigned tag_cost_ppm(int unsigned payload);
return (OCT_TAG * 1_000_000) / wire_slot(payload, 1'b0);
endfunction
function automatic int unsigned efficiency_ppm(int unsigned payload,
bit tagged);
return (payload * 1_000_000) / wire_slot(payload, tagged);
endfunction
// ---- derived: flood width --------------------------------------------
function automatic int unsigned ports_per_vlan(int unsigned ports,
int unsigned vlans);
return (vlans == 0) ? ports : (ports / vlans);
endfunction
function automatic int unsigned flood_copies(int unsigned ports,
int unsigned vlans);
int unsigned p;
p = ports_per_vlan(ports, vlans);
return (p == 0) ? 0 : (p - 1);
endfunction
// ---- derived: per-station broadcast rate ------------------------------
function automatic int unsigned station_rx_per_s(int unsigned ports,
int unsigned vlans,
int unsigned b_per_s);
return flood_copies(ports, vlans) * b_per_s;
endfunction
// ---- derived: the trunk, which segmentation does not reduce -----------
function automatic int unsigned trunk_per_s(int unsigned ports,
int unsigned vlans,
int unsigned b_per_s);
// every VLAN's stations, all of them, cross the trunk.
return ports_per_vlan(ports, vlans) * vlans * b_per_s;
endfunction
function automatic int unsigned datapaths_milli(int unsigned bce);
return (bce * 1000) / DATAPATH_BCE;
endfunction
endpackage// ---------------------------------------------------------------------
// tag_cost_model -- what four octets cost, and the conservation law
// that says nothing else about bandwidth changed.
//
// The output that matters is throughput_delta_ppm, which is zero for
// every configuration. The tag's cost is the only nonzero bandwidth
// figure in this chapter and it has the wrong sign.
// ---------------------------------------------------------------------
module tag_cost_model
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] payload_octets,
input logic tagged,
input logic [15:0] ports,
input logic [15:0] vlans,
input logic [15:0] port_rate_gbps,
output logic [31:0] slot_untagged,
output logic [31:0] slot_tagged,
output logic [31:0] tag_cost_ppm_o,
output logic [31:0] eff_untagged_ppm,
output logic [31:0] eff_tagged_ppm,
output logic [31:0] aggregate_gbps_before,
output logic [31:0] aggregate_gbps_after,
output logic signed [31:0] throughput_delta_ppm,
output logic capacity_is_conserved,
output logic only_effect_is_negative,
output logic [31:0] c_evaluations
);
always_comb begin
slot_untagged = 32'(wire_slot(int'(payload_octets), 1'b0));
slot_tagged = 32'(wire_slot(int'(payload_octets), 1'b1));
tag_cost_ppm_o = 32'(tag_cost_ppm(int'(payload_octets)));
eff_untagged_ppm = 32'(efficiency_ppm(int'(payload_octets), 1'b0));
eff_tagged_ppm = 32'(efficiency_ppm(int'(payload_octets), 1'b1));
// Section 2's conservation law, evaluated rather than asserted.
aggregate_gbps_before = 32'(ports) * 32'(port_rate_gbps);
aggregate_gbps_after = 32'(ports) * 32'(port_rate_gbps);
throughput_delta_ppm = 32'sd0;
capacity_is_conserved = (aggregate_gbps_after == aggregate_gbps_before);
// And the one bandwidth figure that does move.
only_effect_is_negative = tagged
&& (eff_tagged_ppm < eff_untagged_ppm);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_evaluations <= '0;
else c_evaluations <= c_evaluations + 32'd1;
end
endmoduleClassification: an accounting model whose central output is a hard-wired zero.
What it teaches: that throughput_delta_ppm is zero and vlans appears nowhere in the capacity calculation. The module takes the VLAN count as an input and does not use it, deliberately — because there is no term in N × R for it, and a model that produced a nonzero delta would have to invent one.
And it teaches that tag_cost_ppm_o reports 47 619 at a 46-octet payload — 4.762% of the wire — and 2 600 at 1 500 octets and 442 at 9 000. Chapter 8.3 §2's hyperbola once more: the constant grew by four octets and the shape did not change.
Deliberately simplified: the model treats every frame as tagged or untagged uniformly, where a real switch tags on trunk ports and strips on access ports — Chapter 13.3 §3's rule — so the 4.76% is paid on trunk links and not on access links. Ports are divided evenly among VLANs, which no deployment does. And aggregate_gbps_after is written as a separate expression identical to before, which a synthesis tool will collapse and which is written out so the conservation is visible in the source rather than argued in a comment.
Production implication: the trunk-only tagging is the detail that makes the 4.76% bearable and it is worth being precise about where it is paid. An access port carries untagged frames and pays nothing; a trunk carries tagged frames and pays 4.76% at minimum frame size. So the cost falls exactly on the links that are already the most heavily loaded — the uplinks — and Section 8 shows those same links carry every VLAN's broadcast traffic unreduced. A design that segments to relieve its uplinks has added 4.76% to them and moved none of their broadcast load, which is the chapter's central finding arriving as a deployment consequence.
4. What a VLAN Actually Changes — the Flood Width
Section 2 found the bandwidth delta to be zero. This section finds the quantity that does move, and it moves by a factor of nine.
Chapter 12.4 §3 established that a flood is the only one-to-many operation in a switch and that the multiplier is the port count less one. A VLAN replaces the port count with the VLAN's member count.
| Configuration, 64 ports | Ports per VLAN | Copies per flooded frame | Share of the unsegmented width |
|---|---|---|---|
| 1 VLAN | 64 | 63 | 100% |
| 2 VLANs | 32 | 31 | 49.2% |
| 4 VLANs | 16 | 15 | 23.8% |
| 8 VLANs | 8 | 7 | 11.1% |
| 16 VLANs | 4 | 3 | 4.8% |
| 32 VLANs | 2 | 1 | 1.6% |
Eight VLANs of eight ports reduce a flood's egress demand to 11.1% of its unsegmented value, and that is a real, large, correctly-claimed improvement in exactly one quantity.
Put it in the units Chapter 12.4 §3 used, at Chapter 23.3's scale. One port flooding minimum-size frames at 100 Gb/s emits 148.81 Mpps.
| VLANs | Copies | Emissions/s from one flooding port | Of the switch's 9.524 Gpps budget |
|---|---|---|---|
| 1 | 63 | 9.375 G | 98.4% |
| 4 | 15 | 2.232 G | 23.4% |
| 8 | 7 | 1.042 G | 10.9% |
| 16 | 3 | 0.446 G | 4.7% |
Row one is the number this chapter shares with Chapter 25.5: one port flooding at line rate consumes 98.4% of a 64-port switch's entire frame-emission budget. Row three says segmentation takes that to 10.9%.
So the claim "VLANs improve performance" is true of a switch that is flooding and false of one that is not, and the difference between those two switches is Chapter 12.5's table rather than anything about VLANs.
5. RTL 2 — The Flood-Width Model
// ---------------------------------------------------------------------
// flood_width_model -- Chapter 12.4 Section 3's amplification with the
// port count replaced by a VLAN's member count, and the flood share
// that bounds what the reduction is worth.
//
// The output that matters is improvement_ppm, because it is zero when
// nothing was flooding -- which is the condition the myth omits.
// ---------------------------------------------------------------------
module flood_width_model
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] ports,
input logic [15:0] vlans,
input logic [31:0] flood_share_ppm,
input logic [31:0] port_pps_k, // thousands of frames/s
input logic [31:0] switch_budget_pps_k,
output logic [15:0] members_per_vlan,
output logic [15:0] copies_segmented,
output logic [15:0] copies_flat,
output logic [31:0] width_share_ppm,
output logic [31:0] emissions_flat_k,
output logic [31:0] emissions_segmented_k,
output logic [31:0] budget_share_flat_ppm,
output logic [31:0] budget_share_seg_ppm,
output logic [31:0] mean_copies_flat_x1000,
output logic [31:0] mean_copies_seg_x1000,
output logic [31:0] improvement_ppm,
output logic improvement_needs_flooding,
output logic [31:0] c_evaluations
);
always_comb begin
members_per_vlan = 16'(ports_per_vlan(int'(ports), int'(vlans)));
copies_segmented = 16'(flood_copies(int'(ports), int'(vlans)));
copies_flat = 16'(flood_copies(int'(ports), 1));
width_share_ppm = (copies_flat == 0) ? 32'd0
: ((32'(copies_segmented) * 1_000_000) / 32'(copies_flat));
emissions_flat_k = port_pps_k * 32'(copies_flat);
emissions_segmented_k = port_pps_k * 32'(copies_segmented);
budget_share_flat_ppm = (switch_budget_pps_k == 0) ? 32'd0
: ((emissions_flat_k * 1_000_000)
/ switch_budget_pps_k);
budget_share_seg_ppm = (switch_budget_pps_k == 0) ? 32'd0
: ((emissions_segmented_k * 1_000_000)
/ switch_budget_pps_k);
// Mean egress copies per frame: 1 normally, copies when flooding.
mean_copies_flat_x1000 = 32'd1000
+ ((flood_share_ppm * (32'(copies_flat) - 32'd1)) / 32'd1000);
mean_copies_seg_x1000 = 32'd1000
+ ((flood_share_ppm * (32'(copies_segmented) - 32'd1)) / 32'd1000);
improvement_ppm = (mean_copies_flat_x1000 == 0) ? 32'd0
: (((mean_copies_flat_x1000 - mean_copies_seg_x1000)
* 1_000_000) / mean_copies_flat_x1000);
// THE condition the myth omits.
improvement_needs_flooding = (flood_share_ppm != 32'd0);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_evaluations <= '0;
else c_evaluations <= c_evaluations + 32'd1;
end
endmoduleClassification: Chapter 12.4 §3's amplification with one substitution, and a weighting that bounds what the substitution buys.
What it teaches: that improvement_ppm is zero when flood_share_ppm is zero, and that this is the module's whole contribution over the naive version. A model that reports only width_share_ppm says eight VLANs cut flooding to 11.1% and stops there; this one weights that by how much flooding there was, and at a 0.1% flood share the improvement is 5.3%.
And it teaches that budget_share_flat_ppm reports 984 252 for one flooding port on a 64-port 100 Gb/s switch — 98.4% of Chapter 23.3 §2's 9.524 Gpps pipeline budget — which is the number that makes a flood an emergency rather than an inefficiency.
Deliberately simplified: ports divide evenly among VLANs, which no deployment does, and an uneven division makes the largest VLAN the binding one. flood_share_ppm is a single scalar where the three flood causes have different shares and different fates at the receiver — Chapter 12.4 §6 established that an unknown unicast costs the receiving station nothing and a broadcast costs it everything. And the model counts egress copies rather than egress octets, which is the right unit for a frame-rate budget and the wrong one for a bandwidth budget.
Production implication: the flood-cause distinction is the one that changes what a deployment should do. A switch flooding unknown unicast has a table problem and segmentation treats the symptom — Chapter 25.5's subject. A switch flooding broadcast has a domain-size problem and segmentation is the correct and only fix at layer 2 — Chapter 12.4 §10's domain limit. A switch flooding multicast has a membership problem and neither helps, because Chapter 12.4 §13 established there is no local fix. Three causes, three different verdicts on the same intervention, and a flood_share_ppm that does not distinguish them gives one answer to three questions.
6. The Domain Rate, and Where It Genuinely Helps
Flood width is an egress demand inside the switch. The broadcast domain's size is a load on every station's CPU, and that is the quantity segmentation improves without qualification.
Chapter 12.4 §10's method: with N stations each emitting B broadcasts per second, each station receives (N − 1) × B.
Configuration, 64 ports, B = 5/s | Stations per domain | Each station receives |
|---|---|---|
| 1 VLAN | 64 | 315/s |
| 2 VLANs | 32 | 155/s |
| 4 VLANs | 16 | 75/s |
| 8 VLANs | 8 | 35/s |
| 16 VLANs | 4 | 15/s |
And unlike the flood width, this improvement has no flood_share qualifier, because a broadcast is always flooded — Chapter 12.4 §6: a card cannot discard it, the address means everyone, and every broadcast reaches every host's CPU on every station, always.
Segmentation's one unconditional benefit is that it reduces how many broadcasts each station's CPU must process, and at 64 ports divided into 8 it is a factor of nine.
Chapter 12.4 §10 turned this into a domain-size limit and the same arithmetic gives the VLAN count a deployment needs.
station budget ÷ B = stations per domain
64 ports ÷ stations per domain = VLANs required| Per-station budget | B = 5/s | Stations | VLANs needed at 64 ports |
|---|---|---|---|
| 500/s | 5 | 101 | 1 — 64 fits |
| 100/s | 5 | 21 | 4 |
| 50/s | 5 | 11 | 6 |
| 20/s | 5 | 5 | 13 |
Row one is the case a deployment is usually in — a 64-port switch in one VLAN puts 315 broadcasts per second on each station, against a budget of 500 — so segmentation is not required and the myth's claim has no room to be true.
7. RTL 3 — The Domain-Rate Model
// ---------------------------------------------------------------------
// domain_rate_model -- Chapter 12.4 Section 10's arithmetic with the
// VLAN count as the free variable, and the CPU budget as the bound.
//
// The output that matters is domain_spans_switches, which is an input
// the switch cannot determine and which changes the answer by a factor
// of the fabric's size.
// ---------------------------------------------------------------------
module domain_rate_model
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] ports_per_switch,
input logic [15:0] switches_in_domain,
input logic [15:0] vlans,
input logic [15:0] b_per_station_s,
input logic [15:0] cpu_budget_fps,
input logic [15:0] us_per_frame,
output logic [31:0] stations_in_domain,
output logic [31:0] rx_per_station_s,
output logic [31:0] cpu_us_per_s,
output logic [15:0] core_share_ppm,
output logic within_budget,
output logic [15:0] vlans_required,
output logic domain_spans_switches,
output logic switch_can_determine_this,
output logic [31:0] c_over_budget
);
logic [31:0] stations_per_vlan;
always_comb begin
// The domain is the fabric, not the switch -- Chapter 12.4 Sec 10.
stations_in_domain = 32'(ports_per_switch) * 32'(switches_in_domain);
stations_per_vlan = (vlans == 0) ? stations_in_domain
: (stations_in_domain / 32'(vlans));
rx_per_station_s = (stations_per_vlan == 0) ? 32'd0
: ((stations_per_vlan - 32'd1) * 32'(b_per_station_s));
cpu_us_per_s = rx_per_station_s * 32'(us_per_frame);
core_share_ppm = 16'(cpu_us_per_s / 32'd1); // us/s is ppm of a core
within_budget = (rx_per_station_s <= 32'(cpu_budget_fps));
// How many VLANs the budget demands.
vlans_required = (cpu_budget_fps == 0) ? 16'hFFFF
: 16'((stations_in_domain * 32'(b_per_station_s)
+ 32'(cpu_budget_fps) - 32'd1)
/ 32'(cpu_budget_fps));
domain_spans_switches = (switches_in_domain > 16'd1);
// THE gap. A switch learns what it hears; nothing tells it how many
// other switches share its broadcast domain.
switch_can_determine_this = 1'b0;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_over_budget <= '0;
else if (!within_budget) c_over_budget <= c_over_budget + 32'd1;
end
endmoduleClassification: a division and a ceiling, whose interesting output is a hard-wired zero saying the inputs are unavailable.
What it teaches: that core_share_ppm reports 1 575 for a single 64-port switch in one VLAN — 0.16% of a core — and 102 375 for a 4 096-station domain, which is 10.2%. The rule of thumb everybody carries is the transition between those two, and on one modern switch the broadcast argument for segmentation is weak.
And it teaches that switch_can_determine_this is zero, so vlans_required depends on an input no device supplies. The switch knows its own port count; it does not know how many switches share its broadcast domain, and Chapter 12.4 §10's sub-argument is that the domain is the fabric. A design that computes a VLAN requirement from its own port count has computed it for a topology of one.
Deliberately simplified: vlans_required uses total broadcasts ÷ budget rather than the exact ceil(ports ÷ (budget ÷ B + 1)), so it is conservative by one VLAN in places — it reports 7 where the exact figure is 6 at a 50/s budget — and the conservatism is deliberate in a sizing model. us_per_frame is a single constant where the cost varies enormously by protocol — an ARP request a host must answer costs far more than a discovery beacon it ignores. The model assumes every station emits at the same rate, where a small number of chatty stations dominate. And core_share_ppm treats microseconds per second as parts per million of a core, which is exact and looks like a coincidence.
Production implication: the chatty-station skew is what turns a comfortable domain into an uncomfortable one without the station count changing. A single misconfigured host emitting 500 broadcasts per second raises every other station's receive rate by 500 — which on a 64-station domain takes it from 315 to 815 and past a 500/s budget — and the domain size did not move. So the quantity to monitor is broadcasts received per station, which Chapter 12.4 §11's storm-control mechanism already measures per ingress port, and the per-port view names the offender while the per-domain average hides it. One counter per ingress port, already present in most parts, and the answer is a sort.
8. The Trunk, Which Segmentation Does Not Relieve
Sections 4 and 6 gave segmentation's two genuine benefits, both inside a switch and both about a station. This section is about the link between switches, and there the arithmetic does not move at all.
A trunk carries every VLAN. Chapter 13.3 §2's definition: an access port belongs to one VLAN and a trunk port carries tagged frames for many. So a broadcast in any VLAN crosses the trunk.
Work it at 64 ports and B = 5 per station per second.
| VLANs | Ports each | Stations total | Broadcasts/s on the trunk |
|---|---|---|---|
| 1 | 64 | 64 | 320 |
| 2 | 32 | 64 | 320 |
| 4 | 16 | 64 | 320 |
| 8 | 8 | 64 | 320 |
| 16 | 4 | 64 | 320 |
| 32 | 2 | 64 | 320 |
Three hundred and twenty broadcasts per second, at every VLAN count, because the station count did not change and every station's broadcasts still cross the trunk. Segmentation redistributes a load it does not reduce.
And the trunk pays the tag. Chapter 13.3 §3: a trunk carries tagged frames, so every frame on it is four octets longer — Section 3's 4.76% at minimum frame size.
| Access link | Trunk | |
|---|---|---|
| frames tagged | no — stripped on egress | yes |
| wire cost of the tag | 0 | 4.76% at 64 octets, 0.26% at 1 518 |
| broadcasts carried | its own VLAN's | every VLAN's — unchanged by segmentation |
| flood copies delivered | reduced by segmentation | the trunk is one port; it carries one copy per VLAN |
Row four is the subtlety and it is worth stating precisely. A flooded frame in one VLAN crosses the trunk once — the trunk is a single port, and Chapter 12.4 §5's replication puts one copy on it. So the trunk's flood load is the sum over VLANs of each VLAN's flood rate, and segmenting into more VLANs does not reduce that sum; it only reduces how many ports on the far side receive each copy.
Which produces the chapter's most quotable consequence.
| Segmenting 64 ports into 8 VLANs | |
|---|---|
| each station's broadcast CPU load | 315/s → 35/s — a factor of 9 |
| each flood's egress demand inside the switch | 63 copies → 7 — a factor of 9 |
| the trunk's broadcast load | 320/s → 320/s — no change |
| the trunk's wire cost | 0 → 4.76% at minimum frame size |
Rows one and two improve by nine and rows three and four get worse or stay still, and rows three and four are about the link a deployment is usually worried about.
9. RTL 4 — The Trunk Load Model
// ---------------------------------------------------------------------
// trunk_load_model -- what the uplink carries before and after
// segmentation, which is the same thing plus a tag.
//
// The output that matters is trunk_relief_ppm, which is zero or
// negative for every configuration. It is the chapter's conservation
// law measured on the link a deployment cares about.
// ---------------------------------------------------------------------
module trunk_load_model
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] ports,
input logic [15:0] vlans,
input logic [15:0] b_per_station_s,
input logic [15:0] mean_frame_octets,
input logic [15:0] trunk_rate_gbps,
output logic [31:0] stations,
output logic [31:0] trunk_bcast_flat,
output logic [31:0] trunk_bcast_segmented,
output logic signed [31:0] trunk_relief_ppm,
output logic [31:0] trunk_tag_cost_ppm,
output logic [31:0] trunk_bcast_bps,
output logic [31:0] trunk_bcast_share_ppm,
output logic segmentation_relieves_trunk,
output logic trunk_pays_the_tag,
output logic [31:0] c_evaluations
);
always_comb begin
stations = 32'(ports_per_vlan(int'(ports), int'(vlans)))
* ((vlans == 0) ? 32'd1 : 32'(vlans));
// Chapter 12.4 Section 10's rate, summed over every VLAN.
trunk_bcast_flat = 32'(trunk_per_s(int'(ports), 1, int'(b_per_station_s)));
trunk_bcast_segmented = 32'(trunk_per_s(int'(ports), int'(vlans),
int'(b_per_station_s)));
trunk_relief_ppm = (trunk_bcast_flat == 0) ? 32'sd0
: $signed((32'(trunk_bcast_flat)
- 32'(trunk_bcast_segmented)) * 1_000_000)
/ $signed(32'(trunk_bcast_flat));
// And the tag, which the trunk pays and the access ports do not.
trunk_tag_cost_ppm = 32'(tag_cost_ppm(int'(mean_frame_octets)));
// Broadcast bandwidth on the trunk, for scale.
trunk_bcast_bps = trunk_bcast_segmented
* (32'(mean_frame_octets) + 32'(OCT_FIXED) + 32'(OCT_TAG))
* 32'd8;
trunk_bcast_share_ppm = (trunk_rate_gbps == 0) ? 32'd0
: (trunk_bcast_bps / (32'(trunk_rate_gbps) * 1000));
// THE two structural facts.
segmentation_relieves_trunk = (trunk_relief_ppm > 32'sd0);
trunk_pays_the_tag = 1'b1;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_evaluations <= '0;
else c_evaluations <= c_evaluations + 32'd1;
end
endmoduleClassification: a sum over VLANs that equals a sum over stations, which is the point.
What it teaches: that trunk_relief_ppm is zero at every VLAN count, because trunk_per_s multiplies the per-VLAN station count by the VLAN count and recovers the total. The function is written that way deliberately — it looks like it depends on the VLAN count and the two factors cancel, which is Section 2's conservation law in the one place a deployment would most like it not to hold.
And it teaches that trunk_bcast_share_ppm puts the load in perspective. At 320 broadcasts per second and a 1 500-octet mean frame, the trunk carries 3.95 Mb/s of broadcast — 0.004% of a 100 Gb/s uplink. The broadcast load on a modern trunk is not a bandwidth problem at all, which is why Section 6's CPU argument is the real one and Section 8's tag cost is the real cost.
Deliberately simplified: the model counts broadcasts and not the unknown-unicast floods that share the trunk, and Chapter 25.5 establishes that those can be far larger. Stations emit uniformly. And trunk_relief_ppm is declared signed and can only be zero, which is written out rather than hard-wired so that a reader can see the subtraction produce it.
Production implication: the omitted unknown-unicast flood is what actually loads a trunk and it changes the verdict on segmentation. A switch refusing 19.5% of its addresses — Chapter 25.5's figure at a 128k four-way table offered 131 072 addresses — floods nearly a fifth of its unicast traffic, and every one of those floods crosses the trunk once per VLAN that contains a member. Segmenting into eight VLANs therefore multiplies the trunk's flood load by up to eight, because a frame that was flooded once now has eight VLANs' worth of copies to cross with. That is segmentation making the trunk strictly worse, it is the case Section 10 develops, and it is invisible to a model that counts only broadcasts.
10. Three Cases Where VLANs Make Throughput Worse
Sections 4 and 6 gave the benefits and Section 8 gave the null result. This section gives the negatives, and there are three.
Case one — the tag, on every trunk frame. Section 3: 4.76% of the wire at minimum frame size, 0.26% at 1 518, 0.04% at jumbo — and it is paid on the trunk, which is the busiest link.
Case two — a flood that now crosses the trunk once per VLAN. Section 9's production note. A frame flooded in a flat network crosses the trunk once; in an eight-VLAN network with members on both sides, a flood in each VLAN crosses it separately.
| Flat | 8 VLANs, members on both sides | |
|---|---|---|
| a broadcast from one station | 1 trunk crossing | 1 — it is in one VLAN |
| the aggregate broadcast load | 320/s | 320/s — Section 8 |
an unknown-unicast flood rate of f per VLAN | f crossings | 8f crossings |
Row three is the case and it needs the condition stated. A flat network floods an unknown destination once across the trunk; a segmented one floods it once per VLAN in which the destination is unknown — and a destination unknown in one VLAN is usually unknown in all of them, because the reason is the table. So the trunk's flood load multiplies by the VLAN count.
Case three — the forwarding key grew, and so did the table. Chapter 13.4 §8 derived that the key goes from 48 bits to 60 and the table's memory grows by exactly 25%, with no change to the set-occupancy fractions because the key's width appears nowhere in a balls-in-bins calculation.
Scale that to Chapter 23.3's table rather than repeating it at 8 192 entries.
| BCE | × the datapath | % of the switch | |
|---|---|---|---|
| Chapter 23.3's MAC table, 128k × 96 b | 1.26 × 10⁷ | 44.4 | 2.24% |
| 12 bits per entry for the VID | 1 572 864 | 5.55 | 0.28% |
| the table after widening | 1.42 × 10⁷ | 50.0 | 2.52% |
A quarter of a per cent of the switch, which is affordable — and it is a cost, in a chapter about a mechanism people believe is a benefit.
Three negatives — 4.76% of the trunk's wire, up to 8× its flood load, and 0.28% of the die — against two positives that are both about scope and neither of which is bandwidth.
11. RTL 5 — The Realignment and Table Cost
// ---------------------------------------------------------------------
// tag_realign_cost -- what inserting four octets mid-frame costs a
// datapath, and what the widened key costs a table at Chapter 23.3's
// scale.
//
// The output that matters is on_critical_path, which is high: the
// realignment sits in the middle of the fastest path in the design,
// and its cost is a timing margin rather than an area.
// ---------------------------------------------------------------------
module tag_realign_cost
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [15:0] datapath_bits,
input logic [15:0] max_tags,
input logic [31:0] table_entries,
input logic [15:0] key_bits_before,
input logic [15:0] key_bits_after,
output logic [15:0] shift_positions,
output logic [15:0] mux_ways,
output logic [31:0] realign_ge,
output logic [31:0] holdover_flops,
output logic [31:0] key_growth_bits,
output logic [31:0] table_growth_bce,
output logic [31:0] table_growth_dp_milli,
output logic [15:0] table_growth_pct,
output logic on_critical_path,
output logic occupancy_fractions_move,
output logic [31:0] c_evaluations
);
always_comb begin
// A tag stack of 0..max_tags means a shift of 0, 4, 8, ... octets.
shift_positions = max_tags + 16'd1;
mux_ways = shift_positions;
// One mux per datapath bit, about 3 GE per 2:1 way.
realign_ge = 32'(datapath_bits) * 32'(mux_ways - 16'd1) * 32'd3;
// The octets carried across a beat boundary.
holdover_flops = 32'(max_tags) * 32'd32;
// Chapter 13.4 Section 8's widening, at Chapter 23.3's scale.
key_growth_bits = 32'(key_bits_after) - 32'(key_bits_before);
table_growth_bce = table_entries * key_growth_bits;
table_growth_dp_milli = 32'(datapaths_milli(table_growth_bce));
table_growth_pct = (key_bits_before == 0) ? 16'd0
: 16'((key_growth_bits * 32'd100) / 32'(key_bits_before));
// Chapter 13.2 Section 2: four octets is a fraction of a word on
// every datapath wider than 32 bits, so it misaligns everything
// after it -- in the middle of the fastest path in the design.
on_critical_path = (datapath_bits > 16'd32);
// Chapter 13.4 Section 8's structural result: the key's width
// appears nowhere in a balls-in-bins calculation.
occupancy_fractions_move = 1'b0;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_evaluations <= '0;
else c_evaluations <= c_evaluations + 32'd1;
end
endmoduleClassification: a mux count and a memory delta, with two constants that carry results from Chapter 13.2 and Chapter 13.4.
What it teaches: that realign_ge at a 1 024-bit datapath with two tags is 6 144 GE — 1 024 bits × 2 extra ways × 3 GE — which is small in area and on the critical path. Chapter 13.2 §2 established why: four octets is half a word at 64 bits, a quarter at 128 and an eighth at 256, so inserting or removing it misaligns everything after it. on_critical_path is the output that matters and the gate count is not.
And it teaches that table_growth_bce is 1 572 864 at Chapter 23.3's 128k entries with a 12-bit VID — 5.55 datapaths, 0.28% of the switch — while occupancy_fractions_move is zero, which is Chapter 13.4 §8's structural result: widening the key widens what is stored and compared and does not widen the index.
Deliberately simplified: the realignment is priced as a flat mux per bit, where a real implementation folds it into an existing alignment stage and pays a delay rather than an area. holdover_flops assumes the carried-over octets are registered, which a combinational implementation avoids at a further timing cost. And max_tags bounds the stack where Chapter 13.2 §7's question of how deep a stack to tolerate has no standard answer.
Production implication: the timing cost is the one that is never in a budget and always in a schedule. Chapter 23.6 §8 established that frequency is an output of the implementation flow, and a three-way realignment mux inserted between a parser and a datapath is exactly the kind of structure that turns a closing design into one that misses by a few per cent. The area is 6 144 GE — nothing — and the delay is one mux level on the block Chapter 19.1 §9 already calls the MAC's critical path. A design that adds VLAN support late discovers this after synthesis, and the fix is a pipeline stage, which costs latency in a design whose latency budget was closed.
12. Where the Claim Is Actually True
Five sections of negatives and nulls owe a positive statement, and there is one. It is narrow, it is conditional, and the condition is measurable.
Segmentation improves delivered throughput exactly when a switch was already wasting egress capacity on floods. Section 5's arithmetic, restated as a decision rule.
improvement ≈ flood_share × (copies_flat − copies_segmented) ÷ mean_copies_flat
and the tag costs 4.76% of the trunk's wire at minimum frame size
so segmentation pays when flood_share × (copies_flat − copies_segmented)
> tag_cost × mean_copies_flatWork it at 64 ports into 8 VLANs, where copies_flat − copies_segmented is 56.
| Flood share | Improvement | Tag cost at the trunk's mean frame | Net, minimum frames | Net, 1 518-octet frames |
|---|---|---|---|---|
| 0.1% | 5.3% | 4.76% / 0.26% | +0.5% | +5.0% |
| 1% | 34.6% | 4.76% / 0.26% | +29.8% | +34.3% |
| 5% | 68.3% | 4.76% / 0.26% | +63.5% | +68.0% |
| 19.5% | 83.4% | 4.76% / 0.26% | +78.6% | +83.1% |
Every row is positive, which is the honest result — and row one is positive by half a percentage point at minimum frame size, which is inside the noise of any measurement anybody would make.
Segmentation is worth its cost above about a 0.1% flood share, and the flood share is a quantity Chapter 12.4 §15's telemetry already reports.
And the three benefits, ordered by how conditional they are.
| Benefit | Conditional on | Size at 64 ports into 8 VLANs |
|---|---|---|
| fewer broadcasts per station's CPU | nothing — a broadcast is always flooded | 315/s → 35/s, a factor of 9 |
| smaller blast radius for a storm | nothing | a storm reaches 8 stations, not 64 |
| lower flood egress demand | there being floods | 63 copies → 7, weighted by the flood share |
| more bandwidth | — | none, ever |
Rows one and two are unconditional and neither is bandwidth. Row three is the bandwidth-adjacent one and it is conditional. Row four is the myth.
13. RTL 6 — The Benefit Auditor
// ---------------------------------------------------------------------
// benefit_auditor -- which of segmentation's claimed benefits this
// deployment will actually receive, given its own counters.
//
// The output that matters is bandwidth_benefit, which is hard-wired
// zero. Every other benefit this module reports is about a scope.
// ---------------------------------------------------------------------
module benefit_auditor
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic [47:0] c_flooded,
input logic [47:0] c_forwarded,
input logic [15:0] ports,
input logic [15:0] vlans,
input logic [15:0] mean_frame_octets,
input logic [15:0] b_per_station_s,
input logic [15:0] cpu_budget_fps,
output logic [31:0] flood_share_ppm,
output logic [31:0] egress_improvement_ppm,
output logic [31:0] tag_cost_ppm_o,
output logic signed [31:0] net_ppm,
output logic worth_the_tag,
output logic [31:0] cpu_improvement_ppm,
output logic cpu_benefit_unconditional,
output logic [31:0] blast_radius_before,
output logic [31:0] blast_radius_after,
output logic bandwidth_benefit,
output logic [31:0] c_audits
);
logic [31:0] copies_flat, copies_seg, mean_flat_x1000, mean_seg_x1000;
always_comb begin
flood_share_ppm = (c_forwarded == 0) ? 32'd0
: 32'((c_flooded * 48'd1_000_000) / c_forwarded);
copies_flat = 32'(flood_copies(int'(ports), 1));
copies_seg = 32'(flood_copies(int'(ports), int'(vlans)));
mean_flat_x1000 = 32'd1000
+ ((flood_share_ppm * (copies_flat - 32'd1)) / 32'd1000);
mean_seg_x1000 = 32'd1000
+ ((flood_share_ppm * (copies_seg - 32'd1)) / 32'd1000);
egress_improvement_ppm = (mean_flat_x1000 == 0) ? 32'd0
: (((mean_flat_x1000 - mean_seg_x1000) * 1_000_000) / mean_flat_x1000);
tag_cost_ppm_o = 32'(tag_cost_ppm(int'(mean_frame_octets)));
net_ppm = $signed(egress_improvement_ppm) - $signed(tag_cost_ppm_o);
worth_the_tag = (net_ppm > 32'sd0);
// The unconditional benefit: a broadcast is always flooded, so the
// per-station CPU load falls with the domain size regardless of
// what the flood share is.
cpu_improvement_ppm = (copies_flat == 0) ? 32'd0
: (((copies_flat - copies_seg) * 1_000_000) / copies_flat);
cpu_benefit_unconditional = 1'b1;
blast_radius_before = copies_flat + 32'd1;
blast_radius_after = copies_seg + 32'd1;
// THE constant. Section 2's conservation law, as a bit.
bandwidth_benefit = 1'b0;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) c_audits <= '0;
else c_audits <= c_audits + 32'd1;
end
endmoduleClassification: a decision model built entirely from counters a switch already keeps.
What it teaches: that worth_the_tag is computable from c_flooded, c_forwarded and a mean frame size, all of which Chapter 12.4 §15 and Chapter 19.7 already provide. The segmentation decision is an arithmetic question about a deployment's own traffic and it is almost always made on a diagram instead.
And it teaches that bandwidth_benefit is a hard-wired zero beside a cpu_improvement_ppm that reports 888 888 at 64 ports into 8 VLANs — 88.9%, unconditionally. The module is designed so that the real benefit and the mythical one sit next to each other and are visibly different kinds of thing: one is a measured improvement in a CPU load and the other is a constant zero.
Deliberately simplified: flood_share_ppm lumps Chapter 12.4 §2's three flood causes together, and Section 5's production note established that they have three different verdicts on segmentation. net_ppm compares an egress-copy improvement with a wire-octet cost, which are different units — copies per frame against octets per frame — and the comparison is meaningful only because both are expressed as fractions of the same baseline. And the model assumes the flood share is unchanged by segmentation, which Section 10's case two shows is the assumption most likely to be false.
Production implication: that last assumption is the one to test rather than to make, and the test is a before-and-after measurement of the same ratio. Segment a pilot VLAN, leave the rest flat, and compare c_flooded ÷ c_forwarded on the trunk in both. If the ratio rises, Section 10's case two is happening — more VLANs, more keys, more offered addresses, more refusals — and the intervention is making the thing it was deployed to fix worse. The measurement costs nothing because both counters already exist, and it is the only way to distinguish the two directions, since Section 12's model predicts a benefit under an assumption the deployment itself decides.
14. What a Segmentation Claim Must Never Do
Five prohibitions.
| # | Never | Because |
|---|---|---|
| 1 | claim a bandwidth improvement from segmentation | Section 2: N × R has no term for the VLAN count |
| 2 | quote a flood-width reduction without the flood share | Section 5: 63 → 7 is worth 5.3% at a 0.1% flood share and 83.4% at 19.5% |
| 3 | claim a trunk is relieved | Section 8: 320 broadcasts per second at every VLAN count, and the trunk pays the tag |
| 4 | segment to fix unknown-unicast flooding | Section 10: the cause is the table, and more VLANs can offer it more addresses |
| 5 | cross-VLAN traffic without counting the extra switch traversal | Section 2's callout: a split conversation costs the switch twice |
Row four is the one that inverts the outcome and Section 13's production note gives the test.
15. RTL 7 — VLAN Performance Telemetry
// ---------------------------------------------------------------------
// vlanperf_telemetry -- the counters that make a segmentation claim
// checkable against the deployment it was made about.
//
// Design rule: every claimed benefit gets a counter, and the claim
// that has no counter is the one to disbelieve.
// ---------------------------------------------------------------------
module vlanperf_telemetry
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic clear,
input logic frame_valid,
input logic frame_tagged,
input logic frame_flooded,
input logic frame_broadcast,
input logic frame_unknown_unicast,
input logic frame_crossed_router,
input logic on_trunk,
input logic [15:0] frame_octets,
input logic [15:0] copies_emitted,
output logic [47:0] c_frames,
output logic [47:0] c_tagged,
output logic [47:0] c_flooded_o,
output logic [47:0] c_broadcast,
output logic [47:0] c_unknown_unicast,
output logic [47:0] c_cross_vlan,
output logic [47:0] c_trunk_frames,
output logic [47:0] c_trunk_tag_octets,
output logic [47:0] c_copies,
output logic [31:0] flood_share_ppm_o,
output logic [31:0] mean_copies_x1000,
output logic [31:0] cross_vlan_ppm,
output logic [31:0] trunk_tag_overhead_ppm,
output logic flood_is_unknown_unicast_dominated
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n || clear) begin
c_frames <= '0; c_tagged <= '0; c_flooded_o <= '0;
c_broadcast <= '0; c_unknown_unicast <= '0; c_cross_vlan <= '0;
c_trunk_frames <= '0; c_trunk_tag_octets <= '0; c_copies <= '0;
end else if (frame_valid) begin
c_frames <= c_frames + 48'd1;
c_copies <= c_copies + 48'(copies_emitted);
if (frame_tagged) c_tagged <= c_tagged + 48'd1;
if (frame_flooded) c_flooded_o <= c_flooded_o + 48'd1;
if (frame_broadcast) c_broadcast <= c_broadcast + 48'd1;
if (frame_unknown_unicast) c_unknown_unicast <= c_unknown_unicast + 48'd1;
if (frame_crossed_router) c_cross_vlan <= c_cross_vlan + 48'd1;
if (on_trunk) begin
c_trunk_frames <= c_trunk_frames + 48'd1;
c_trunk_tag_octets <= c_trunk_tag_octets + 48'(OCT_TAG);
end
end
end
always_comb begin
flood_share_ppm_o = (c_frames == 0) ? 32'd0
: 32'((c_flooded_o * 48'd1_000_000) / c_frames);
mean_copies_x1000 = (c_frames == 0) ? 32'd0
: 32'((c_copies * 48'd1000) / c_frames);
// Section 2's callout: a split conversation costs the switch twice.
cross_vlan_ppm = (c_frames == 0) ? 32'd0
: 32'((c_cross_vlan * 48'd1_000_000) / c_frames);
// Section 8: the trunk pays 4 octets a frame and nothing else does.
trunk_tag_overhead_ppm = (c_trunk_frames == 0) ? 32'd0
: 32'((c_trunk_tag_octets * 48'd1_000_000)
/ (c_trunk_frames * 48'd84));
// Section 5's production note: three flood causes, three verdicts.
flood_is_unknown_unicast_dominated =
(c_flooded_o != 48'd0) && (c_unknown_unicast > c_broadcast);
end
endmoduleClassification: nine counters and four ratios, of which one ratio decides the chapter's whole argument for a given deployment.
What it teaches: that flood_share_ppm_o is the number Section 12's decision rule needs and it is already in the hardware. Chapter 12.4 §15's flood-to-forward telemetry provides it; a deployment arguing about VLANs without reading it is arguing about a quantity it could measure in an hour.
And it teaches that flood_is_unknown_unicast_dominated is the bit that selects the right intervention. A flood share dominated by unknown unicast points at Chapter 12.5's table and segmentation treats a symptom; one dominated by broadcast points at Chapter 12.4 §10's domain size and segmentation is the correct fix. Same ratio, different cause, opposite verdicts.
Deliberately simplified: copies_emitted is supplied from the replication engine rather than counted here, so the module trusts a structure it is measuring. cross_vlan_ppm requires a frame_crossed_router input the switch cannot generate — it knows a frame left toward a router and not that it came back — so a real deployment measures it by correlating ingress and egress at the router port. And trunk_tag_overhead_ppm normalises against an 84-octet slot rather than the observed mean frame.
Production implication: the cross-VLAN counter is the one nobody has and it is the one that catches Section 2's callout in the field. A conversation split across VLANs enters the switch, leaves toward the router, comes back, and leaves again — two ingress events and two egress events for one conversation — and every counter in the switch records both halves as ordinary traffic. The signature is a router port whose ingress and egress rates are both high and nearly equal, which is exactly what a router port looks like when it is doing its job, so the pathological case and the healthy case are indistinguishable without knowing the traffic matrix. The measurement that separates them is a per-VLAN-pair matrix at the router, which is V² counters and is why nobody builds it — and the practical substitute is to compare the router port's rate against the switch's total, since a healthy deployment sends a small fraction of its traffic through a router and a badly segmented one sends most of it.
16. RTL 8 — The Segmentation Conformance Monitor
// ---------------------------------------------------------------------
// vlanperf_conformance -- the checks that hold a segmentation claim to
// the quantity it actually moved.
//
// There is no check that throughput improved, because Section 2
// establishes it cannot. Every check here is about a claim's shape.
// ---------------------------------------------------------------------
module vlanperf_conformance
import vlanperf_pkg::*;
(
input logic clk,
input logic rst_n,
input logic claims_bandwidth_gain,
input logic claims_trunk_relief,
input logic claims_flood_reduction,
input logic flood_share_measured,
input logic [31:0] flood_share_ppm_i,
input logic segmented_to_fix_unknown_unicast,
input logic flood_is_uu_dominated_i,
input logic [31:0] cross_vlan_ppm_i,
input logic [31:0] cross_vlan_budget_ppm,
input logic signed [31:0] net_ppm_i,
output logic v_bandwidth_claim,
output logic v_trunk_relief_claim,
output logic v_flood_claim_unmeasured,
output logic v_symptom_treated,
output logic v_cross_vlan_excessive,
output logic v_net_negative,
output logic [5:0] violations,
output logic conformant
);
always_comb begin
// 1. Section 2 -- there is no term for it.
v_bandwidth_claim = claims_bandwidth_gain;
// 2. Section 8 -- 320/s at every VLAN count.
v_trunk_relief_claim = claims_trunk_relief;
// 3. Section 5 -- a width reduction is worth the flood share.
v_flood_claim_unmeasured = claims_flood_reduction && !flood_share_measured;
// 4. Section 10 -- the cause is the table.
v_symptom_treated = segmented_to_fix_unknown_unicast
&& flood_is_uu_dominated_i;
// 5. Section 2's callout -- a split conversation costs twice.
v_cross_vlan_excessive = (cross_vlan_ppm_i > cross_vlan_budget_ppm);
// 6. Section 12 -- the tag is not free.
v_net_negative = flood_share_measured && (net_ppm_i <= 32'sd0);
violations = { v_net_negative, v_cross_vlan_excessive, v_symptom_treated,
v_flood_claim_unmeasured, v_trunk_relief_claim,
v_bandwidth_claim };
conformant = (violations == 6'b000000);
end
endmoduleClassification: six checks on claims rather than on behaviour, of which two are unconditional refusals.
What it teaches: that v_bandwidth_claim and v_trunk_relief_claim fire on the claim alone, with no traffic input at all. Section 2's conservation law and Section 8's trunk arithmetic are true of every configuration, so the claims are wrong before any measurement — and a monitor that refuses them without data is correct rather than lazy.
And it teaches that v_flood_claim_unmeasured is the useful one in practice. The flood-reduction claim is true and its size is entirely a function of a ratio the deployment can read. A claim made without reading it is not false; it is unquantified, and this check distinguishes the two.
Deliberately simplified: segmented_to_fix_unknown_unicast is an intent an operator supplies, which no hardware can infer — the same unavoidable input Chapter 25.3 §5 needed for threat. cross_vlan_budget_ppm has no principled default. And there is no check that the flood share fell after segmentation, which Section 13's production note argues is the measurement that matters and which requires a before-and-after rather than an instantaneous state.
Production implication: the before-and-after gap is the general shape of what a runtime monitor cannot do, and it is worth stating because this chapter is the fourth place it has appeared. A monitor evaluates the present; a claim about an intervention is about a difference between two presents. Chapter 24.3 §21's directed test needed the same thing — a regression's pass rate before and after a specification amendment — and Chapter 25.2 §21's needed a count distribution an hour later. The repair in all three is the same: record the quantity with a timestamp and a configuration identifier, so a later comparison is possible, which costs a counter and a register and is the difference between a fabric that can be reasoned about and one that can only be observed.
17. The Five Quantities, Priced Side by Side
Everything this chapter derived, at 64 ports divided into 8 VLANs.
| Quantity | Flat | 8 VLANs | Change | Is it bandwidth? |
|---|---|---|---|---|
| aggregate capacity | 6.4 Tb/s | 6.4 Tb/s | 0 | YES, and it does not move |
| wire per tagged frame, 64 octets | 84 | 88 | +4.76% | YES, and it is worse |
| flood copies per flooded frame | 63 | 7 | −88.9% | an egress demand — conditionally |
| broadcasts per station per second | 315 | 35 | −88.9% | no — a CPU load |
| trunk broadcasts per second | 320 | 320 | 0 | no, and it does not move either |
| MAC table | 1.26 × 10⁷ BCE | 1.42 × 10⁷ | +0.28% of the switch | no — a cost |
| a cross-VLAN conversation | 1 switch traversal | 2 | +100% | YES, and it is worse |
Two rows are bandwidth and improve nothing; two rows are bandwidth and get worse; two rows improve by 88.9% and are not bandwidth; one row is a cost.
Segmentation's genuine, unconditional benefit is that it divides a broadcast domain by the VLAN count. Everything the myth claims beyond that is either conditional on a flood rate the deployment can measure, or false by conservation.
And the decision rule, in one line.
read c_flooded ÷ c_forwarded
above about 0.1%, segmentation pays for its tag
below it, segment for scope, containment and policy — not for throughput18. What the Correction Assumes
Eight assumptions.
| # | Assumption | If it is false |
|---|---|---|
| 1 | ports divide evenly among VLANs | the largest VLAN binds; the flood width is its member count, not the mean |
| 2 | B = 5 broadcasts per station per second | Chapter 12.4 §10's typical figure; every domain-rate number scales linearly with it |
| 3 | a 4-octet tag — Chapter 13.2 | a stacked tag is 8 and doubles the wire cost to 9.52% at minimum frame size |
| 4 | the trunk carries every VLAN | a trunk pruned to the VLANs with members on both sides carries fewer, and Section 8's null result becomes a partial one |
| 5 | Chapter 23.3's 128k × 96 b table | the widening is 12 bits per entry either way; the percentage of the switch scales |
| 6 | a 12-bit VID | Chapter 13.2 §3's field; 4 094 usable values |
| 7 | flood share unchanged by segmentation | Section 10's case two; this is the assumption most likely to be false and the one Section 13's test checks |
| 8 | BCE prices the table growth | Section 19 examines it and it holds |
Assumption 4 deserves the caveat because pruning is real and it is the one thing that moves Section 8's result. A trunk configured to carry only the VLANs with members on both sides carries a subset, and if the segmentation is drawn so that most VLANs are local to one switch, the trunk's broadcast load does fall. The condition is a topology property rather than a VLAN-count property: pruning helps exactly when the segmentation aligns with the physical layout, and a deployment that segments by function across a fabric has guaranteed it does not.
19. The Cost, Accounted — in BCE
This chapter's blocks.
| Block | Flops | BCE | × the datapath |
|---|---|---|---|
tag_cost_model | 32 | 640 | 0.002 |
flood_width_model | 32 | 640 | 0.002 |
domain_rate_model | 32 | 640 | 0.002 |
trunk_load_model | 32 | 640 | 0.002 |
tag_realign_cost | 32 | 640 | 0.002 |
benefit_auditor | 32 | 640 | 0.002 |
vlanperf_telemetry | 480 | 9 600 | 0.034 |
vlanperf_conformance | 0 — combinational | 0 | 0 |
| this chapter's additions | 672 | 13 440 | 0.047 |
Seven of the eight blocks are models and one holds state, which is characteristic of a chapter whose subject is an argument rather than a datapath.
And the designs the blocks describe.
| BCE | × the datapath | % of the switch | |
|---|---|---|---|
| Chapter 23.3's MAC table, 48-bit key | 1.26 × 10⁷ | 44.4 | 2.24% |
| the VID widening, 12 bits × 128k | 1 572 864 | 5.55 | 0.28% |
| the table after widening | 1.42 × 10⁷ | 50.0 | 2.52% |
| Chapter 13.4 §17's VLAN state, scaled to 64 ports | 2.62 × 10⁵ | 0.93 | 0.047% |
| the realignment mux, 1 024 bits, 2 tags | 6 144 GE | — | logic, not state |
Row four is Chapter 13.4 §17's 9.6 KiB of VLAN state at 24 ports, scaled: the per-port arrays grow with the port count and the VID maps do not, so at 64 ports it is about 32 KiB — 2.62 × 10⁵ BCE, 0.047% of the switch. The VLAN machinery itself is nearly free and the table's growth is five times larger, which is Chapter 13.4 §17's finding confirmed at a different scale.
20. Properties Worth Asserting, and One Worth Refusing
Fifty-one properties in six groups, and the refused one is true, measurable, and about one factor of a product.
Group A — conservation and the tag (9).
// A1. Aggregate capacity does not depend on the VLAN count.
p_cn_conserved: assert property (@(posedge clk) disable iff (!rst_n)
(aggregate_gbps_after == aggregate_gbps_before));
// A2. And the delta is zero, always.
p_cn_delta_zero: assert property (@(posedge clk) disable iff (!rst_n)
(throughput_delta_ppm == 32'sd0));
// A3. The model reports conservation.
p_cn_reports: assert property (@(posedge clk) disable iff (!rst_n)
capacity_is_conserved);
// A4. A tagged slot is four octets longer.
p_cn_slot: assert property (@(posedge clk) disable iff (!rst_n)
(slot_tagged == slot_untagged + 32'(OCT_TAG)));
// A5. Tagging always lowers efficiency.
p_cn_eff_falls: assert property (@(posedge clk) disable iff (!rst_n)
(eff_tagged_ppm < eff_untagged_ppm));
// A6. And the model says the only bandwidth effect is negative.
p_cn_negative: assert property (@(posedge clk) disable iff (!rst_n)
tagged |-> only_effect_is_negative);
// A7. The tag's cost falls as the frame grows -- Chapter 8.3's curve.
p_cn_hyperbola: assert property (@(posedge clk) disable iff (!rst_n)
(payload_octets > $past(payload_octets)) |->
(tag_cost_ppm_o <= $past(tag_cost_ppm_o)));
// A8. At minimum frame size it is 47 619 ppm.
p_cn_min_frame: assert property (@(posedge clk) disable iff (!rst_n)
(payload_octets == 16'd46) |-> (tag_cost_ppm_o == 32'd47_619));
// A9. The VLAN count enters no capacity expression.
p_cn_vlans_absent: assert property (@(posedge clk) disable iff (!rst_n)
(vlans != $past(vlans)) |-> (aggregate_gbps_after == $past(aggregate_gbps_after)));Group B — flood width (9).
// B1. Copies are the member count less one.
p_fw_copies: assert property (@(posedge clk) disable iff (!rst_n)
(copies_segmented == members_per_vlan - 16'd1));
// B2. Segmenting never raises the copy count.
p_fw_monotone: assert property (@(posedge clk) disable iff (!rst_n)
(vlans >= 16'd1) |-> (copies_segmented <= copies_flat));
// B3. One VLAN is the flat case.
p_fw_one_vlan: assert property (@(posedge clk) disable iff (!rst_n)
(vlans == 16'd1) |-> (copies_segmented == copies_flat));
// B4. The width share is the ratio of the two.
p_fw_share: assert property (@(posedge clk) disable iff (!rst_n)
(copies_flat != 16'd0) |->
(width_share_ppm == (32'(copies_segmented) * 1_000_000) / 32'(copies_flat)));
// B5. THE qualifier: no flooding, no improvement.
p_fw_needs_flooding: assert property (@(posedge clk) disable iff (!rst_n)
(flood_share_ppm == 32'd0) |-> (improvement_ppm == 32'd0));
// B6. And with flooding there is one.
p_fw_has_improvement: assert property (@(posedge clk) disable iff (!rst_n)
((flood_share_ppm != 32'd0) && (vlans > 16'd1)) |-> (improvement_ppm != 32'd0));
// B7. The improvement never exceeds unity.
p_fw_bounded: assert property (@(posedge clk) disable iff (!rst_n)
(improvement_ppm <= 32'd1_000_000));
// B8. Emissions are copies times the port rate.
p_fw_emissions: assert property (@(posedge clk) disable iff (!rst_n)
(emissions_flat_k == port_pps_k * 32'(copies_flat)));
// B9. One flooding port at 64 ports takes most of the budget.
p_fw_budget: assert property (@(posedge clk) disable iff (!rst_n)
((ports == 16'd64) && (port_pps_k == 32'd148_810) &&
(switch_budget_pps_k == 32'd9_524_000))
|-> (budget_share_flat_ppm >= 32'd980_000));Group C — the domain rate (8).
// C1. The domain is the fabric, not the switch.
p_dr_domain: assert property (@(posedge clk) disable iff (!rst_n)
(stations_in_domain == 32'(ports_per_switch) * 32'(switches_in_domain)));
// C2. Each station receives the others' broadcasts.
p_dr_rx: assert property (@(posedge clk) disable iff (!rst_n)
(stations_in_domain != 0) |-> (rx_per_station_s <
(stations_in_domain * 32'(b_per_station_s)) + 32'd1));
// C3. More VLANs never raises the per-station rate.
p_dr_monotone: assert property (@(posedge clk) disable iff (!rst_n)
(vlans > $past(vlans)) |-> (rx_per_station_s <= $past(rx_per_station_s)));
// C4. The switch cannot determine the domain's extent.
p_dr_unknowable: assert property (@(posedge clk) disable iff (!rst_n)
(switch_can_determine_this == 1'b0));
// C5. Spanning switches is reported.
p_dr_spans: assert property (@(posedge clk) disable iff (!rst_n)
(switches_in_domain > 16'd1) |-> domain_spans_switches);
// C6. Within budget is a comparison and nothing else.
p_dr_budget: assert property (@(posedge clk) disable iff (!rst_n)
within_budget |-> (rx_per_station_s <= 32'(cpu_budget_fps)));
// C7. Over budget is counted.
p_dr_counted: assert property (@(posedge clk) disable iff (!rst_n)
!within_budget |=> (c_over_budget == $past(c_over_budget) + 32'd1));
// C8. CPU microseconds follow the frame rate.
p_dr_cpu: assert property (@(posedge clk) disable iff (!rst_n)
(cpu_us_per_s == rx_per_station_s * 32'(us_per_frame)));Group D — the trunk (8).
// D1. The trunk's broadcast load is the station count times B.
p_tk_load: assert property (@(posedge clk) disable iff (!rst_n)
(trunk_bcast_segmented == 32'(trunk_per_s(int'(ports), int'(vlans),
int'(b_per_station_s)))));
// D2. And it does not depend on the VLAN count.
p_tk_invariant: assert property (@(posedge clk) disable iff (!rst_n)
(trunk_bcast_segmented == trunk_bcast_flat));
// D3. So the relief is zero.
p_tk_no_relief: assert property (@(posedge clk) disable iff (!rst_n)
(trunk_relief_ppm == 32'sd0));
// D4. And the model says so.
p_tk_reports: assert property (@(posedge clk) disable iff (!rst_n)
!segmentation_relieves_trunk);
// D5. The trunk pays the tag.
p_tk_pays: assert property (@(posedge clk) disable iff (!rst_n)
trunk_pays_the_tag);
// D6. The tag cost is Chapter 24.3's arithmetic with four octets.
p_tk_tag_cost: assert property (@(posedge clk) disable iff (!rst_n)
(trunk_tag_cost_ppm == 32'(tag_cost_ppm(int'(mean_frame_octets)))));
// D7. Broadcast bandwidth is a small share of a fast trunk.
p_tk_small: assert property (@(posedge clk) disable iff (!rst_n)
((trunk_rate_gbps >= 16'd100) && (trunk_bcast_segmented <= 32'd1000))
|-> (trunk_bcast_share_ppm <= 32'd100));
// D8. Station count is preserved across segmentation.
p_tk_stations: assert property (@(posedge clk) disable iff (!rst_n)
(vlans != 16'd0) |-> (stations == 32'(ports)));Group E — realignment, table growth and the auditor (9).
// E1. Shift positions are the tag depth plus one.
p_rl_positions: assert property (@(posedge clk) disable iff (!rst_n)
(shift_positions == max_tags + 16'd1));
// E2. The realignment is on the critical path above 32 bits.
p_rl_critical: assert property (@(posedge clk) disable iff (!rst_n)
(datapath_bits > 16'd32) |-> on_critical_path);
// E3. The key's width does not move the occupancy fractions.
p_rl_fractions: assert property (@(posedge clk) disable iff (!rst_n)
(occupancy_fractions_move == 1'b0));
// E4. Table growth is entries times the added bits.
p_rl_growth: assert property (@(posedge clk) disable iff (!rst_n)
(table_growth_bce == table_entries * key_growth_bits));
// E5. A 48-to-60-bit key is a 25% growth.
p_rl_25pct: assert property (@(posedge clk) disable iff (!rst_n)
((key_bits_before == 16'd48) && (key_bits_after == 16'd60))
|-> (table_growth_pct == 16'd25));
// E6. The auditor's bandwidth benefit is zero.
p_au_no_bandwidth: assert property (@(posedge clk) disable iff (!rst_n)
(bandwidth_benefit == 1'b0));
// E7. And the CPU benefit is unconditional.
p_au_cpu: assert property (@(posedge clk) disable iff (!rst_n)
cpu_benefit_unconditional);
// E8. Worth-the-tag requires a positive net.
p_au_worth: assert property (@(posedge clk) disable iff (!rst_n)
worth_the_tag |-> (net_ppm > 32'sd0));
// E9. The blast radius shrinks with the VLAN count.
p_au_radius: assert property (@(posedge clk) disable iff (!rst_n)
(vlans > 16'd1) |-> (blast_radius_after <= blast_radius_before));Group F — telemetry and conformance (8).
// F1. Flooded frames are a subset of frames.
p_tl_subset: assert property (@(posedge clk) disable iff (!rst_n)
(c_flooded_o <= c_frames));
// F2. The flood share is the ratio the decision rule needs.
p_tl_share: assert property (@(posedge clk) disable iff (!rst_n)
(c_frames != 48'd0) |->
(flood_share_ppm_o == 32'((c_flooded_o * 48'd1_000_000) / c_frames)));
// F3. Mean copies is at least one.
p_tl_mean: assert property (@(posedge clk) disable iff (!rst_n)
(c_frames != 48'd0) |-> (mean_copies_x1000 >= 32'd1000));
// F4. A bandwidth claim is always a violation.
p_cf_bandwidth: assert property (@(posedge clk) disable iff (!rst_n)
claims_bandwidth_gain |-> v_bandwidth_claim);
// F5. So is a trunk-relief claim.
p_cf_trunk: assert property (@(posedge clk) disable iff (!rst_n)
claims_trunk_relief |-> v_trunk_relief_claim);
// F6. A flood claim needs the share measured.
p_cf_measured: assert property (@(posedge clk) disable iff (!rst_n)
(claims_flood_reduction && !flood_share_measured) |-> v_flood_claim_unmeasured);
// F7. Segmenting to fix unknown unicast treats a symptom.
p_cf_symptom: assert property (@(posedge clk) disable iff (!rst_n)
(segmented_to_fix_unknown_unicast && flood_is_uu_dominated_i)
|-> v_symptom_treated);
// F8. Conformance is the disjunction of its six checks.
p_cf_vector: assert property (@(posedge clk) disable iff (!rst_n)
conformant |-> (violations == 6'b000000));Coverage — the configurations that decide the argument.
c_cn_min_frame: cover property (@(posedge clk) payload_octets <= 16'd46);
c_cn_jumbo: cover property (@(posedge clk) payload_octets >= 16'd9000);
c_fw_no_flood: cover property (@(posedge clk) flood_share_ppm == 32'd0);
c_fw_heavy: cover property (@(posedge clk) flood_share_ppm >= 32'd195_000);
c_fw_32_vlans: cover property (@(posedge clk) vlans == 16'd32);
c_dr_multiswitch:cover property (@(posedge clk) switches_in_domain >= 16'd4);
c_dr_over: cover property (@(posedge clk) !within_budget);
c_tk_relief: cover property (@(posedge clk) segmentation_relieves_trunk);
c_rl_two_tags: cover property (@(posedge clk) max_tags == 16'd2);
c_rl_1024: cover property (@(posedge clk) datapath_bits == 16'd1024);
c_au_marginal: cover property (@(posedge clk) (net_ppm > 32'sd0) && (net_ppm < 32'sd10_000));
c_au_negative: cover property (@(posedge clk) net_ppm <= 32'sd0);
c_tl_uu_dom: cover property (@(posedge clk) flood_is_unknown_unicast_dominated);
c_cf_symptom: cover property (@(posedge clk) v_symptom_treated);21. Verification Scenarios
Fifty-eight scenarios in six groups, plus one directed test random stimulus will not produce.
Group 1 — conservation and the tag (10).
| # | Scenario | Expect |
|---|---|---|
| 1 | 64 ports at 100 Gb/s, 1 VLAN | 6 400 Gb/s aggregate |
| 2 | the same, 8 VLANs | 6 400 Gb/s — throughput_delta_ppm = 0 |
| 3 | the same, 32 VLANs | 6 400 Gb/s |
| 4 | 46-octet payload, untagged | slot 84; efficiency 547 619 ppm |
| 5 | 46-octet payload, tagged | slot 88; efficiency 522 727 ppm; tag 47 619 ppm |
| 6 | 1 500-octet payload, tagged | tag 2 600 ppm — 0.26% |
| 7 | 9 000-octet payload, tagged | tag 442 ppm — 0.044% |
| 8 | a stacked tag, 8 octets, 46-octet payload | 9.52% of the wire |
| 9 | vlans changed with rates fixed | capacity unchanged — p_cn_vlans_absent |
| 10 | any tagged configuration | only_effect_is_negative high |
Group 2 — flood width (10).
| # | Scenario | Expect |
|---|---|---|
| 11 | 64 ports, 1 VLAN | 63 copies |
| 12 | 64 ports, 2 VLANs | 31 copies; 49.2% of flat |
| 13 | 64 ports, 8 VLANs | 7 copies; 11.1% |
| 14 | 64 ports, 32 VLANs | 1 copy; 1.6% |
| 15 | flood share 0 | improvement_ppm = 0 — p_fw_needs_flooding |
| 16 | flood share 0.1%, 8 VLANs | 5.3% improvement |
| 17 | flood share 1%, 8 VLANs | 34.6% |
| 18 | flood share 19.5%, 8 VLANs | 83.4% |
| 19 | one port flooding at 148.81 Mpps, 64 ports | 9.375 Gpps — 98.4% of the budget |
| 20 | the same at 8 VLANs | 1.042 Gpps — 10.9% |
Group 3 — the domain rate (9).
| # | Scenario | Expect |
|---|---|---|
| 21 | 64 stations, 1 VLAN, B = 5 | 315/s each; 1 575 µs/s; 0.16% of a core |
| 22 | the same, 8 VLANs | 35/s each |
| 23 | 512 stations, 1 domain | 2 555/s; 1.28% of a core |
| 24 | 4 096 stations | 20 475/s; 10.2% |
| 25 | 4 switches of 63, 1 domain | 252 stations; 1 255/s; 0.63% |
| 26 | budget 500/s, B = 5, 64 ports | 101 stations fit; 1 VLAN suffices |
| 27 | budget 50/s | 11 stations; 6 VLANs exact, 7 from the model |
| 28 | switches_in_domain = 4 | domain_spans_switches high |
| 29 | switch_can_determine_this | 0, at every setting |
Group 4 — the trunk (10).
| # | Scenario | Expect |
|---|---|---|
| 30 | 64 ports, 1 VLAN, B = 5 | 320/s on the trunk |
| 31 | the same, 4 VLANs | 320/s |
| 32 | the same, 16 VLANs | 320/s |
| 33 | trunk_relief_ppm at any VLAN count | 0 |
| 34 | segmentation_relieves_trunk | low, always |
| 35 | trunk_pays_the_tag | high, always |
| 36 | 1 500-octet mean frame on the trunk | tag 2 600 ppm |
| 37 | 320 broadcasts/s, 1 500-octet frames, 100 Gb/s trunk | 3.95 Mb/s — 0.004% |
| 38 | a pruned trunk carrying 2 of 8 VLANs | the model reports the unpruned figure — a stated simplification |
| 39 | stations across configurations | 64, unchanged — p_tk_stations |
Group 5 — realignment, table and the auditor (10).
| # | Scenario | Expect |
|---|---|---|
| 40 | 1 024-bit datapath, 2 tags | 3 shift positions; 6 144 GE; on the critical path |
| 41 | 32-bit datapath | on_critical_path low — a tag is exactly one word |
| 42 | 48-bit key → 60-bit | 25% table growth; fractions unmoved |
| 43 | 128k entries, 12 added bits | 1 572 864 BCE; 5.55 datapaths; 0.28% |
| 44 | occupancy_fractions_move | 0 — Chapter 13.4 §8's structural result |
| 45 | flood share 0.1%, 1 500-octet trunk frames | net +5.0%; worth_the_tag high |
| 46 | flood share 0.1%, minimum-size trunk frames | net +0.5% — inside anybody's measurement noise |
| 47 | flood share 0.01%, minimum-size frames | net negative; worth_the_tag low |
| 48 | bandwidth_benefit at any setting | 0 |
| 49 | cpu_improvement_ppm, 64 ports into 8 | 888 888 — 88.9%, unconditional |
Group 6 — telemetry and conformance (9).
| # | Scenario | Expect |
|---|---|---|
| 50 | 1 000 frames, 10 flooded | flood_share_ppm_o = 10 000 |
| 51 | flooded frames dominated by unknown unicast | flood_is_unknown_unicast_dominated high |
| 52 | the same, dominated by broadcast | low — and segmentation is now the correct fix |
| 53 | a bandwidth-gain claim | v_bandwidth_claim, with no traffic input |
| 54 | a trunk-relief claim | v_trunk_relief_claim |
| 55 | a flood claim with no measurement | v_flood_claim_unmeasured |
| 56 | segmenting to fix unknown unicast | v_symptom_treated |
| 57 | cross-VLAN share above budget | v_cross_vlan_excessive |
| 58 | all six checks clear | conformant high |
22. Debugging a Segmentation Decision
Six symptoms, and the first question in every row is which quantity did you expect to move.
| Symptom | First question | Where to look |
|---|---|---|
| segmentation deployed, throughput unchanged | what was the flood share before? | Section 5 — at 0.1% the improvement is 5.3% and the tag costs 4.76% |
| the trunk is as busy as it was | did you expect it to change? | Section 8 — 320 broadcasts per second at every VLAN count |
| flooding got worse after segmenting | did the distinct key count grow? | Section 21's directed test — servers reachable from several VLANs |
| a design that closed timing now misses | when was VLAN support added? | Section 11 — one mux level on the MAC's critical path |
| the switch is carrying every frame twice | which conversations cross a router? | Section 2's callout — a split conversation costs the switch twice |
| stations report fewer broadcasts and users report no change | was the problem ever broadcast? | Section 6 — 0.16% of a core at 64 stations |
Row six is the common case and it is worth being blunt about. A 64-port switch in one VLAN puts 315 broadcasts per second on each station, which is 0.16% of one core. Reducing it to 35 is a factor of nine on a number that was already negligible, and users notice nothing because there was nothing to notice.
23. Misconceptions
Misconception 1 — "VLANs give each segment more bandwidth."
The wrong model: dividing a switch into parts gives each part a share it did not have.
What it costs: a capacity plan built on a term that does not exist. Section 2: a switch of N ports at rate R has aggregate capacity N × R before and after, and the VLAN count appears in no factor of it. The fabric, the buffer and the scheduler are all shared.
The corrected model: a VLAN is a membership relation evaluated during forwarding — Chapter 13.4 §6's key construction — and evaluating a relation does not create a link.
Misconception 2 — "the tag is free."
The wrong model: four octets on a 1 518-octet frame is nothing.
What it costs: 4.76% of the wire at minimum frame size, paid on trunks. Section 3: the tag's cost is Chapter 8.3 §2's hyperbola with the constant raised from 38 to 42 — 4.76% at 46 octets of payload, 0.26% at 1 500, 0.044% at 9 000.
The corrected model: access ports strip the tag and pay nothing; trunks carry it and pay 4.76% at the small end — and trunks are the links a deployment is usually worried about.
Misconception 3 — "segmenting relieves the uplink."
The wrong model: fewer stations per domain means less broadcast traffic crossing the trunk.
What it costs: an uplink upgrade deferred on a false premise. Section 8: the trunk carries every VLAN, so its broadcast load is the total station count times B — 320 per second at 64 ports and B = 5, at every VLAN count from 1 to 32.
The corrected model: segmentation redistributes a load it does not reduce, and the only things that relieve a trunk are routing at the edge, more uplinks, a faster uplink, storm control, or fixing the table.
Misconception 4 — "more VLANs is always safer."
The wrong model: finer segmentation contains more and costs nothing.
What it costs: two switch traversals per split conversation and a multiplied key count. Section 2's callout: two stations put in different VLANs now go through a router, which costs the switch twice the capacity it cost before. Section 21's directed test: segmenting by traffic class multiplied 20 000 keys into 160 000 and took the flood share from 0.08% to 27.68%.
The corrected model: segmentation's cost is paid by whatever traffic crosses the boundaries it draws, and segmenting by function segments along exactly the lines the traffic crosses.
Misconception 5 — "we segmented and flooding went down, so it worked."
The wrong model: the flood width fell, therefore the flood load fell.
What it costs: Section 20's class 118. Flood load is rate × copies and segmentation moves both — the copies down by the VLAN count and the rate up, if the widened key multiplies the address population past the table's effective capacity.
The corrected model: assert the product and report both factors. c_flooded ÷ c_forwarded before and after, on the same switch, and Chapter 12.4 §15's telemetry already provides both numbers.
Misconception 6 — "the broadcast load was the problem."
The wrong model: stations were drowning in broadcasts and segmentation fixed it.
What it costs: a project justified by a quantity that was never binding. Section 6: 64 stations at 5 broadcasts per second each is 315 per station per second — 1 575 µs of CPU per second, 0.16% of one core. The rule of thumb everybody carries comes from a domain of a few thousand stations, where it is 10.2% and genuinely matters.
The corrected model: compute the domain's station count, not the switch's port count — Chapter 12.4 §10's sub-argument that the domain is the fabric. Four 64-port switches in one domain is 252 stations and 0.63% of a core; sixteen is 1 008 and 2.5%.
24. Interview Questions
Six, with what a strong answer contains.
1. Do VLANs improve performance?
They improve one quantity conditionally and one unconditionally, and neither is bandwidth. A strong answer opens with conservation: a switch of N ports at rate R has aggregate capacity N × R before and after segmentation, and the VLAN count appears in no factor of it. Then the two real benefits: flood width falls from 63 copies to 7 at 64 ports into 8 VLANs, worth flood_share × 88.9%; and each station's broadcast receive rate falls from 315 per second to 35, unconditionally. The best answers add the cost: 4.76% of the trunk's wire at minimum frame size.
2. Does segmenting relieve an uplink?
No, and the arithmetic is one line. A trunk carries every VLAN, so its broadcast load is the total station count times the per-station rate — 320 per second at 64 ports and B = 5, at every VLAN count. A strong answer names what does relieve a trunk: routing at the edge, more uplinks, a faster uplink, storm control, or fixing the table — and observes that only one of those adds bandwidth.
3. What does a VLAN tag cost?
Four octets, on trunks only. Chapter 8.3 §2's hyperbola with the constant raised from 38 to 42: 4.76% of the wire at a 46-octet payload, 0.26% at 1 500, 0.044% at 9 000. A strong answer adds the two costs that are not the tag: the forwarding key grows from 48 to 60 bits, which at Chapter 23.3's 128k table is 1 572 864 BCE — 0.28% of the switch — and the mid-frame realignment is one mux level on the MAC's critical path.
4. When is segmenting worth it?
When the flood share is above about 0.1%, and the deployment can read it off its own hardware. c_flooded ÷ c_forwarded — Chapter 12.4 §15's telemetry. A strong answer gives the comparison: at a 0.1% flood share the improvement is 5.3% and the tag costs 4.76% on minimum-size trunk frames, so the net is half a percentage point. At 19.5% the improvement is 83.4%.
5. Can segmenting make things worse?
Yes, three ways. A split conversation costs two switch traversals instead of one. The trunk pays the tag and carries the same broadcasts. And the widened key can multiply the address population: segmenting 20 000 stations by traffic class into 8 VLANs produces 160 000 distinct keys, which against Chapter 23.3's 128k four-way table takes the refused share from 0.08% to 27.68% — and the mean egress copies per frame from 1.048 to 2.661, a 2.54× regression.
6. A team reports that segmenting cut flooding from 63 copies to 7. What do you ask?
What happened to the flood rate. A strong answer names the structure: flood load is rate × copies, the measurement covers one factor, and the same intervention moves the other. That is Section 20's class 118 — an improvement in one factor of a product where the mechanism moves the other — and it belongs to the group where something outside the property's interface decides the outcome, with the distinguishing feature that here the outside term is moved by the intervention itself.
25. Questions and Answers
26. What's Next
Four myths down and two to go, and the next one is the cause behind this chapter's most uncomfortable result.
Chapter 25.5 takes "switches eliminate broadcasts". It derives Chapter 12.4 §6's replication at Chapter 23.3's scale — one port flooding minimum-size frames at 100 Gb/s emits 9.375 Gpps, which is 98.4% of a 64-port switch's entire 9.524 Gpps frame budget — and then goes to the case that actually hurts.
Unknown-unicast flooding is a broadcast in every respect except its address. A frame whose destination is not in the table is replicated to every port in the VLAN, exactly as a broadcast is; Chapter 12.4 §6 established that the receiving stations discard it in hardware at no CPU cost, so it is cheaper at the endpoint and identical inside the fabric.
And the chapter's subject is when that happens, which is a property of Chapter 12.5's table rather than of the traffic. A 128k-entry four-way table offered 131 072 distinct addresses stores 105 465 and refuses 25 607 — 19.5% — and every refused address floods every frame sent to it, permanently, while the occupancy counter reads well below capacity and no error counter moves.
Then it ties the two chapters together through Chapter 25.2 §8's stale entries — 833 of them at 10 000 container teardowns an hour — because a table holding entries for stations that no longer exist is a table with fewer sets available for stations that do.
Continue learning
Related tutorials
- Related topic
Why VLANs Exist
Segmentation is the only lever on the broadcast-domain limit, and it turns every singleton in a switch into a vector. Supporting all 4094 VLANs costs 60 KiB — 0.94× the entire forwarding table.
- Related topic
Frame Format Overview
Every field exists to let a receiver make one decision at one moment, and the field order is the order those decisions must be made. The check value comes last because it covers everything before it — which makes every decision taken before it provisional.
- Related topic
Flooding — Unknown Unicast, Multicast and Broadcast
One port flooding at line rate consumes 47.9% of a 24-port switch's aggregate capacity. Three causes produce identical flooding, only one costs every host's CPU, and the switch can observe none of it.
- Related topic
Access Ports, Trunk Ports and Tag Handling
A frame's VLAN is assigned by the receiving port, not carried by the frame. Across an untagged link it is destroyed and re-invented — which is why a native-VLAN mismatch leaks both ways with every check clean.
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.
