Skip to content
VLSI Mentor

Ethernet · Module 25

"Switches Eliminate Broadcasts"

One flooding port takes 98.4% of a 64-port switch's frame budget, a 128k table refuses 25 607 of 131 072 addresses, and the occupancy counter reads 80.5% throughout.

Chapter 12.1 said what a switch does and Chapter 12.4 said what it does when it cannot. This chapter is about the gap between those two sentences, which is larger than anybody expects and is a property of a table rather than of the traffic.

The myth, stated as the people who hold it would state it. A hub repeats every frame to every port. A switch learns where stations are and forwards only to the right one. So switching replaced broadcasting, and a switched network does not flood.

The first two sentences are true. The third confuses two different things a switch replicates.

What a switch doesWhy
a known unicastforwards to one portthe table has an entry — Chapter 12.2
a broadcastreplicates to every port in the VLANby specification; the address means everyone
an unknown unicastreplicates to every port in the VLANbecause the table has no entry
a multicast with no membershipthe sameChapter 12.4 §13 — no local fix

Rows two to four are the same operation and only row two is deliberate. Row three is this chapter's subject: a broadcast in every respect except its address, and it happens whenever a table cannot hold what the network offers it.

Three numbers dispose of the myth.

The numberSection
what one flooding port costs a 64-port switch9.375 Gpps of emission — 98.4% of its 9.524 Gpps budget2
what a 128k four-way table stores when offered 131 072 addresses105 465 — it refuses 25 607, or 19.5%6
what the occupancy counter reads while that happens80.5% — comfortably below capacity8

A switch does not eliminate flooding. It reduces it to the cases the table cannot cover, and the size of that set is a property of a hash nobody chose.


1. Scope — Two Replications, One Mechanism

This chapter owns four derivations.

What is derived
Sections 2 to 3the replication at Chapter 23.3's scale — 98.4% of the frame budget from one port
Sections 4 to 7the table's effective capacity at 128k entries, and the occupancy at which refusals begin
Sections 8 to 11why the occupancy counter reads healthy, and what a design must report instead
Sections 12 to 13the interaction with Chapter 25.2 §8's stale entries — 833 sets held for stations that no longer exist

What this chapter does not own. Chapter 12.4 owns the three flood causes, the replication engine, the receiver's three fates and the domain-size limit; Chapter 12.5 owns the CAM, the hash, the set-associative store and the ageing sweep. This chapter re-derives neither at their own scale — it takes Chapter 12.4 §3's amplification from a 24-port gigabit switch to Chapter 23.3's 64-port 100 Gb/s part, and Chapter 12.5 §9's capacity arithmetic from 8 192 entries to 131 072.


2. The Replication, at 64 Ports and 100 Gb/s

One ingress port of a sixty four port switch receives minimum size frames at one hundred gigabits per second. Eighty four octets on the wire gives one hundred and forty eight point eight one million frames per second. Because the destination is unknown or the address is a broadcast, each frame is replicated to the other sixty three ports, which is nine point three seven five gigapackets per second of frame emission. Chapter twenty three point three derived that switch's aggregate frame budget as nine point five two four gigapackets per second, which is what sets its eight wide pipeline at one point two gigahertz. So one port consumes ninety eight point four per cent of the whole switch's frame handling capacity and leaves one point six per cent for the other sixty three ports. In bandwidth the same arithmetic gives one hundred gigabits per second in demanding six thousand three hundred gigabits per second out of a six thousand four hundred gigabit aggregate. The amplification is the port count less one, so a larger switch suffers the same proportional damage and inflicts it on twice as many stations.One ingress port100 Gb/s, 84-octetslots148.81 Mppsminimum-size framesReplicate ×63unknown or broadcast9.375 Gppsframe emissionsBudget 9.524 Gpps8-wide at 1.2 GHz98.4%from ONE port1.6% leftfor 63 other portsA bigger switchsame share, morevictims12
Figure 1 — one flooding port asks for 98.4% of a 6.4 Tb/s switch's entire frame budget, and a bigger switch asks for the same share.

Chapter 12.4 §3 derived the amplification on a 24-port gigabit switch. Take it to the part Chapter 23.3 built.

One port flooding minimum-size frames at 100 Gb/s.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
84 octets on the wire → 100 × 10⁹ ÷ (84 × 8) = 148.81 Mpps
× 63 copies = 9.375 Gpps of frame emission

And Chapter 23.3 §2 derived that switch's aggregate frame budget from its port count: 9.524 Gpps, which is what sets its eight-wide pipeline.

Value
one port, minimum-size frames at 100 Gb/s148.81 Mpps
× 63 copies9.375 Gpps
the switch's whole frame budget9.524 Gpps
share consumed by ONE flooding port98.4%
left for the other 63 ports1.6%

One port, flooding at line rate, consumes 98.4% of a 6.4 Tb/s switch's entire frame-handling capacity — and Chapter 12.4 §3's observation holds: buying a bigger switch does not help, because the amplification scales with the port count.

In bandwidth rather than frames the arithmetic is the same shape.

Value
1 port in at 100 Gb/s100 Gb/s
63 copies out6 300 Gb/s
aggregate egress capacity6 400 Gb/s
share98.4%

Chapter 12.4 §3's convergence holds: (N − 1) ÷ N → 1 in bandwidth and (N − 1) ÷ 2N → ½ when the ingress is counted too. At 64 ports the egress share is 98.4% and it never exceeds 100%.

And the frame-rate view is the binding one, because Chapter 23.3 §2's pipeline is sized in packets per second: an eight-wide pipeline at 1.2 GHz handles 9.6 Gpps and a single flooding port asks for 9.375.


3. RTL 1 — The Broadcast Package and the Amplification Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// bcastmyth_pkg -- the constants a flooding argument needs at Chapter
// 23.3's scale, with every derived figure computed here.
//
// 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 switch: 64 ports, 9.524 Gpps, table 128k x 96 b
// ---------------------------------------------------------------------
package bcastmyth_pkg;

  localparam int unsigned DATAPATH_BCE = 283_320;
  localparam int unsigned BCE_PER_FLOP = 20;

  localparam int unsigned PORTS          = 64;
  localparam int unsigned MIN_SLOT_OCT   = 84;     // Chapter 24.3 Sec 1
  localparam int unsigned BUDGET_PPS_K   = 9_524_000;

  // ---- Chapter 12.5's table, at Chapter 23.3's size --------------------
  localparam int unsigned TBL_ENTRIES = 128 * 1024;
  localparam int unsigned TBL_WAYS    = 4;
  localparam int unsigned TBL_SETS    = TBL_ENTRIES / TBL_WAYS;   // 32 768
  localparam int unsigned TBL_WIDTH   = 96;

  // ---- Chapter 12.5 Section 5's ageing, Chapter 25.2 Section 8's churn --
  localparam int unsigned AGEING_SECONDS = 300;

  typedef enum logic [1:0] {
    FLOOD_NONE     = 2'd0,
    FLOOD_BCAST    = 2'd1,   // by specification
    FLOOD_UNKNOWN  = 2'd2,   // because the table has no entry
    FLOOD_MCAST    = 2'd3    // no membership -- Chapter 12.4 Section 13
  } floodcause_e;

  // ---- derived: replication ---------------------------------------------
  function automatic int unsigned copies(int unsigned members);
    return (members == 0) ? 0 : (members - 1);
  endfunction

  function automatic int unsigned pps_at(int unsigned gbps,
                                         int unsigned slot_oct);
    // frames per second, in thousands, to keep the product in range.
    return (gbps * 1_000_000) / (slot_oct * 8);
  endfunction

  // ---- derived: effective capacity, Chapter 12.5 Section 9 --------------
  // stored = S x E[min(Load, W)] with Load ~ Poisson(n/S), so the
  // refused share depends only on the LOAD -- offered addresses per
  // set -- and not on the key's width. Chapter 13.4 Section 8's result.
  //
  // Integer hardware cannot evaluate exp, so the curve is tabulated at
  // eight anchors and interpolated between them. The anchors, at W = 4:
  //   load 1.00 ->  0.435%     load 2.00 ->  3.757%
  //   load 3.00 -> 10.645%     load 4.00 -> 19.537%
  //   load 5.00 -> 28.737%     load 6.00 -> 37.217%
  //   load 8.00 -> 50.744%     load 16.00 -> 75.001%
  // Worst interpolation error is about six per cent, at load 12.
  function automatic int unsigned load_x100(int unsigned offered,
                                            int unsigned sets);
    return (offered * 100) / sets;
  endfunction

  function automatic int unsigned refused_ppm(int unsigned offered,
                                              int unsigned sets);
    int unsigned l;
    l = load_x100(offered, sets);
    if (l <=  100) return (l * 43);
    if (l <=  200) return   4_349 + ((l -  100) * 332);
    if (l <=  300) return  37_571 + ((l -  200) * 688);
    if (l <=  400) return 106_452 + ((l -  300) * 889);
    if (l <=  500) return 195_367 + ((l -  400) * 920);
    if (l <=  600) return 287_369 + ((l -  500) * 847);
    if (l <=  800) return 372_167 + ((l -  600) * 676);
    if (l <= 1600) return 507_436 + ((l -  800) * 303);
    return 750_007;
  endfunction

  function automatic int unsigned datapaths_milli(int unsigned bce);
    return (bce * 1000) / DATAPATH_BCE;
  endfunction

endpackage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// flood_amplification_model -- what a flooding port asks of a switch,
// at the scale Chapter 23.3 built.
//
// The output that matters is budget_share_ppm, which reaches 984 252
// for one port at 64 -- and the fact that it cannot be delivered is
// why the symptom is "the network is slow" rather than "port 7 is".
// ---------------------------------------------------------------------
module flood_amplification_model
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] members,
  input  logic [15:0] port_gbps,
  input  logic [15:0] slot_octets,
  input  logic [31:0] budget_pps_k,

  output logic [15:0] copies_o,
  output logic [31:0] port_pps_k,
  output logic [31:0] emissions_pps_k,
  output logic [31:0] budget_share_ppm,
  output logic [31:0] egress_bw_gbps,
  output logic [31:0] egress_share_ppm,
  output logic        exceeds_budget,
  output logic        bigger_switch_helps,
  output logic [31:0] c_over_budget
);
  always_comb begin
    copies_o   = 16'(copies(int'(members)));
    port_pps_k = 32'(pps_at(int'(port_gbps), int'(slot_octets)));

    emissions_pps_k = port_pps_k * 32'(copies_o);

    budget_share_ppm = (budget_pps_k == 0) ? 32'd0
                     : ((emissions_pps_k * 1_000_000) / budget_pps_k);

    egress_bw_gbps   = 32'(port_gbps) * 32'(copies_o);
    egress_share_ppm = (members == 0) ? 32'd0
                     : ((32'(copies_o) * 1_000_000) / 32'(members));

    exceeds_budget = (emissions_pps_k > budget_pps_k);

    // Chapter 12.4 Section 3's observation: the amplification scales
    // WITH the port count, so a larger part suffers the same share.
    bigger_switch_helps = 1'b0;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)               c_over_budget <= '0;
    else if (exceeds_budget)  c_over_budget <= c_over_budget + 32'd1;
  end
endmodule

Classification: a multiplication by the member count, with a constant that refuses the obvious remedy.

What it teaches: that budget_share_ppm is 984 252 for one flooding port on a 64-port switch98.4% — and that bigger_switch_helps is hard-wired zero. Chapter 12.4 §3 established why: the amplification is the port count less one, so a 128-port switch faced with the same flooding port suffers the same proportional damage and inflicts it on twice as many stations.

And it teaches that egress_share_ppm converges on unity rather than on a half. Chapter 12.4 §3's (N − 1) ÷ 2N → ½ counts the ingress in the denominator; (N − 1) ÷ N → 1 counts only the egress, which is the capacity the flood actually consumes. At 64 ports it is 98.4% and at 8 ports 87.5%.

Deliberately simplified: members is the VLAN's member count and the model takes it as given, where Chapter 25.4 §4 derived that segmentation sets it. slot_octets is a constant where a real flood carries a mixture and the frame-rate arithmetic is dominated by the small end. And the model computes a demand rather than a delivery — it says what the switch is asked for, not what comes out, and the callout in Section 2 is the account of what actually happens.

Production implication: the demand-against-delivery distinction is what makes this failure hard to attribute in the field. A switch asked for 9.375 Gpps and capable of 9.524 delivers frames from every port at a reduced rate, and every port's drop counter increments — including the sixty-three ports that are victims. So a per-port drop count ranks the victims and never names the cause, which is Chapter 14.3 §8's second_order_pct argument arriving in a third mechanism: the place a shortage is felt and the place it is caused are different ports, and the obvious counter reports the first. The counter that names the cause is flooded frames per INGRESS port, and Chapter 12.4 §4's classifier already computes it.


4. Unknown Unicast Is a Broadcast With a Different Address

Section 2 priced the replication. This section establishes that the switch performs it for two entirely different reasons, and that only one of them is anybody's decision.

The two are identical inside the switch.

BroadcastUnknown unicast
destination addressall onesa specific station's
the forwarding decisionreplicate to every memberreplicate to every member
the replication engine usedChapter 12.4 §5'sthe same
copies at 64 ports6363
egress capacity consumed98.4% at line rate98.4%
the buffer pressureidenticalidentical

And they differ completely at the receiver — Chapter 12.4 §6.

BroadcastUnknown unicast
the card's decisionnone — always acceptedexact 48-bit compare
cost to the hostfull, on every stationzero — discarded in hardware
who noticesevery usernobody
who reports ita help desk, within minutesno one, ever

The two floods are the same operation with the same cost to the switch and opposite visibility to the people who pay for the switch.

And the causes differ in kind.

A broadcast floods because it is supposed to. ARP, DHCP discovery, and every neighbour-discovery protocol depend on it; Chapter 12.4 §6 established that a card cannot discard it without breaking them. The rate is a property of the hosts and it is bounded by what they choose to send.

An unknown unicast floods because the table failed. The destination exists, it is reachable, the switch has forwarded to it before — and the entry is not there now. The rate is a property of the table and it is bounded by nothing the operator configured.

Four reasons an entry is absent, and only the first is benign.

ReasonDurationChapter
the station has not spoken yetuntil it does — one frameChapter 12.2
the entry aged out during a quiet intervaluntil the station speaks againChapter 12.5 §13
a topology change flushed the tablesecondsChapter 12.5
the table REFUSED the insertpermanentlyChapter 12.5 §9 — Sections 6 to 8

Row four is this chapter's subject and it is the one with no expiry. A station whose address the table refused floods every frame sent to it, forever, on a switch whose occupancy counter reads comfortably below capacity.


5. RTL 2 — The Flood-Cause Classifier

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// unknown_unicast_classifier -- why this frame is flooding, and how
// long the reason will last.
//
// The output that matters is reason_is_permanent, which is true for
// exactly one of the four causes and is the one nothing reports.
// ---------------------------------------------------------------------
module unknown_unicast_classifier
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_valid,
  input  logic        dest_is_broadcast,
  input  logic        dest_is_multicast,
  input  logic        lookup_hit,
  input  logic        insert_was_refused,
  input  logic        entry_aged_out,
  input  logic        topology_changed,
  input  logic        never_seen,
  input  logic [15:0] members,

  output logic  [1:0] cause,
  output logic        is_flooding,
  output logic [15:0] copies_now,
  output logic        reason_is_permanent,
  output logic        host_cost_is_zero,
  output logic        anybody_will_notice,
  output logic [31:0] c_bcast,
  output logic [31:0] c_unknown,
  output logic [31:0] c_unknown_permanent,
  output logic [31:0] c_mcast
);
  always_comb begin
    cause = dest_is_broadcast ? 2'(FLOOD_BCAST)
          : (!lookup_hit && !dest_is_multicast) ? 2'(FLOOD_UNKNOWN)
          : (dest_is_multicast && !lookup_hit)  ? 2'(FLOOD_MCAST)
                                                 : 2'(FLOOD_NONE);

    is_flooding = (cause != 2'(FLOOD_NONE));
    copies_now  = is_flooding ? 16'(copies(int'(members))) : 16'd1;

    // Section 4's table: four reasons an entry is absent and one has
    // no expiry. A refused insert is not retried and never ages.
    reason_is_permanent = (cause == 2'(FLOOD_UNKNOWN)) && insert_was_refused;

    // Chapter 12.4 Section 6: an exact 48-bit compare discards it.
    host_cost_is_zero = (cause == 2'(FLOOD_UNKNOWN));

    // Which is why nobody reports it.
    anybody_will_notice = (cause == 2'(FLOOD_BCAST));
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_bcast <= '0; c_unknown <= '0; c_unknown_permanent <= '0; c_mcast <= '0;
    end else if (frame_valid && is_flooding) begin
      case (cause)
        2'(FLOOD_BCAST):   c_bcast   <= c_bcast + 32'd1;
        2'(FLOOD_MCAST):   c_mcast   <= c_mcast + 32'd1;
        default: begin
          c_unknown <= c_unknown + 32'd1;
          if (reason_is_permanent)
            c_unknown_permanent <= c_unknown_permanent + 32'd1;
        end
      endcase
    end
  end
endmodule

Classification: a four-way classifier over a forwarding outcome, with a duration attached to each cause.

What it teaches: that reason_is_permanent separates a flood that will stop from one that will not, and that nothing in a deployed switch computes it. Chapter 12.4 §4's classifier answers why is this frame flooding; this one answers and how long for, which is the question that decides whether an operator should act.

And it teaches that anybody_will_notice is true for exactly one cause. A broadcast flood reaches every host's CPU and produces complaints; an unknown-unicast flood is discarded in hardware at every station and produces none — so the counters are the only witness, and c_unknown_permanent is the one that matters.

Deliberately simplified: insert_was_refused must come from Chapter 12.5 §6's store, which reports TI_SET_FULL when a set is full and eviction is not permitted — so the classifier depends on a signal a real table does produce and most designs do not route out. The four causes are treated as exclusive where a topology change and an ageing expiry can coincide. And multicast is classified crudely, ignoring Chapter 12.4 §13's membership table.

Production implication: TI_SET_FULL is the signal that makes this chapter actionable and it is almost never exposed. Chapter 12.5 §6's insert path already computes it — the set was full, eviction was not permitted, the address was not learned — and a design that counts it per set index rather than in aggregate gives an operator the hash distribution directly. 32 768 sets at a 4-bit saturating counter is 131 072 bits, 131 072 BCE, 0.46 datapaths, 0.023% of Chapter 23.3's switchand it turns "the table refuses inserts at 80% occupancy" from a theorem into a histogram somebody can read.


6. The Thrash Threshold, at 128k Entries

A four way set associative table of thirty two thousand seven hundred and sixty eight sets and one hundred and thirty one thousand and seventy two entries, offered increasing numbers of distinct addresses. At thirty two thousand seven hundred and sixty eight offered, the load is one address per set, one hundred and forty two are refused, which is zero point four three five per cent, and occupancy reads twenty four point nine per cent. At sixty five thousand five hundred and thirty six, the load is two, three point seven five seven per cent are refused, occupancy forty eight point one. At one hundred and thirty one thousand and seventy two, which is the table's nominal capacity, the load is four, nineteen point five three seven per cent are refused, and occupancy reads eighty point five per cent. At two hundred and sixty two thousand one hundred and forty four the load is eight, fifty point seven four four per cent are refused, and occupancy reads ninety eight point five. The refused addresses flood permanently, and the occupancy metric's ceiling at the load that matters is below any default alarm threshold.32 768 sets × 4131 072 entriesLoad 1.000.435% refusedLoad 2.003.757% refusedLoad 4.0019.537% refusedLoad 8.0050.744% refusedOccupancy 80.5%at load 4.00Alarm at 90%cannot fire19.5% floodpermanently12
Figure 2 — the refusals begin at a quarter occupancy and reach 19.5% at nominal capacity, while the occupancy metric reads 80.5%.

Section 4 named the permanent cause. This section says when it starts, and the answer is a property of a hash rather than of a capacity.

Chapter 12.5 §9 derived the effective capacity of a four-way set-associative table at 8 192 entries. Take the same arithmetic to Chapter 23.3's 128k-entry table — 32 768 sets of 4 ways — and offer it addresses.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
stored = S × E[min(Load, W)],  Load ~ Poisson(n ÷ S),  S = 32 768,  W = 4
Offered addressesLoad per setStoredRefusedRefused share
32 7681.0032 6261420.435%
65 5362.0063 0742 4623.757%
98 3043.0087 84010 46410.645%
131 0724.00105 46525 60719.537%
163 8405.00116 75947 08128.737%
196 6086.00123 44873 16037.217%
262 1448.00129 123133 02150.744%

Offered exactly its nominal capacity — 131 072 addresses into 131 072 entries — the table stores 105 465 and refuses 25 607. Nineteen and a half per cent of the network's destinations flood every frame, permanently.

Three properties of this curve decide the chapter.

One — the refusals begin long before the table is full. At a load of 1.00 — 32 768 addresses in a 131 072-entry table, 25% occupancy — the table already refuses 142. Chapter 12.5 §7's observation at 8 192 entries scales: sets fill unevenly because the hash is not the operator's to choose.

Two — the refused share is a function of the LOAD and not of the table size. Chapter 13.4 §8 established why: the arithmetic is balls in bins over S sets and W ways, and the key's width appears nowhere in it. A 128k table at a load of 4.00 refuses the same 19.537% an 8k table does at a load of 4.00.

Three — the effective capacity is 80.5% and it is reached by refusing three addresses for every one it keeps. At 262 144 offered, the table holds 129 123 of 131 072 entries — 98.5% of nominaland has refused 133 021 addresses to get there.

Which gives the threshold a deployment needs, in the unit it has.

If the network offersThe table refusesAnd the flood share is
half the nominal capacity0.4%0.4%, plus broadcast
the nominal capacity19.5%19.5%
twice it50.7%50.7%

A datasheet saying "128 000 MAC addresses" describes the memory. Chapter 12.5 §9's sentence, at a larger scale: a four-way implementation of it holds 105 465 when asked for 131 072.


7. RTL 3 — The Table Capacity Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// table_capacity_model -- Chapter 12.5 Section 9's arithmetic at
// Chapter 23.3's scale, reporting the refused share as a flood rate.
//
// The output that matters is flood_share_is_permanent, because the
// refusals in this model do not age, retry or heal.
// ---------------------------------------------------------------------
module table_capacity_model
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] offered_addresses,
  input  logic [31:0] sets,
  input  logic [15:0] ways,
  input  logic [15:0] members,

  output logic [31:0] nominal_entries,
  output logic [31:0] load_x100_o,
  output logic [31:0] refused_ppm_o,
  output logic [31:0] stored_estimate,
  output logic [31:0] refused_estimate,
  output logic [15:0] effective_capacity_pct,
  output logic [31:0] occupancy_pct_x100,
  output logic [31:0] mean_copies_x1000,
  output logic        flood_share_is_permanent,
  output logic        occupancy_reads_healthy,
  output logic [31:0] c_evaluations
);
  always_comb begin
    nominal_entries = sets * 32'(ways);

    load_x100_o   = 32'(load_x100(int'(offered_addresses), int'(sets)));
    refused_ppm_o = 32'(refused_ppm(int'(offered_addresses), int'(sets)));

    refused_estimate = (offered_addresses * refused_ppm_o) / 32'd1_000_000;
    stored_estimate  = offered_addresses - refused_estimate;

    effective_capacity_pct = (offered_addresses == 0) ? 16'd100
                           : 16'((stored_estimate * 32'd100)
                                 / offered_addresses);

    // THE field that reads healthy while a fifth of the network floods.
    occupancy_pct_x100 = (nominal_entries == 0) ? 32'd0
                       : ((stored_estimate * 32'd10_000) / nominal_entries);

    // Chapter 12.4 Section 3's amplification, weighted by the refusals.
    mean_copies_x1000 = 32'd1000
      + ((refused_ppm_o * (32'(copies(int'(members))) - 32'd1)) / 32'd1000);

    // Section 4's table, row four: a refused insert is not retried.
    flood_share_is_permanent = (refused_ppm_o != 32'd0);

    // And the observation this chapter exists for.
    occupancy_reads_healthy = (occupancy_pct_x100 < 32'd9000)
                           && (refused_ppm_o > 32'd100_000);
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) c_evaluations <= '0;
    else        c_evaluations <= c_evaluations + 32'd1;
  end
endmodule

Classification: a balls-in-bins model whose two most useful outputs are a permanence flag and a contradiction detector.

What it teaches: that occupancy_reads_healthy is a real, reachable stateoccupancy below 90% while more than 10% of addresses are refused — and that it is exactly the situation at a load of 4.00: occupancy 80.5%, refusals 19.5%. A design that reports occupancy alone has told the operator the table is fine while a fifth of the destinations flood.

And it teaches that mean_copies_x1000 turns a table statistic into an egress demand. At 19.5% refusals and 64 members the module reports 13 11213.11 mean copies per frame against 1.0 on a healthy switch — which is Chapter 12.4 §3's amplification weighted by a quantity nobody measures.

Deliberately simplified: refused_ppm is the eight-anchor interpolation the package documents, worst error about six per cent at a load of 12. The model assumes every offered address is equally likely to be addressed, where in practice a refused address that nobody sends to costs nothing — so mean_copies_x1000 is an upper bound. And the arrival process is Poisson, which Chapter 12.5 §5's production note warns is optimistic: a hash reducing to the low index bits clusters severely in a real rack and refuses at 30% occupancy.

Production implication: the traffic-weighting simplification cuts both ways and the direction depends on something the switch could measure. If the refused addresses are ones nobody sends to — silent stations, stale entries — the flood cost is far below the model's estimate. If they are popular servers, it is at the estimate or worse, because a refused address that everybody sends to floods on every one of those frames. The measurement that distinguishes them is flooded frames per destination address, which is a per-address counter nobody builds — and the practical substitute is a small, sampled cache of the most-flooded destinations. Sixteen entries of 48 bits plus a 32-bit count is 1 280 bits, 1 280 BCE, 0.005 datapaths, and it turns "19.5% of addresses are refused" into "these sixteen destinations are costing you the switch."


8. Why the Occupancy Counter Reads Healthy

Section 6 established that a table at its nominal load refuses 19.5% of what it is offered. This section is about the instrument, and the instrument is the reason nobody notices.

A switch reports table occupancy: entries used against entries available. At a load of 4.00 — 131 072 addresses offered to a 131 072-entry four-way table — it reports:

Value
entries used105 465
entries available131 072
occupancy80.5%
addresses refused25 607
destinations flooding, permanently19.5%

The alarm threshold in every monitoring system is set at 90% and the metric's ceiling is 80.5%. The alarm cannot fire, and the failure it was built to catch is happening.

The ceiling is not a coincidence and it is not 80.5% by luck. Chapter 12.5 §9 derived it: the effective capacity of a four-way set-associative table is E[min(Load, W)] ÷ W at the load where the table stops accepting, and at a load of 4.00 that is 80.5%. Pushing more addresses at it raises occupancy toward 100% and raises refusals faster — at a load of 8.00 the table is 98.5% occupied and refusing 50.7%.

OfferedOccupancyRefusedWould a 90% alarm fire?
32 76824.9%0.4%no
65 53648.1%3.8%no
131 07280.5%19.5%NO
196 60894.2%37.2%yes — far too late
262 14498.5%50.7%yes

Row three is the state a deployment sits in and the alarm is silent. Row four is where the alarm fires, and by then more than a third of the destinations are flooding.

So the metric is not merely a lagging indicator; it is a metric whose informative range is above the range the design operates in.


9. RTL 4 — The Miss Amplifier

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// miss_amplifier -- how a table's refusal rate becomes an egress
// demand, and what the occupancy metric says while it does.
//
// The output that matters is alarm_can_fire, which is low across the
// whole operating range of a four-way table -- the metric's ceiling
// is beneath the threshold, and Section 20's refused property is the
// assertion built on it.
// ---------------------------------------------------------------------
module miss_amplifier
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] refused_ppm_i,
  input  logic [31:0] occupancy_pct_x100_i,
  input  logic [15:0] occupancy_alarm_pct,
  input  logic [15:0] members,
  input  logic [31:0] frames_per_s_k,

  output logic [31:0] mean_copies_x1000,
  output logic [31:0] egress_demand_pps_k,
  output logic [31:0] demand_vs_flat_x100,
  output logic [31:0] effective_ceiling_pct,
  output logic        alarm_can_fire,
  output logic        alarm_is_silent_at_failure,
  output logic        refusal_is_an_event,
  output logic [31:0] c_silent_failures
);
  always_comb begin
    // Chapter 12.4 Section 3's amplification, weighted by the refusals.
    mean_copies_x1000 = 32'd1000
      + ((refused_ppm_i * (32'(copies(int'(members))) - 32'd1)) / 32'd1000);

    egress_demand_pps_k = (frames_per_s_k * mean_copies_x1000) / 32'd1000;

    demand_vs_flat_x100 = mean_copies_x1000 / 32'd10;

    // Chapter 12.5 Section 9's effective capacity for this structure.
    // At four ways it is 80.5%, and it is a CEILING.
    effective_ceiling_pct = 32'd80;

    // THE finding. An alarm above the ceiling is unreachable.
    alarm_can_fire = (32'(occupancy_alarm_pct) <= effective_ceiling_pct);

    alarm_is_silent_at_failure = !alarm_can_fire
                              && (refused_ppm_i > 32'd100_000);

    // The metric that CAN see it is an event, not a level.
    refusal_is_an_event = 1'b1;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)                          c_silent_failures <= '0;
    else if (alarm_is_silent_at_failure) c_silent_failures <= c_silent_failures + 32'd1;
  end
endmodule

Classification: an amplifier with a ceiling constant, whose output is a statement about an instrument rather than about traffic.

What it teaches: that alarm_can_fire is low for any threshold above 80%, and that 90% is the value every monitoring system ships with. The module makes the ceiling an explicit constant so the comparison is visible: a level alarm set above a metric's ceiling is not a conservative alarm, it is an absent one.

And it teaches that demand_vs_flat_x100 reports 1 311 at a 19.5% refusal rate and 64 members13.11 times the egress demand of a switch that forwards everything to one portwhile occupancy reads 80.5%. The two numbers describe the same switch and only one of them is alarming.

Deliberately simplified: effective_ceiling_pct is hard-coded at 80 for a four-way structure, where Chapter 12.5 §10's table gives 63.2% direct-mapped, 72.9% two-way, 86.0% eight-way and 90.1% sixteen-way — so the right constant depends on the associativity and a general model would compute it. The amplifier assumes every refused address is addressed at the same rate as every stored one, which Section 7's production note identifies as an upper bound. And frames_per_s_k is a scalar where the flood's own copies contend with the traffic being measured.

Production implication: the ceiling's dependence on associativity is worth exposing as a register, because it makes the alarm threshold derivable rather than inherited. A part that reports its effective capacity — E[min(Load, W)] ÷ W for its own W, a constant per design — lets a monitoring system set the threshold below it. At four ways the alarm belongs at 70%, not 90; at sixteen ways it could sit at 85. One read-only register, and the alarm becomes reachable. Without it every deployment inherits a 90% default that is correct for a memory and wrong for every set-associative structure, which is the same shape as Chapter 14.2 §9's 75% watermark: a fraction of a resource standing in for a derivation nobody performed.


10. Stale Entries, and the Sets They Hold

A container fleet tearing down ten thousand interfaces an hour reissues an address every zero point three six seconds. Against chapter twelve point five's three hundred second ageing interval that leaves eight hundred and thirty three forwarding entries at any instant for stations that no longer exist. As a share of a one hundred and thirty one thousand entry table that is zero point six per cent and looks negligible, but the scarce resource is sets with room rather than entries. The eight hundred and thirty three stale keys raise the load per set from four point zero zero zero to four point zero two five and the refused share from nineteen point five three seven per cent to nineteen point seven seven three, which is four hundred and seventy four additional refusals, one for every one point seven six stale entries held. And the asymmetry is the finding: the stale entry ages out after three hundred seconds while the refusal it caused persists until something re-attempts the insert, which for a station that only answers may be never.10 000teardowns/hone every 0.36 s300 s ageingChapter 12.5 §5833 stale entries0.6% of the tableSets, not entriesthe scarce resourceLoad 4.000 →4.02519.537% → 19.773%474 more refusals1 per 1.76 staleStale: 300 sthen it ages outRefusal:permanentnothing retries it12
Figure 3 — the stale entry ages out in 300 seconds and the refusal it caused may never be retried.

Sections 6 and 8 assumed every entry belongs to a station that exists. Chapter 25.2 §8 established that in a container fleet many of them do not.

That chapter's arithmetic: a fleet reissuing addresses holds stale forwarding entries in proportion to its churn against Chapter 12.5 §5's 300-second ageing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
teardown interval = 3 600 ÷ churn per hour
stale entries held = 300 ÷ teardown interval
ChurnTeardown everyStale entriesOf 131 072 entries
100/hour36 s8.30.006%
1 000/hour3.6 s83.30.064%
10 000/hour0.36 s833.30.636%

As a share of the table it is negligible, and that is the wrong denominator.

Chapter 12.5 §7's arithmetic says the scarce resource is not entries; it is SETS WITH ROOM. A stale entry does not consume 1/131 072 of the table's capacity — it consumes one way of one set, and if that set was already at three of four ways, it is the difference between accepting the next address hashing there and refusing it.

Work the effect at a load of 4.00.

Without stale entriesWith 833 stale
live addresses offered131 072131 072
plus stale833
total keys competing131 072131 905
load per set4.0004.025
refused share19.537%19.773%
refusals25 60726 081
extra refusals474

Eight hundred and thirty-three stale entries cause four hundred and seventy-four additional refusalsone for every 1.76 dead entries heldand the ones that fall on live stations flood forever.

The harm of a stale entry is not the memory it occupies. It is the live address it displaces, and the displacement is permanent while the stale entry is transient.


11. RTL 5 — The Stale-Entry Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// stale_entry_model -- Chapter 25.2 Section 8's stale entries, priced
// in the unit Chapter 12.5 Section 7 says is scarce: sets with room.
//
// The output that matters is live_refusals_caused, because it is the
// harm, and it is not the memory the stale entries occupy.
// ---------------------------------------------------------------------
module stale_entry_model
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [31:0] churn_per_hour,
  input  logic [15:0] ageing_seconds,
  input  logic [31:0] live_addresses,
  input  logic [31:0] sets,
  input  logic        eviction_permitted,

  output logic [31:0] teardown_interval_ms,
  output logic [31:0] stale_entries,
  output logic [31:0] stale_share_ppm,
  output logic [31:0] keys_competing,
  output logic [31:0] refused_without_stale,
  output logic [31:0] refused_with_stale,
  output logic [31:0] live_refusals_caused,
  output logic [31:0] dead_per_live_x100,
  output logic        refusal_outlives_the_cause,
  output logic        eviction_would_fix_it,
  output logic [31:0] c_displacements
);
  always_comb begin
    teardown_interval_ms = (churn_per_hour == 0) ? 32'hFFFF_FFFF
                         : (32'd3_600_000 / churn_per_hour);

    stale_entries = (teardown_interval_ms == 0) ? 32'd0
                  : ((32'(ageing_seconds) * 32'd1000) / teardown_interval_ms);

    stale_share_ppm = (live_addresses == 0) ? 32'd0
                    : ((stale_entries * 1_000_000) / live_addresses);

    keys_competing = live_addresses + stale_entries;

    refused_without_stale = (live_addresses
                             * 32'(refused_ppm(int'(live_addresses),
                                               int'(sets)))) / 32'd1_000_000;
    refused_with_stale    = (keys_competing
                             * 32'(refused_ppm(int'(keys_competing),
                                               int'(sets)))) / 32'd1_000_000;

    live_refusals_caused = (refused_with_stale > refused_without_stale)
                         ? (refused_with_stale - refused_without_stale)
                         : 32'd0;

    dead_per_live_x100 = (live_refusals_caused == 0) ? 32'd0
                       : ((stale_entries * 32'd100) / live_refusals_caused);

    // THE asymmetry: the cause ages out and the consequence does not.
    refusal_outlives_the_cause = 1'b1;

    // Chapter 12.5 Section 11's LRU eviction turns a permanent refusal
    // into a transient one, at the cost of being pushable.
    eviction_would_fix_it = !eviction_permitted;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n)                        c_displacements <= '0;
    else if (live_refusals_caused != 0) c_displacements <= c_displacements
                                                        + live_refusals_caused;
  end
endmodule

Classification: a difference between two evaluations of the same capacity curve, with the difference being the harm.

What it teaches: that live_refusals_caused is 474 for 833 stale entries at a load of 4.00, so dead_per_live_x100 reports 175about 1.76 dead entries displace one address. The ratio is not one-to-one because the stale entries land on random sets and only those already at the ways limit cause a refusal.

And it teaches that refusal_outlives_the_cause is a hard-wired one. The stale entry is gone in 300 seconds; the refused station floods until something re-attempts its insert, and Chapter 12.2's silent-station case means that may be never. The cause is transient and the effect is not, which is the asymmetry Section 10's callout develops.

Deliberately simplified: stale entries are assumed uniformly distributed across sets, which they are, and the refusal difference is computed from the aggregate curve rather than per set — so the model gives an expectation and a particular table's answer varies. eviction_permitted is a single bit where Chapter 12.5 §11's policy has several variants. And the model does not distinguish a stale entry whose set was already full — which causes no additional refusal at all — from one that filled the last way.

Production implication: the per-set view is where the fix lives and it is cheaper than it sounds. A sweep that ages entries in the fullest sets first, rather than uniformly by timestamp, removes stale entries from exactly the sets where they are doing harm — and Chapter 12.5 §13's ageing sweep already walks every set, so the change is to order the walk by occupancy rather than by index. The cost is a per-set occupancy count, which Section 5's production note already argued for at 131 072 bits, 0.46 datapaths, and the two features share it. One structure, two uses: an operator-visible histogram of set fullness, and a sweep that prioritises the sets that are refusing.


12. What Storm Control Actually Bounds

Chapter 12.4 §11 called storm control the one mitigation available at this layer, and §12 asked what a setting actually delivers. This section answers it at 64 ports and 100 Gb/s.

Storm control is a rate limiter on flooded traffic, per ingress port. A setting of p per cent permits p per cent of the port's line rate to be flooded and discards the excess.

SettingFlood admitted per portEgress demand at 63 copiesOf the 9.524 Gpps budget
100%148.81 Mpps9.375 Gpps98.4%
10%14.88 Mpps0.938 Gpps9.8%
1%1.49 Mpps0.094 Gpps0.98%
0.1%0.15 Mpps0.009 Gpps0.10%

A 1% setting caps one port's flood contribution at 0.98% of the switch's frame budget, which is the mechanism working exactly as intended.

And then the question Chapter 12.4 §12 asked: what does that do to the traffic it limits?

BroadcastUnknown unicast
what is discardedARP requests, DHCP discovery, neighbour discoveryordinary application traffic to a station the table refused
the consequencea retry, usually invisiblea connection that stalls
who sees itthe protocol's own timersthe user
is a limiter appropriate?yes — the rate is a host property and boundedNO — the rate is a table property and the traffic is legitimate

Row four is the uncomfortable result and it inverts the usual configuration. Storm control on broadcast is correct: the traffic is control-plane, the protocols retry, and a runaway rate is a fault. Storm control on unknown unicast discards real user data to work around a table that is refusing inserts, and the frames it drops are the ones a correct switch would have delivered to one port.

A limiter on unknown unicast converts a bandwidth problem into a loss problem, and the traffic it drops is exactly the traffic whose destination the switch failed to learn.

So the correct configuration is asymmetric and it is usually set the other way round, because the two are the same replication and a configuration interface that offers one knob invites one setting.


13. RTL 6 — The Storm Budget

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// storm_budget -- what a storm-control setting admits, and which
// traffic it discards to get there.
//
// The output that matters is discards_legitimate_traffic, which is
// high whenever the limiter is applied to unknown unicast -- because
// that traffic's destination exists and the switch failed to learn it.
// ---------------------------------------------------------------------
module storm_budget
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        tick_us,

  input  logic [15:0] setting_pct_x10,       // tenths of a per cent
  input  logic [31:0] port_pps_k,
  input  logic [15:0] members,
  input  logic [31:0] budget_pps_k,
  input  logic        applies_to_broadcast,
  input  logic        applies_to_unknown,
  input  logic        frame_flooded,
  input  logic  [1:0] cause_i,

  output logic [31:0] admitted_pps_k,
  output logic [31:0] demand_pps_k,
  output logic [31:0] budget_share_ppm,
  output logic        limiter_active,
  output logic        discards_legitimate_traffic,
  output logic        treats_the_cause,
  output logic [31:0] c_discarded_bcast,
  output logic [31:0] c_discarded_unknown,
  output logic [31:0] c_admitted
);
  logic [31:0] tokens;

  always_comb begin
    admitted_pps_k   = (port_pps_k * 32'(setting_pct_x10)) / 32'd1000;
    demand_pps_k     = admitted_pps_k * 32'(copies(int'(members)));
    budget_share_ppm = (budget_pps_k == 0) ? 32'd0
                     : ((demand_pps_k * 1_000_000) / budget_pps_k);

    limiter_active = (setting_pct_x10 < 16'd1000);

    // Section 12's table, row four. A broadcast the limiter drops is
    // a control-plane frame whose protocol retries; an unknown unicast
    // it drops is application data to a station that exists.
    discards_legitimate_traffic = limiter_active && applies_to_unknown;

    // And neither setting changes how many addresses the table refused.
    treats_the_cause = 1'b0;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      tokens <= '0; c_discarded_bcast <= '0;
      c_discarded_unknown <= '0; c_admitted <= '0;
    end else begin
      if (tick_us) tokens <= admitted_pps_k / 32'd1000;
      if (frame_flooded) begin
        if (tokens != 32'd0) begin
          tokens     <= tokens - 32'd1;
          c_admitted <= c_admitted + 32'd1;
        end else begin
          if ((cause_i == 2'(FLOOD_BCAST)) && applies_to_broadcast)
            c_discarded_bcast <= c_discarded_bcast + 32'd1;
          if ((cause_i == 2'(FLOOD_UNKNOWN)) && applies_to_unknown)
            c_discarded_unknown <= c_discarded_unknown + 32'd1;
        end
      end
    end
  end
endmodule

Classification: a token bucket with two discard counters, and the separation between them is the module's contribution.

What it teaches: that treats_the_cause is a hard-wired zero. A limiter caps the damage and changes no property of the table: the same 25 607 addresses are refused before and after, and the same destinations flood — they simply flood less often, because their frames are being dropped.

And it teaches that c_discarded_bcast and c_discarded_unknown need separate counters because they mean opposite things. A discarded broadcast is the mechanism working; a discarded unknown unicast is user data lost to work around a table failure. One counter for both reports a rate and hides which of the two it is.

Deliberately simplified: the token bucket refills once per microsecond tick with no burst allowance, where Chapter 12.4 §11's limiter has a burst parameter that dominates the behaviour on bursty traffic. Multicast is not limited at all in this model, though real implementations usually offer a third setting. And the limiter is per ingress port where the damage is an aggregate across ports — sixty-four ports each flooding at 1% together demand 63% of the budget, and no per-port setting bounds that sum.

Production implication: the per-port-against-aggregate gap is the one that produces a surprise during an incident. A deployment that sets 1% per port and reasons that flooding is capped at 1% has capped it at 63%, because the amplification applies to each port independently and the budget is shared. The bound a switch actually needs is an aggregate flood budget, which no standard configuration model offers: the natural form is a second token bucket at the replication engine, shared across ingress ports, costing one counter and one comparison. Without it the per-port setting is correct about a quantity nobody cares about, and the arithmetic that matters — ports × setting × copies — is left for an operator to do during the incident.


14. What a Flooding Claim Must Never Do

Five prohibitions.

#NeverBecause
1report table occupancy as table healthSection 8: the ceiling is 80.5% and every default alarm sits at 90%
2count broadcast and unknown-unicast floods in one counterSection 4: identical in the switch, opposite at the receiver, different causes, different fixes
3apply storm control to unknown unicast as a remedySection 12: it discards legitimate data to work around a table refusing inserts
4reason about a per-port limiter as an aggregate boundSection 13: 64 ports at 1% each is 63% of the budget
5treat a stale entry as costing only its own memorySection 10: 833 stale entries cause 474 additional refusals

Row one is the one this chapter exists for and it is a one-register fix: report the effective capacity beside the occupancy.


15. RTL 7 — Broadcast Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// bcast_telemetry -- the counters that let an operator tell a switch
// that is forwarding from one that is flooding.
//
// Design rule: a level cannot see this failure, so every counter here
// is an EVENT or a per-set distribution.
// ---------------------------------------------------------------------
module bcast_telemetry
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        clear,

  input  logic        frame_valid,
  input  logic        is_flooding_i,
  input  logic  [1:0] cause_i,
  input  logic [15:0] copies_i,
  input  logic        insert_attempted,
  input  logic        insert_refused,
  input  logic [15:0] set_index,
  input  logic  [3:0] set_occupancy,
  input  logic [15:0] ingress_port,

  output logic [47:0] c_frames,
  output logic [47:0] c_flooded,
  output logic [47:0] c_bcast_o,
  output logic [47:0] c_unknown_o,
  output logic [47:0] c_mcast_o,
  output logic [47:0] c_copies_emitted,
  output logic [47:0] c_insert_refused,
  output logic [47:0] c_sets_at_capacity,
  output logic [31:0] flood_share_ppm,
  output logic [31:0] mean_copies_x1000,
  output logic [31:0] refused_rate_ppm,
  output logic        refusals_are_occurring,
  output logic        occupancy_would_have_alarmed,
  output logic [15:0] worst_ingress_port
);
  logic [47:0] per_port_flood [64];

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || clear) begin
      c_frames <= '0; c_flooded <= '0; c_bcast_o <= '0; c_unknown_o <= '0;
      c_mcast_o <= '0; c_copies_emitted <= '0; c_insert_refused <= '0;
      c_sets_at_capacity <= '0; worst_ingress_port <= '0;
      for (int i = 0; i < 64; i++) per_port_flood[i] <= '0;
    end else begin
      if (frame_valid) begin
        c_frames <= c_frames + 48'd1;
        if (is_flooding_i) begin
          c_flooded        <= c_flooded + 48'd1;
          c_copies_emitted <= c_copies_emitted + 48'(copies_i);
          // Section 3's production note: the counter that names the
          // cause is flooded frames per INGRESS port.
          per_port_flood[ingress_port[5:0]] <=
            per_port_flood[ingress_port[5:0]] + 48'd1;
          if (per_port_flood[ingress_port[5:0]] >
              per_port_flood[worst_ingress_port[5:0]])
            worst_ingress_port <= ingress_port;
          case (cause_i)
            2'(FLOOD_BCAST):   c_bcast_o   <= c_bcast_o + 48'd1;
            2'(FLOOD_MCAST):   c_mcast_o   <= c_mcast_o + 48'd1;
            default:           c_unknown_o <= c_unknown_o + 48'd1;
          endcase
        end
      end
      // The event a level cannot see.
      if (insert_attempted && insert_refused)
        c_insert_refused <= c_insert_refused + 48'd1;
      if (set_occupancy == 4'd4)
        c_sets_at_capacity <= c_sets_at_capacity + 48'd1;
    end
  end

  always_comb begin
    flood_share_ppm = (c_frames == 0) ? 32'd0
                    : 32'((c_flooded * 48'd1_000_000) / c_frames);

    mean_copies_x1000 = (c_frames == 0) ? 32'd0
                      : 32'((c_copies_emitted * 48'd1000) / c_frames);

    refused_rate_ppm = (c_frames == 0) ? 32'd0
                     : 32'((c_insert_refused * 48'd1_000_000) / c_frames);

    // Section 8: this is nonzero long before any level alarm.
    refusals_are_occurring = (c_insert_refused != 48'd0);

    // And the counterfactual, reported so the gap is visible.
    occupancy_would_have_alarmed = 1'b0;
  end
endmodule

Classification: eight counters, of which the two that see the failure are an event count and a per-set distribution.

What it teaches: that refusals_are_occurring is nonzero at a load of 1.00 — 142 refusals in a table 24.9% occupiedand every level alarm is silent. Chapter 12.5 §8's argument for watching the distribution rather than the total, rendered as the difference between a counter that can report the fault and one that cannot.

And it teaches that worst_ingress_port is the attribution Section 3's production note argued for. A per-port drop count ranks the victims; flooded frames per ingress port names the source, and Chapter 14.3 §8's second_order_pct made exactly this point about a different mechanism: the obvious counter reports where the shortage is felt and the useful one names a party other than the one reporting.

Deliberately simplified: occupancy_would_have_alarmed is hard-wired zero rather than computed, which makes it a claim and not a check — it is there so the counterfactual has a name in the register map. The per-port array is declared for 64 ports and indexed by the low six bits. And c_sets_at_capacity increments on every observation of a full set rather than counting distinct sets, so it is a rate rather than a census — the census needs Chapter 12.5 §13's sweep to walk the table.

Production implication: the census-against-rate distinction decides whether the number is actionable. A rate of full-set observations rises with traffic and tells an operator nothing about the table's state; a census — how many of 32 768 sets currently hold four entries — is a direct measurement of how close the table is to refusing. Chapter 12.5 §13's ageing sweep already visits every set on a timer, so the census costs one accumulator reset at the start of the sweep and one increment per full set — and it produces the number Section 8's callout says is the only level that can see the failure. One counter, folded into a sweep that already runs.


16. RTL 8 — The Flooding Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// bcast_conformance -- the checks that hold a switch's flooding
// behaviour to what it can actually claim.
//
// There is no check that the switch does not flood, because Chapter
// 12.1 Section 13's class 49 established that flooding is the
// specified response to an unknown destination.
// ---------------------------------------------------------------------
module bcast_conformance
  import bcastmyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] occupancy_alarm_pct,
  input  logic [15:0] effective_capacity_pct,
  input  logic        counters_merged,
  input  logic        storm_control_on_unknown,
  input  logic [15:0] per_port_setting_pct_x10,
  input  logic [15:0] ports_i,
  input  logic [15:0] aggregate_budget_pct,
  input  logic        refusals_reported,
  input  logic        claims_no_flooding,
  input  logic [47:0] c_flooded_i,

  output logic        v_alarm_unreachable,
  output logic        v_counters_merged,
  output logic        v_limiter_on_unknown,
  output logic        v_aggregate_unbounded,
  output logic        v_refusals_unreported,
  output logic        v_claims_no_flooding,
  output logic  [5:0] violations,
  output logic        conformant
);
  logic [31:0] aggregate_pct_x10;

  always_comb begin
    // 1. Section 8 -- a threshold above the ceiling cannot fire.
    v_alarm_unreachable = (occupancy_alarm_pct > effective_capacity_pct);

    // 2. Prohibition 2 -- two causes, two fixes, one counter.
    v_counters_merged = counters_merged;

    // 3. Section 12 -- the limiter discards legitimate data.
    v_limiter_on_unknown = storm_control_on_unknown;

    // 4. Section 13 -- a per-port setting is not an aggregate bound.
    aggregate_pct_x10     = 32'(per_port_setting_pct_x10) * 32'(ports_i);
    v_aggregate_unbounded = (aggregate_pct_x10
                             > (32'(aggregate_budget_pct) * 32'd10));

    // 5. Section 15 -- the only metric that can see it.
    v_refusals_unreported = !refusals_reported;

    // 6. Chapter 12.1 Section 13's class 49: flooding is specified.
    v_claims_no_flooding = claims_no_flooding && (c_flooded_i != 48'd0);

    violations = { v_claims_no_flooding, v_refusals_unreported,
                   v_aggregate_unbounded, v_limiter_on_unknown,
                   v_counters_merged, v_alarm_unreachable };
    conformant = (violations == 6'b000000);
  end
endmodule

Classification: six checks, of which the first is about an instrument's range rather than a design's behaviour.

What it teaches: that v_alarm_unreachable compares a threshold against a ceiling and fires for any occupancy alarm above 80% on a four-way table. That is every default in every monitoring system, and the check turns a silent misconfiguration into a boot-time message.

And it teaches that v_claims_no_flooding is the check against the myth itself. Chapter 12.1 §13's class 49 established that asserting a switch never floods is asserting the absence of a behaviour the design was built to perform; this monitor does not assert it and catches a design that claims it.

Deliberately simplified: effective_capacity_pct must be supplied and is a design constant — Chapter 12.5 §10's table gives 63.2%, 72.9%, 80.5%, 86.0% and 90.1% for one, two, four, eight and sixteen ways — so a part that does not expose it leaves this check unbindable. counters_merged and refusals_reported are configuration facts the monitor trusts. And v_aggregate_unbounded assumes every port is configured identically, which a real deployment does not guarantee.

Production implication: effective_capacity_pct is the register this chapter most wants and it costs nothing. It is a compile-time constant of the table's geometry — one number per design, derivable by the designer in an afternoon with Chapter 12.5 §9's balls-in-bins sum — and exposing it read-only lets every downstream tool set a reachable threshold. Without it each deployment inherits 90%, which is right for a memory and wrong for every set-associative structure, and the failure it was meant to catch happens at 80.5% with the alarm silent. The general habit is the one Chapter 25.2 §3's production note named for a different structure: a design that cannot report its own limits has made them unauditable, and the fix is a read-only register rather than a document.


17. The Two Floods, Priced Side by Side

A decision procedure using two counters a switch already computes. Read the refused insert count first, because it is the cause. If it is zero and the flood share is low, nothing is wrong. If it is zero and the flood share is high, the flooding is broadcast and the problem is the broadcast domain's size, for which segmentation is the correct fix. If it is nonzero and the flood share is high, the problem is the forwarding table and the fix is more entries or more associativity. And if it is nonzero while the flood share is low, the refusals are falling on addresses nobody sends to, which is worth watching but not acting on. Four rows, two counters, and a verdict.c_insert_refusedread this firstZerothe table is copingNonzerothe table is refusingFlood share lownothing is wrongFlood share highbroadcast domainFlood share highthe tableSegmentChapter 25.4 §6More entries orways2.24% of the switch12
Figure 4 — two counters and a verdict, and every deployment has both counters and reads neither.

Everything this chapter derived, at 64 ports and 100 Gb/s.

BroadcastUnknown unicast
copies per frame6363
egress demand at line rate9.375 Gpps — 98.4% of the budgetthe same
cost to each receiving hostfull — every frame reaches the CPUzero — an exact 48-bit compare discards it
who complainsevery user, within minutesnobody, ever
the causea host chose to send itthe table refused an insert
the rate is bounded bywhat hosts sendnothing the operator configured
durationthe sender's ownpermanent, for a refused address
storm control iscorrecta way of discarding user data
the real fixChapter 12.4 §10's domain sizea bigger or more associative table

Rows three and four together are why the myth survives, and rows five to seven are why the second flood is the dangerous one.

And the table arithmetic, at Chapter 23.3's 128k entries.

OfferedLoadStoredRefusedOccupancyMean copies/frame
32 7681.0032 6250.4%24.9%1.27
65 5362.0063 0743.8%48.1%3.33
131 0724.00105 46519.5%80.5%13.11
196 6086.00123 43737.2%94.2%24.07
262 1448.00129 12350.7%98.5%32.46

Row three is a switch at its rated capacity, with an occupancy counter reading 80.5%, no alarm firing, and thirteen times the egress demand of a switch that forwards everything.


18. What the Correction Assumes

Eight assumptions.

#AssumptionIf it is false
1Chapter 23.3's 64-port 100 Gb/s partthe amplification is N − 1 either way; the budget share converges on 1
2a four-way 32 768-set tableSection 6's callout: the refused share is 36.8% direct-mapped and 9.9% at sixteen ways
3addresses arrive Poisson across setsChapter 12.5 §5: a poor hash clusters and refuses at 30% occupancy
4the eight-anchor interpolation of the capacity curveworst error about six per cent, at a load of 12
5every refused address is addressed at the same rateSection 7's production note: the flood cost is an upper bound
6Chapter 12.5 §5's 300-second ageingSection 10's stale-entry count scales inversely with it
7minimum-size frames for the frame-rate arithmeticlarger frames lower the packet rate and the budget share
8BCE prices the table and the countersSection 19 examines it and it holds cleanly

Assumption 3 is the one that makes every figure in this chapter optimistic. A Poisson arrival is the best case for a hash, and Chapter 12.5 §5's production note gives the failure: a hash that reduces to the low index bits works perfectly in simulation with random addresses and clusters severely in a real rack, refusing inserts at 30% occupancy rather than 80.5%. Every refusal figure here is a floor.


19. The Cost, Accounted — in BCE

This chapter's blocks.

BlockFlopsBCE× the datapath
flood_amplification_model326400.002
unknown_unicast_classifier1282 5600.009
table_capacity_model326400.002
miss_amplifier326400.002
stale_entry_model326400.002
storm_budget1282 5600.009
bcast_telemetry, 64 ports3 45669 1200.244
bcast_conformance0 — combinational00
this chapter's additions3 84076 8000.271

bcast_telemetry is 90.0% of the total and most of it is the per-ingress-port flood array64 counters of 48 bits, priced as registers at 20 BCE per bit. A production design puts that bank in an SRAM with a read-modify-write and pays 3 072 BCE instead of 61 440, which is the second table's figure and the reason the two differ by twenty.

And the structures the chapter argues for.

BCE× the datapath% of the switch
per-set occupancy, 32 768 × 4 bits131 0720.4630.023%
per-ingress-port flood counters, 64 × 48, as an array3 0720.0110.001%
a 16-entry most-flooded cache1 2800.0050.000%
an effective-capacity register80.000
all four135 4320.4780.024%
for comparison — the MAC table1.26 × 10⁷44.42.24%
doubling the table to 256k+1.26 × 10⁷+44.4+2.24%

Every diagnostic this chapter asks for costs 0.024% of the switch together, and the fix they diagnose costs 2.24%. The instruments are a hundredth of the remedy, which is the same ratio Chapter 24.2 §19 found and the same argument for keeping all of them.


20. Properties Worth Asserting, and One Worth Refusing

Fifty-one properties in six groups, and the refused one is an alarm whose metric cannot reach its threshold.

Group A — the replication (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A1. Copies are the member count less one.
p_rp_copies: assert property (@(posedge clk) disable iff (!rst_n)
  (members != 16'd0) |-> (copies_o == members - 16'd1));

// A2. Emissions are copies times the ingress rate.
p_rp_emissions: assert property (@(posedge clk) disable iff (!rst_n)
  (emissions_pps_k == port_pps_k * 32'(copies_o)));

// A3. One flooding port at 64 members takes almost the whole budget.
p_rp_budget: assert property (@(posedge clk) disable iff (!rst_n)
  ((members == 16'd64) && (port_gbps == 16'd100) &&
   (slot_octets == 16'd84) && (budget_pps_k == 32'd9_524_000))
    |-> (budget_share_ppm >= 32'd980_000));

// A4. And it exceeds the budget.
p_rp_exceeds: assert property (@(posedge clk) disable iff (!rst_n)
  (emissions_pps_k > budget_pps_k) |-> exceeds_budget);

// A5. A bigger switch does not help.
p_rp_bigger_no: assert property (@(posedge clk) disable iff (!rst_n)
  (bigger_switch_helps == 1'b0));

// A6. The egress share converges on unity, not on a half.
p_rp_converges: assert property (@(posedge clk) disable iff (!rst_n)
  (members >= 16'd8) |-> (egress_share_ppm >= 32'd875_000));

// A7. Egress bandwidth is the port rate times the copies.
p_rp_bandwidth: assert property (@(posedge clk) disable iff (!rst_n)
  (egress_bw_gbps == 32'(port_gbps) * 32'(copies_o)));

// A8. A single-member VLAN floods nowhere.
p_rp_single: assert property (@(posedge clk) disable iff (!rst_n)
  (members == 16'd1) |-> (copies_o == 16'd0));

// A9. Over-budget states are counted.
p_rp_counted: assert property (@(posedge clk) disable iff (!rst_n)
  exceeds_budget |=> (c_over_budget == $past(c_over_budget) + 32'd1));

Group B — the flood causes (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// B1. A broadcast destination always floods.
p_fc_bcast: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_valid && dest_is_broadcast) |-> (cause == 2'(FLOOD_BCAST)));

// B2. A unicast miss floods too.
p_fc_unknown: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_valid && !lookup_hit && !dest_is_multicast && !dest_is_broadcast)
    |-> (cause == 2'(FLOOD_UNKNOWN)));

// B3. A hit does not flood.
p_fc_hit: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_valid && lookup_hit && !dest_is_broadcast) |-> !is_flooding);

// B4. Only a refused insert is permanent.
p_fc_permanent: assert property (@(posedge clk) disable iff (!rst_n)
  reason_is_permanent |-> ((cause == 2'(FLOOD_UNKNOWN)) && insert_was_refused));

// B5. An unknown unicast costs the host nothing.
p_fc_host_zero: assert property (@(posedge clk) disable iff (!rst_n)
  (cause == 2'(FLOOD_UNKNOWN)) |-> host_cost_is_zero);

// B6. And only a broadcast will be noticed.
p_fc_notice: assert property (@(posedge clk) disable iff (!rst_n)
  anybody_will_notice |-> (cause == 2'(FLOOD_BCAST)));

// B7. Copies are emitted only when flooding.
p_fc_copies_gated: assert property (@(posedge clk) disable iff (!rst_n)
  !is_flooding |-> (copies_now == 16'd1));

// B8. The permanent counter is a subset of the unknown counter.
p_fc_subset: assert property (@(posedge clk) disable iff (!rst_n)
  (c_unknown_permanent <= c_unknown));

// B9. Causes are mutually exclusive.
p_fc_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
  frame_valid |-> !(dest_is_broadcast && dest_is_multicast));

Group C — table capacity (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// C1. The nominal entry count is sets times ways.
p_tc_nominal: assert property (@(posedge clk) disable iff (!rst_n)
  (nominal_entries == sets * 32'(ways)));

// C2. Refusals rise with the load.
p_tc_monotone: assert property (@(posedge clk) disable iff (!rst_n)
  (load_x100_o > $past(load_x100_o)) |-> (refused_ppm_o >= $past(refused_ppm_o)));

// C3. At a load of 4.00 the refused share is about 19.5%.
p_tc_at_nominal: assert property (@(posedge clk) disable iff (!rst_n)
  (load_x100_o == 32'd400) |-> ((refused_ppm_o > 32'd190_000) &&
                                (refused_ppm_o < 32'd201_000)));

// C4. Refusals begin well below full occupancy.
p_tc_early: assert property (@(posedge clk) disable iff (!rst_n)
  (load_x100_o >= 32'd100) |-> (refused_ppm_o != 32'd0));

// C5. Stored plus refused is what was offered.
p_tc_partition: assert property (@(posedge clk) disable iff (!rst_n)
  (stored_estimate + refused_estimate == offered_addresses));

// C6. Occupancy never exceeds the nominal entry count.
p_tc_occupancy_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  (stored_estimate <= nominal_entries));

// C7. A refused insert is permanent.
p_tc_permanent: assert property (@(posedge clk) disable iff (!rst_n)
  (refused_ppm_o != 32'd0) |-> flood_share_is_permanent);

// C8. The contradiction state is reachable.
p_tc_contradiction: assert property (@(posedge clk) disable iff (!rst_n)
  ((occupancy_pct_x100 < 32'd9000) && (refused_ppm_o > 32'd100_000))
    |-> occupancy_reads_healthy);

// C9. Mean copies rise with refusals.
p_tc_copies: assert property (@(posedge clk) disable iff (!rst_n)
  (refused_ppm_o > $past(refused_ppm_o)) |->
    (mean_copies_x1000 >= $past(mean_copies_x1000)));

Group D — stale entries and the amplifier (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// D1. Stale entries scale with churn against the ageing interval.
p_se_count: assert property (@(posedge clk) disable iff (!rst_n)
  (churn_per_hour != 32'd0) |->
    (stale_entries == (32'(ageing_seconds) * 32'd1000) / teardown_interval_ms));

// D2. They add to the keys competing for sets.
p_se_competing: assert property (@(posedge clk) disable iff (!rst_n)
  (keys_competing == live_addresses + stale_entries));

// D3. And cause additional refusals.
p_se_causes: assert property (@(posedge clk) disable iff (!rst_n)
  (stale_entries != 32'd0) |-> (refused_with_stale >= refused_without_stale));

// D4. The refusal outlives its cause.
p_se_outlives: assert property (@(posedge clk) disable iff (!rst_n)
  refusal_outlives_the_cause);

// D5. Eviction would convert a permanent refusal into a transient one.
p_se_eviction: assert property (@(posedge clk) disable iff (!rst_n)
  !eviction_permitted |-> eviction_would_fix_it);

// D6. Zero churn means no stale entries.
p_se_zero: assert property (@(posedge clk) disable iff (!rst_n)
  (churn_per_hour == 32'd0) |-> (stale_entries == 32'd0));

// D7. The alarm cannot fire above the ceiling.
p_ma_unreachable: assert property (@(posedge clk) disable iff (!rst_n)
  (occupancy_alarm_pct > effective_ceiling_pct) |-> !alarm_can_fire);

// D8. And at a real failure it is silent.
p_ma_silent: assert property (@(posedge clk) disable iff (!rst_n)
  (!alarm_can_fire && (refused_ppm_i > 32'd100_000)) |-> alarm_is_silent_at_failure);

Group E — storm control (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// E1. The admitted rate is the setting times the port rate.
p_sc_admitted: assert property (@(posedge clk) disable iff (!rst_n)
  (admitted_pps_k == (port_pps_k * 32'(setting_pct_x10)) / 32'd1000));

// E2. A setting below 100% makes the limiter active.
p_sc_active: assert property (@(posedge clk) disable iff (!rst_n)
  (setting_pct_x10 < 16'd1000) |-> limiter_active);

// E3. Applying it to unknown unicast discards legitimate traffic.
p_sc_legitimate: assert property (@(posedge clk) disable iff (!rst_n)
  (limiter_active && applies_to_unknown) |-> discards_legitimate_traffic);

// E4. It never treats the cause.
p_sc_not_cause: assert property (@(posedge clk) disable iff (!rst_n)
  (treats_the_cause == 1'b0));

// E5. Broadcast and unknown discards are counted apart.
p_sc_apart: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_flooded && (cause_i == 2'(FLOOD_BCAST)) && (tokens == 32'd0)
   && applies_to_broadcast)
    |=> (c_discarded_unknown == $past(c_discarded_unknown)));

// E6. An admitted frame consumes a token.
p_sc_token: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_flooded && (tokens != 32'd0)) |=> (c_admitted == $past(c_admitted) + 32'd1));

// E7. The demand is the admitted rate times the copies.
p_sc_demand: assert property (@(posedge clk) disable iff (!rst_n)
  (demand_pps_k == admitted_pps_k * 32'(copies(int'(members)))));

// E8. A per-port setting is not an aggregate bound.
p_sc_aggregate: assert property (@(posedge clk) disable iff (!rst_n)
  ((per_port_setting_pct_x10 * ports_i) > (aggregate_budget_pct * 16'd10))
    |-> v_aggregate_unbounded);

Group F — telemetry and conformance (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// F1. Flooded frames are a subset of frames.
p_tl_subset: assert property (@(posedge clk) disable iff (!rst_n)
  (c_flooded <= c_frames));

// F2. Causes partition the floods.
p_tl_partition: assert property (@(posedge clk) disable iff (!rst_n)
  (c_flooded == c_bcast_o + c_unknown_o + c_mcast_o));

// F3. A refusal is an event and a nonzero count is the signal.
p_tl_event: assert property (@(posedge clk) disable iff (!rst_n)
  (c_insert_refused != 48'd0) |-> refusals_are_occurring);

// F4. 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));

// F5. An alarm above the effective capacity is a violation.
p_cf_alarm: assert property (@(posedge clk) disable iff (!rst_n)
  (occupancy_alarm_pct > effective_capacity_pct) |-> v_alarm_unreachable);

// F6. Merged counters are a violation.
p_cf_merged: assert property (@(posedge clk) disable iff (!rst_n)
  counters_merged |-> v_counters_merged);

// F7. A no-flooding claim with floods counted is a violation.
p_cf_claim: assert property (@(posedge clk) disable iff (!rst_n)
  (claims_no_flooding && (c_flooded_i != 48'd0)) |-> v_claims_no_flooding);

// 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 states a table reaches without any alarm firing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_tc_load1:      cover property (@(posedge clk) load_x100_o == 32'd100);
c_tc_load4:      cover property (@(posedge clk) load_x100_o == 32'd400);
c_tc_load8:      cover property (@(posedge clk) load_x100_o == 32'd800);
c_tc_silent:     cover property (@(posedge clk) occupancy_reads_healthy);
c_fc_permanent:  cover property (@(posedge clk) reason_is_permanent);
c_fc_unknown:    cover property (@(posedge clk) cause == 2'(FLOOD_UNKNOWN));
c_rp_saturated:  cover property (@(posedge clk) exceeds_budget);
c_se_stale:      cover property (@(posedge clk) stale_entries >= 32'd800);
c_se_displaced:  cover property (@(posedge clk) live_refusals_caused != 32'd0);
c_ma_unreachable:cover property (@(posedge clk) !alarm_can_fire);
c_sc_unknown_lim:cover property (@(posedge clk) discards_legitimate_traffic);
c_sc_aggregate:  cover property (@(posedge clk) v_aggregate_unbounded);
c_tl_refusals:   cover property (@(posedge clk) refusals_are_occurring);
c_cf_alarm:      cover property (@(posedge clk) v_alarm_unreachable);

21. Verification Scenarios

Fifty-eight scenarios in six groups, plus one directed test random stimulus will not produce.

Group 1 — the replication (10).

#ScenarioExpect
164 members, 100 Gb/s, 84-octet slot148.81 Mpps; 63 copies; 9.375 Gpps
2the same against a 9.524 Gpps budget984 252 ppm — 98.4%; exceeds_budget
3128 members127 copies; 99.2% egress share
48 members7 copies; 87.5% egress share
51 member0 copies; no flood
6bigger_switch_helps at any setting0
71 518-octet frames instead of minimum8.13 Mpps; 0.512 Gpps; 5.4% of the budget
89 018-octet frames1.38 Mpps; 0.087 Gpps; 0.91%
9egress bandwidth at 64 members6 300 Gb/s of 6 400
10over-budget observedc_over_budget increments

Group 2 — the flood causes (9).

#ScenarioExpect
11a broadcast destinationFLOOD_BCAST; anybody_will_notice high
12a unicast missFLOOD_UNKNOWN; host_cost_is_zero high
13a unicast hitnot flooding; one copy
14a miss with insert_was_refusedreason_is_permanent
15a miss with never_seennot permanent — one frame
16a miss after entry_aged_outnot permanent
17a miss after topology_changednot permanent
18anybody_will_notice on an unknown unicastLOW — the finding
191 000 floods, 200 permanentc_unknown_permanent = 200

Group 3 — table capacity (10).

#ScenarioExpect
2032 768 offered, 32 768 setsload 1.00; refused 0.435%; occupancy 24.9%
2165 536 offeredload 2.00; 3.757%; 48.1%
2298 304 offeredload 3.00; 10.645%
23131 072 offeredload 4.00; 19.537%; occupancy 80.5%
24163 840 offeredload 5.00; 28.737%
25196 608 offeredload 6.00; 37.217%; occupancy 94.2%
26262 144 offeredload 8.00; 50.744%; occupancy 98.5%
27the load-4.00 caseoccupancy_reads_healthy HIGH
28mean copies at load 4.00, 64 members13.11
298-way, 16 384 sets, 131 072 offeredrefused 14.0%; effective 86.0%

Group 4 — stale entries (9).

#ScenarioExpect
30100 teardowns/hour, 300 s ageing8.3 stale entries
311 000/hour83.3
3210 000/hour833.3
33833 stale against 131 072 livekeys 131 905; load 4.025; refused 19.773%
34the difference474 additional refusals
35the ratio1.76 dead entries per additional refusal
36refusal_outlives_the_cause1, at every setting
37eviction_permitted loweviction_would_fix_it high
38zero churnno stale entries; no extra refusals

Group 5 — storm control (10).

#ScenarioExpect
39100% setting148.81 Mpps admitted; 98.4% of the budget
4010%14.88 Mpps; 9.8%
411%1.49 Mpps; 0.98%
420.1%0.15 Mpps; 0.10%
431% on 64 ports63% of the budget — the aggregate nobody bounds
44applied to broadcast onlydiscards_legitimate_traffic low
45applied to unknown unicastHIGH — user data discarded
46treats_the_cause at any setting0
47a broadcast discarded at the limitc_discarded_bcast moves; unknown counter still
48an unknown unicast discardedc_discarded_unknown moves

Group 6 — telemetry and conformance (10).

#ScenarioExpect
49142 refusals at load 1.00refusals_are_occurring high; no level alarm
50a 90% alarm on a four-way tablev_alarm_unreachable
51a 70% alarm on the same tableclean
52merged flood countersv_counters_merged
53storm control on unknown unicastv_limiter_on_unknown
5464 ports at 1% against a 10% aggregate budgetv_aggregate_unbounded
55refusals not reportedv_refusals_unreported
56a no-flooding claim with floods countedv_claims_no_flooding
57one ingress port dominating the floodsworst_ingress_port names it
58all six checks clearconformant high

22. Debugging a Flooding Switch

Six symptoms, and five of them present on ports that are not the cause.

SymptomFirst questionWhere to look
every port dropping, no port obviously at faultwhich ingress port is flooding?Section 15 — worst_ingress_port, not the drop counters
the table reads 80% and the network is slowwhat is c_insert_refused?Section 8 — the alarm's ceiling is below its threshold
flooding after a container fleet grewdid the key count grow past the table's load?Section 6 — 19.5% refused at a load of 4.00
flooding that storm control "fixed"is user traffic being discarded?Section 12 — a limiter on unknown unicast drops real data
one station unreachable, everything else finewas its insert refused?Section 4 — a refused address floods forever
broadcast complaints but no unknown-unicast complaintswould anybody notice the second?Section 1's callout — no host sees it

Row six is the structural point of the chapter. Unknown-unicast flooding produces no host-visible symptom at all, so the absence of complaints is not evidence of its absence — and the only witness is a counter inside the switch.


23. Misconceptions

Misconception 1 — "switches do not flood."

The wrong model: a switch learns where stations are and forwards to one port, so flooding belongs to hubs.

What it costs: a capacity plan with no term for replication. Section 2: 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 Chapter 12.4 §3's observation holds: buying a bigger switch does not help, because the amplification scales with the port count.

The corrected model: a switch floods whenever the table has no entry, and Chapter 12.1 §13's class 49 already established that asserting otherwise is asserting the absence of a behaviour the design was built to perform.

Misconception 2 — "unknown unicast is a corner case."

The wrong model: the table learns quickly, so misses are transient.

What it costs: a fifth of the network's destinations flooding permanently. Section 6: a 128k-entry four-way table offered 131 072 addresses stores 105 465 and refuses 25 607. Section 4: a refused insert is not retried, does not age, and does not heal.

The corrected model: there are four reasons an entry is absent and only one of them expires. A station that has not spoken, an aged-out entry and a topology flush all resolve; a refused insert is permanent.

Misconception 3 — "the table is fine, it is only 80% full."

The wrong model: occupancy below the alarm threshold means capacity to spare.

What it costs: Section 20's class 119. The effective capacity of a four-way table is 80.5%Chapter 12.5 §9 — so an alarm at 90% cannot fire at the load where refusals become serious. The metric's ceiling is beneath the threshold.

The corrected model: watch the event, not the level. c_insert_refused is nonzero at a load of 1.00 with the table 24.9% occupied, and an event count needs no threshold at all.

Misconception 4 — "storm control fixed the flooding."

The wrong model: a rate limiter on flooded traffic solves the problem.

What it costs: discarded user data and an unchanged table. Section 12: a limiter applied to unknown unicast drops frames whose destination exists and whose address the switch failed to learn — and Section 13's treats_the_cause is a hard-wired zero. Section 13 also derives that 64 ports at a 1% setting together demand 63% of the budget, so a per-port bound is not an aggregate one.

The corrected model: storm control on broadcast is correct and on unknown unicast is a way of hiding a table problem, and the two are the same replication with different classifications.

Misconception 5 — "a few stale entries cost a few entries of memory."

The wrong model: 833 stale entries in a 131 072-entry table is 0.6% and negligible.

What it costs: 474 additional permanent refusals. Section 10: the scarce resource is not entries, it is sets with roomChapter 12.5 §7 — and a stale entry that fills the last way of a set is the difference between accepting the next address and refusing it.

The corrected model: the stale entry ages out in 300 seconds and the refusal it caused may never be retried, because Chapter 12.2's silent station only transmits when addressed, and it is addressed by a flood.

Misconception 6 — "nobody is complaining, so nothing is flooding."

The wrong model: a flood would be noticed.

What it costs: the entire failure, indefinitely. Chapter 12.4 §6: an unknown unicast is discarded by an exact 48-bit compare in every receiving card's hardware, at zero cost to every host. Sixty-three copies land and sixty-two evaporate before anything wakes.

The corrected model: a broadcast storm produces complaints within minutes and an unknown-unicast flood produces none, everthe switch does all the suffering — so the absence of complaints is evidence about the hosts and not about the fabric.


24. Interview Questions

Six, with what a strong answer contains.

1. Do switches eliminate broadcasts?

No, and they add a second kind of flood that looks identical inside the switch. A strong answer separates them: a broadcast is replicated by specification and an unknown unicast because the table has no entry, and both go to every port in the VLAN. At 64 ports both are 63 copies and 98.4% of the switch's frame budget at line rate. The best answers give the receiver-side difference: a broadcast reaches every host's CPU and an unknown unicast is discarded by a 48-bit compare, so only one of them produces complaints.

2. When does a switch flood unknown unicast?

Whenever the table has no entry, and there are four reasons. A station that has not transmitted yet; an entry that aged out; a topology change that flushed the table; and an insert the table refused. A strong answer notes that only the last is permanent — it is not retried, does not age and does not heal — and that Chapter 12.5 §9's arithmetic says how often it happens: a 128k four-way table offered its nominal capacity refuses 25 607 addresses, 19.5%.

3. Why does the table refuse inserts with memory free?

Because a set-associative table's capacity is a property of the hash, not of the memory. Addresses land in sets by a hash the operator does not choose; sets fill unevenly; a set at its way limit refuses every further insert into it while other sets have room. A strong answer gives the effective capacities: 63.2% direct-mapped, 72.9% two-way, 80.5% four-way, 86.0% eight-way, 90.1% sixteen-way — and notes that Chapter 12.5 §5 established a better hash cannot fix it, because the unevenness is in the arrival process rather than the function.

4. Your switch reports 80% table occupancy and the network is slow. What do you check?

The refusal count, not the occupancy. A strong answer names the structural point: the effective capacity of a four-way table is 80.5%, so an occupancy alarm at 90% cannot fire at the load where refusals matter. That is Section 20's class 119 — a health property whose metric cannot reach its own threshold — and the repair is an event count and a threshold derived from the structure: near 70% at four ways, not 90.

5. Does storm control fix it?

It caps the damage and discards real data to do it. A strong answer distinguishes the two classes: on broadcast it is correct, because the traffic is control-plane and the protocols retry; on unknown unicast it discards application data to a station that exists. And it names the arithmetic gap: a per-port setting of 1% on 64 ports permits 63% of the budget, because the amplification applies to each port independently and no standard configuration offers an aggregate bound.

6. What is the cheapest thing that would have caught this?

Four counters costing 0.024% of the switch. c_insert_refused — an event, needing no threshold. The flood share split by cause, which selects the fix. Flooded frames per ingress port, which names the source where the drop counters rank the victims 63 to 1. And a per-set occupancy census folded into Chapter 12.5 §13's ageing sweep, which is the only level metric that can see the failure. The best answers add the fifth: a read-only register reporting the table's effective capacity, so a monitoring system can set a reachable threshold.


25. Questions and Answers


26. What's Next

One myth left, and it is the oldest one in the track.

Chapter 25.6 takes "full-duplex Ethernet uses CSMA/CD". Chapter 1.5 §2 already listed what full duplex removed from the MAC — the slot-time counter, the collision classifier, the jam generator and the backoff engine all become unreachable — so the correction itself is settled. What that chapter did not do is derive what the slot time was FOR, and that is where the number is.

512 bit times is a round-trip budget, and a round-trip budget is a distance. At 10 Mb/s it is 51.2 µs, which at Chapter 8.2 §3's 5.05 ns per metre of copper is 5 069 metres of one-way cable before repeaters and detection latency are charged against it. At 100 Mb/s the same 512 bit times is 507 metres and at 1 Gb/s it is 51 — which is why gigabit half duplex needed carrier extension to 4 096 bit times, and why the industry stopped extending shared segments.

On a point-to-point link the bound is not merely satisfied; it is satisfied by four orders of magnitude — a 3-metre link's round trip is 30.3 ns against a 51 200 ns budget — and there is no second transmitter for it to bound.

And then the chapter finds where the vestige still costs something, which is the minimum frame size. 64 octets exists because 512 bit times had to be a frame, and it survives the timing argument that created it — at 45.24% of the wire per Chapter 24.3 §1, and, more expensively, as the packet rate that sets Chapter 23.3 §2's eight-wide pipeline.

Continue learning

Standards & specifications

Governing standard
IEEE Std 802.3 (Ethernet)(opens IEEE in a new tab)

Defines the Ethernet MAC, the media-independent interfaces and the physical-layer sublayers, including framing, access control, auto-negotiation and per-rate PHY specifications. VLAN tagging, priority and time-sensitive shaping are defined by IEEE 802.1, not by 802.3.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the Ethernet curriculum.