Skip to content
VLSI Mentor

Ethernet · Module 12

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.

Chapter 12.3 deferred this twice. A miss floods, an unresolved lookup floods, and both were called the safe answer without examining the bill.

Here is the bill. One gigabit port flooding at line rate on a 24-port switch generates 23 × 1.4881 M = 34.23 million frame-emissions per second and demands 23 Gb/s of egress bandwidth — 47.9% of the switch's entire 48 Gb/s aggregate, from one port.

And that is only the switch's half of the cost. Every copy lands on a station, and what the station does with it depends entirely on which of the three causes produced the flood — a distinction invisible in the switch, identical on the wire, and worth several orders of magnitude in host CPU.

1. Scope — What This Chapter Owns

This chapter owns the cost of not knowing: what a flood does to every port in the domain, where each of the three flood types is actually discarded, why broadcast is different in kind rather than in degree, the domain-size limit that follows, and the one mitigation available at this layer.

It does not own the decision to floodChapter 12.3 §2 and §3 established when a flood is the answer and why a miss is not an absence. This chapter takes the decision as given and prices it.

It does not own the tableChapter 12.5 owns the structure whose capacity determines how often a miss happens at all.

It does not own segmentation. The mitigation that actually works is to make the domain smaller, and Chapter 13.1 owns why that is the answer and what it costs. This chapter establishes the problem that VLANs exist to solve, and stops at the boundary.

And it does not own group membership. A mechanism that lets a switch learn which ports want a multicast group exists, and it lives above the layer this track covers. Section 14 explains precisely why no mechanism at this layer can do better, which is a different claim from nothing can.

2. Three Causes, One Behaviour

A flood is N−1 copies of a frame, one to each forwarding port except the ingress. Chapter 12.3 §9 built the mask. Three unrelated conditions produce it.

Unknown unicast — the switch does not know. The destination is an individual address with no table entry. Chapter 12.3 §2 gave five reasons a present station produces a miss, and flooding is the only response correct under all five.

Multicast — the switch cannot know. A group address names a set of stations, and nothing in a basic switch tells it which ports lead to members. Chapter 12.2 §4's first eligibility rule forbids learning a group address, because nothing ever transmits from one — so the table can never contain a group destination, and every multicast misses forever by construction.

Broadcast — the address means everyone. FF:FF:FF:FF:FF:FF is not a lookup failure. It is a successful classification with a known answer, and Chapter 12.3 §10's composer skips the lookup entirely for it.

Three causes. Identical wire behaviour. And the difference between them is everything.

will stoptable's rolefixable by a bigger tablefixable by segmentation
unknown unicaston the station's next framea missyesyes
multicastnevercannot be learnednoyes
broadcastnevernot consultednoyes — the only fix

The right-hand columns are the operational content of this chapter. An engineer who sees an elevated flood rate and buys a switch with a larger table has fixed exactly one of the three, and the one they fixed is the one that was self-limiting anyway.

What each cause would need in order to stop

Asking what information would end each flood makes the three causes' differences structural rather than descriptive.

CauseWhat would end itDoes that information exist at this layer
unknown unicastthe station transmitting onceyes — and it happens by itself
multicastwhich ports contain group membersno — nothing transmits from a group address
broadcastnothingthe address means every port; there is nothing to know

The right-hand column is the whole taxonomy. One cause is cured by information the network generates on its own. One is cured by information that does not exist at this layer and must come from a protocol above it. And one cannot be cured at all, because the flood is not a consequence of ignorance — it is the address's definition.

Which is why the only remedy that touches all three is to make the domain smaller. Segmentation does not require any information the switch lacks; it changes how many ports are in scope, and it therefore works identically on a cause the switch understands, a cause it cannot know, and a cause that is not a question at all.

3. The Amplification Arithmetic

A flood is the only operation in a switch where one frame in produces many frames out, and the multiplier is the port count.

SwitchCopies per flooded frame1 Gb/s of flood in demandsas a share of aggregate
8 ports77 Gb/s7 ÷ 16 = 43.8%
24 ports2323 Gb/s23 ÷ 48 = 47.9%
48 ports4747 Gb/s47 ÷ 96 = 49.0%

The share converges on 50% and never exceeds it, which is a fact worth holding: (N−1) ÷ 2N → ½. One port, flooding at line rate, can demand very close to half of any switch's total egress capacity regardless of how many ports it has.

In frames rather than bits, on the 24-port case at minimum length:

1.4881 M frames/s × 23 copies = 34.23 M frame-emissions per second

And Chapter 12.1 §12 established that a 24-port gigabit switch's total frame-handling requirement is 35.71 Mpps. So one port flooding minimum-length frames at line rate consumes 95.9% of the switch's entire frame-emission budget — leaving 4.1% for the other 23 ports combined.

Buying a bigger switch does not help. The amplification scales with the port count, so a 48-port switch faced with the same single flooding port suffers the same proportional damage and inflicts it on twice as many stations.

A single frame arrives at one ingress port of a 24-port switch and, because it must be flooded, is replicated to all twenty-three other forwarding ports. One gigabit per second of arriving flood therefore demands twenty-three gigabits per second of egress bandwidth, which is 47.9 percent of the switch's 48 gigabit aggregate capacity. Measured in frames rather than bits, 1.4881 million minimum-length frames per second becomes 34.23 million frame emissions per second, which is 95.9 percent of the switch's total frame handling budget of 35.71 million per second. The amplification factor is the port count minus one, so a larger switch suffers the same proportional damage and inflicts it on more stations.One frame in1 Gb/s on one portReplicateN − 1 copies23 Gb/s demandedon 23 egress ports47.9% of aggregateconverges on one half34.23 M emissions/sminimum-length frames95.9% of framebudget35.71 Mpps totalA bigger switchsame share, more victims12
Figure 1 — a flood is the only one-to-many operation in a switch; the multiplier is the port count, and the share of aggregate capacity converges on one half.

4. RTL 1 — Classifying Why This Frame Is Flooding

One behaviour, three causes, three counters. The classification costs two comparators and it is the difference between a diagnosable switch and an undiagnosable one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_pkg -- shared types for flood handling.
//
// The cause encoding exists because Chapter 12.3 Section 3's lesson
// applies again here: three conditions produce identical wire behaviour,
// and folding them into one counter destroys the only information that
// tells an operator which of three unrelated remedies to reach for.
// -----------------------------------------------------------------------
package flood_pkg;

  typedef enum logic [2:0] {
    FC_NONE            = 3'd0,
    FC_UNKNOWN_UNICAST = 3'd1,  // a miss -- self-limiting, table-shaped fix
    FC_MULTICAST       = 3'd2,  // cannot be learned -- permanent
    FC_BROADCAST       = 3'd3,  // means everyone -- permanent, CPU cost
    FC_UNRESOLVED      = 3'd4   // Chapter 12.3's lookup deadline
  } flood_cause_e;

  // What a RECEIVING station does with a frame. Section 7 models this,
  // and it is where the three causes stop being equivalent.
  typedef enum logic [1:0] {
    RX_DROP_EXACT = 2'd0,  // 48-bit address filter, hardware, free
    RX_DROP_HASH  = 2'd1,  // multicast hash filter, hardware, imperfect
    RX_TO_CPU     = 2'd2,  // reached the host -- costs real time
    RX_WANTED     = 2'd3   // addressed to this station, legitimately
  } rx_fate_e;

  localparam int ADDR_W = 48;

endpackage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_classifier -- says WHY this frame is being flooded.
//
// The decision to flood was made by Chapter 12.3's composer. This module
// adds nothing to that decision and changes no behaviour. It exists
// entirely so that the three causes can be counted apart, because their
// remedies are unrelated and their costs differ by orders of magnitude.
// -----------------------------------------------------------------------
module flood_classifier
  import flood_pkg::*;
#(
  parameter int CNT_W = 32
)(
  input  logic              clk,
  input  logic              rst_n,

  input  logic              flood_valid,
  input  logic [ADDR_W-1:0] dst,
  input  logic              lookup_was_performed,
  input  logic              lookup_timed_out,

  output flood_cause_e      cause,
  output logic              cause_valid,

  output logic [CNT_W-1:0]  c_unknown_unicast,
  output logic [CNT_W-1:0]  c_multicast,
  output logic [CNT_W-1:0]  c_broadcast,
  output logic [CNT_W-1:0]  c_unresolved,
  output logic              permanent_flood     // multicast or broadcast
);

  logic is_broadcast, is_group;

  assign is_broadcast = (dst == {ADDR_W{1'b1}});
  // Chapter 5.3's I/G bit. Broadcast is tested FIRST because it is also a
  // group address, and the two need different counters.
  assign is_group     = dst[40];

  always_comb begin
    cause = FC_NONE;
    if (flood_valid) begin
      if (is_broadcast)                cause = FC_BROADCAST;
      else if (is_group)               cause = FC_MULTICAST;
      else if (lookup_timed_out)       cause = FC_UNRESOLVED;
      else if (lookup_was_performed)   cause = FC_UNKNOWN_UNICAST;
      else                             cause = FC_UNKNOWN_UNICAST;
    end
  end

  // A flood that will never stop on its own. The distinction matters
  // operationally: an unknown-unicast flood ends when the station next
  // transmits, and these do not end at all.
  assign permanent_flood = flood_valid &&
                           ((cause == FC_MULTICAST) || (cause == FC_BROADCAST));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      cause_valid       <= 1'b0;
      c_unknown_unicast <= '0;
      c_multicast       <= '0;
      c_broadcast       <= '0;
      c_unresolved      <= '0;
    end else begin
      cause_valid <= flood_valid;
      if (flood_valid) begin
        unique case (cause)
          FC_UNKNOWN_UNICAST:
            if (!(&c_unknown_unicast)) c_unknown_unicast <= c_unknown_unicast + 1'b1;
          FC_MULTICAST:
            if (!(&c_multicast))       c_multicast       <= c_multicast + 1'b1;
          FC_BROADCAST:
            if (!(&c_broadcast))       c_broadcast       <= c_broadcast + 1'b1;
          FC_UNRESOLVED:
            if (!(&c_unresolved))      c_unresolved      <= c_unresolved + 1'b1;
          default: ;
        endcase
      end
    end
  end

endmodule

Classification: synthesizable.

What it teaches: that broadcast must be tested before the group bit, because FF:FF:FF:FF:FF:FF has the I/G bit set and would otherwise be counted as multicast. Chapter 12.1 §4's classifier made the same ordering choice for the same reason; here the consequence is a counter that silently misattributes the single most expensive flood type to the second most expensive one.

And it teaches that permanent_flood is the operationally useful bit. An unknown-unicast flood is a transient the network will resolve without help. Multicast and broadcast floods are steady-state, and an operator who does not know which kind they are looking at cannot tell a problem that will clear itself from one that will not.

Deliberately simplified: the cause is derived combinationally from the destination address and two status bits. A production design carries the cause forward as a field of the frame descriptor Chapter 12.1 §4 introduced, so that the egress side can apply per-cause policy — for example, dropping unknown-unicast floods on a port configured to hold only known stations while still passing broadcast.

Production implication: the four counters answer a question that no port statistic can: is this switch flooding because it does not know, or because it has been asked to? c_unknown_unicast high with c_broadcast low is a learning-side problem — Chapter 12.2 §18's table applies. c_broadcast high is not a switch problem at all; it is a statement about what the attached stations are saying, and the only remedies are above this layer or in Section 12's rate limiter.

5. RTL 2 — Replicating a Frame Without Duplicating It

A flood emits N−1 copies and Chapter 12.1's P10 forbids two copies on one port. Those two sentences are in tension only if the replication is done carelessly, and the careless version is the natural one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_replicator -- turns one frame and a mask into exactly one emission
// per set bit, in a bounded number of cycles, with no port served twice
// and no port skipped.
//
// The frame is stored ONCE and referenced N-1 times. Copying the payload
// per port would multiply the buffer bandwidth by the port count, which
// is the resource Section 3 already showed is 95.9% consumed.
// -----------------------------------------------------------------------
module flood_replicator
  import flood_pkg::*;
#(
  parameter int N_PORTS   = 24,
  parameter int PORT_BITS = 5,
  parameter int BUF_BITS  = 12,
  parameter int CNT_W     = 32
)(
  input  logic                 clk,
  input  logic                 rst_n,

  input  logic                 flood_start,
  input  logic [N_PORTS-1:0]   flood_mask,     // Chapter 12.3's built mask
  input  logic [BUF_BITS-1:0]  buf_handle,     // the ONE stored copy
  input  flood_cause_e         cause,

  output logic                 emit_valid,
  output logic [PORT_BITS-1:0] emit_port,
  output logic [BUF_BITS-1:0]  emit_handle,
  input  logic                 emit_ready,

  output logic                 busy,
  output logic                 release_buffer, // all copies emitted
  output logic [CNT_W-1:0]     c_floods,
  output logic [CNT_W-1:0]     c_copies,
  output logic [7:0]           last_fanout
);

  logic [N_PORTS-1:0]  pending_q;
  logic [BUF_BITS-1:0] handle_q;
  logic [7:0]          fanout_q;

  // Lowest set bit of the pending mask. A fixed priority is acceptable
  // here BECAUSE every set bit is served before the operation completes --
  // there is no starvation in a drain, only an order.
  logic [PORT_BITS-1:0] next_port;
  logic                 has_pending;

  always_comb begin
    next_port   = '0;
    has_pending = |pending_q;
    for (int p = N_PORTS-1; p >= 0; p--)
      if (pending_q[p]) next_port = PORT_BITS'(p);
  end

  assign busy        = |pending_q;
  assign emit_valid  = has_pending;
  assign emit_port   = next_port;
  assign emit_handle = handle_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      pending_q      <= '0;
      handle_q       <= '0;
      fanout_q       <= '0;
      release_buffer <= 1'b0;
      c_floods       <= '0;
      c_copies       <= '0;
      last_fanout    <= '0;
    end else begin
      release_buffer <= 1'b0;

      // A new flood is accepted only when the previous one has fully
      // drained. Accepting a second flood while the first is incomplete
      // would drop copies of the first -- silently, since nothing counts
      // an unsent copy.
      if (flood_start && !busy) begin
        pending_q <= flood_mask;
        handle_q  <= buf_handle;
        fanout_q  <= 8'($countones(flood_mask));
        if (!(&c_floods)) c_floods <= c_floods + 1'b1;
        last_fanout <= 8'($countones(flood_mask));
      end

      // CLEAR THE BIT ON ACCEPTANCE. Clearing on issue instead would lose
      // a copy whenever the egress applied backpressure -- the frame
      // would be counted as emitted and never appear on the wire.
      if (emit_valid && emit_ready) begin
        pending_q[next_port] <= 1'b0;
        if (!(&c_copies)) c_copies <= c_copies + 1'b1;
        // The last copy releases the single stored frame.
        if ($countones(pending_q) == 1) release_buffer <= 1'b1;
      end
    end
  end

  wire _unused_cause = |cause;

endmodule

Classification: synthesizable.

What it teaches: that a flood stores the frame once and emits a handle N−1 times. Copying the payload per port would multiply the buffer's read bandwidth by 23 on a 24-port switch — and Section 3 showed the frame-emission budget is already 95.9% consumed by a single flooding port. The buffer is the resource that cannot afford the copies; the descriptor is what gets replicated.

And it teaches why the pending bit clears on emit_ready rather than on emit_valid. An egress port can apply backpressure. Clearing on issue counts the copy as emitted while the port never took it — and nothing downstream would ever notice, because a flood has no completion signal and no receiver reports back. The lost copy is one station that silently did not get the frame.

Deliberately simplified: one flood in flight, drained at one copy per cycle, with a fixed-priority scan. A production replicator keeps several floods in flight against a shared buffer with reference counts, because a 23-cycle drain at 500 MHz is 46 ns and Chapter 12.1 §12's per-frame budget is 28 ns — the drain alone exceeds the budget, which is why replication in real switches happens in the egress scheduler rather than as a serial loop.

Production implication: last_fanout is worth exposing because it is the only place the flood's actual reach is visible. Chapter 12.3 §9's flood_reduced said the mask was smaller than the full set; last_fanout says by how much. A flood with a fanout of 4 on a 24-port switch reached four stations, and if the destination was behind one of the other nineteen, the frame is legitimately lost with no counter calling it a loss — the switch flooded correctly, and the flood did not reach.

6. Where Each Flood Type Is Actually Discarded

This is the section the chapter exists for. The three causes are identical inside the switch and diverge completely at the receiver.

Every flooded copy arrives at a station's network card, and the card asks one question: is this frame for me? The answer is computed in hardware, before the host CPU is involved, and the mechanism is different for each of the three address types.

how the card decidescost to the hostcan it be wrong
unknown unicastexact 48-bit compare against the card's own addresszero — discarded in hardwareno
multicasthash of the address into a small bucket tablenear zero, degradingyes — false accepts
broadcastno decision — always acceptedfullnot applicable

Unknown-unicast flooding costs the receiving stations nothing at all. The card compares 48 bits, finds no match, and discards the frame without waking anything. Twenty-three copies of a frame land on twenty-three cards and twenty-two of them evaporate in hardware. The cost is entirely bandwidth, and it is entirely inside the network.

Broadcast is the opposite, and it is not a matter of degree. A card cannot discard a broadcast — the address means everyone, and discarding it would break ARP, DHCP, and every discovery protocol ever written. Every broadcast frame reaches the host's CPU, on every station, always.

Multicast sits between them, imperfectly. The card keeps a small hash table — 64 buckets is a common size — with a bit set for each bucket that any joined group maps into. A frame whose group hashes to an enabled bucket is accepted and passed to the CPU, which then checks whether it is really a member.

A flooded frame arrives at a station's network interface card. If the destination is an individual unicast address, the card performs an exact forty-eight bit comparison against its own address, and a frame not addressed to it is discarded in hardware at zero cost to the host. If the destination is a multicast group address, the card hashes it into a small bucket table, typically sixty-four buckets, and accepts the frame if the bucket is enabled by any joined group, which means unwanted groups whose hash collides are passed to the CPU as false accepts. If the destination is the broadcast address, the card cannot discard it at all, because broadcast means everyone, so every broadcast frame reaches the host CPU on every station without exception. The three costs differ by orders of magnitude and the switch can observe none of them.Flooded copyarrivesat every station's cardUnicast — exactmatch48-bit compareDiscarded inhardwarezero host costMulticast — hashfilter64 buckets, imperfectFalse acceptscollided groups reach CPUBroadcast — nofilteralways acceptedHost CPUthe cost that scales12
Figure 2 — the same flooded frame, three fates: exact-match hardware discard, imperfect hash filter, or unconditional delivery to the CPU.

7. RTL 3 — Modelling the Receiver, Because the Switch Cannot See It

This module is not part of a switch. It is a model of what happens to every copy the switch emits, and it exists because the switch has no way to obtain any of it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// receiver_filter_model -- what a station's network card does with an
// arriving frame.
//
// NOT SYNTHESIZABLE INTO A SWITCH. This is the far end of every flooded
// copy, and Ethernet provides no path by which the switch could learn any
// of it. It is here because the cost of flooding is paid at this module
// and measured nowhere.
// -----------------------------------------------------------------------
module receiver_filter_model
  import flood_pkg::*;
#(
  parameter int HASH_BUCKETS = 64,
  parameter int CNT_W        = 32
)(
  input  logic              clk,
  input  logic              rst_n,

  input  logic              rx_valid,
  input  logic [ADDR_W-1:0] rx_dst,
  input  logic [ADDR_W-1:0] my_address,
  input  logic [HASH_BUCKETS-1:0] mcast_buckets,   // set by joined groups
  input  int                joined_group_count,

  output rx_fate_e          fate,
  output logic              to_cpu,

  output logic [CNT_W-1:0]  c_wanted,
  output logic [CNT_W-1:0]  c_dropped_exact,
  output logic [CNT_W-1:0]  c_dropped_hash,
  output logic [CNT_W-1:0]  c_to_cpu_broadcast,
  output logic [CNT_W-1:0]  c_to_cpu_false_accept
);

  logic is_broadcast, is_group, is_mine;

  assign is_broadcast = (rx_dst == {ADDR_W{1'b1}});
  assign is_group     = rx_dst[40];
  assign is_mine      = (rx_dst == my_address);

  // A typical card hashes the address -- often the top bits of a CRC --
  // into a small bucket table. The table cannot express "this exact
  // group"; only "some joined group lands in this bucket".
  logic [5:0] bucket;
  assign bucket = rx_dst[47:42] ^ rx_dst[41:36] ^ rx_dst[23:18] ^ rx_dst[5:0];

  always_comb begin
    fate = RX_DROP_EXACT;
    if (rx_valid) begin
      if (is_broadcast) begin
        // NO FILTER EXISTS. Broadcast means everyone, and a card that
        // discarded it would break ARP, DHCP and every discovery
        // protocol. This branch has no alternative.
        fate = RX_TO_CPU;
      end else if (is_group) begin
        // The hash filter. Enabled bucket -> accept and let software
        // decide. A collision means a group this station never joined
        // costs it a CPU interrupt and a stack traversal.
        fate = mcast_buckets[bucket] ? RX_TO_CPU : RX_DROP_HASH;
      end else if (is_mine) begin
        fate = RX_WANTED;
      end else begin
        // EXACT 48-BIT COMPARE. Every unknown-unicast flood copy dies
        // here, in hardware, for free. This is why unknown-unicast
        // flooding costs bandwidth and nothing else.
        fate = RX_DROP_EXACT;
      end
    end
  end

  assign to_cpu = rx_valid && ((fate == RX_TO_CPU) || (fate == RX_WANTED));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_wanted              <= '0;
      c_dropped_exact       <= '0;
      c_dropped_hash        <= '0;
      c_to_cpu_broadcast    <= '0;
      c_to_cpu_false_accept <= '0;
    end else if (rx_valid) begin
      unique case (fate)
        RX_WANTED:      if (!(&c_wanted))        c_wanted        <= c_wanted + 1'b1;
        RX_DROP_EXACT:  if (!(&c_dropped_exact)) c_dropped_exact <= c_dropped_exact + 1'b1;
        RX_DROP_HASH:   if (!(&c_dropped_hash))  c_dropped_hash  <= c_dropped_hash + 1'b1;
        RX_TO_CPU: begin
          if (is_broadcast) begin
            if (!(&c_to_cpu_broadcast))
              c_to_cpu_broadcast <= c_to_cpu_broadcast + 1'b1;
          end else begin
            // Accepted by the hash, and software will have to decide
            // whether this station actually joined the group.
            if (!(&c_to_cpu_false_accept))
              c_to_cpu_false_accept <= c_to_cpu_false_accept + 1'b1;
          end
        end
        default: ;
      endcase
    end
  end

  wire _unused_groups = (joined_group_count != 0);

endmodule

Classification: model only — this is the station, not the switch, and it is deliberately outside the device under test.

What it teaches: that the broadcast branch has no alternative implementation. Every other address type reaches a decision that can discard; broadcast reaches RX_TO_CPU unconditionally, and there is no card, no driver and no configuration that changes it without breaking the protocols broadcast exists for. That single unconditional branch is why Section 8 argues broadcast differs in kind rather than degree.

And it teaches why the multicast filter degrades. The bucket table expresses "a joined group lands here", never "this exact group". With 64 buckets and g joined groups, the probability that an arbitrary unwanted group collides into an enabled bucket is 1 − (1 − 1/64)^g:

Groups joinedUnwanted multicast reaching the CPU
11.6%
46.1%
1622.3%
6463.5%
25698.2%

A station that has joined 256 groups has a multicast filter that passes 98.2% of everything — it has effectively no filter at all, and every multicast frame in the domain costs it an interrupt.

Deliberately simplified: a fixed 64-bucket table and an XOR-folded hash. Real cards use a CRC-derived index and offer a small number of exact-match slots alongside the hash, so the first handful of groups are filtered precisely and the rest fall back to the hash. The degradation curve is the same shape, displaced by the number of exact slots.

Production implication: none of these counters exists in the switch, and that is the point of including the module. An engineer sizing a broadcast domain is making a decision whose cost lands entirely in this module — on hardware they do not own, measured by counters they cannot read, on stations that will never report back. Section 17's rejected property is exactly the attempt to assert something about this module from inside the switch.

8. Why Broadcast Is Different in Kind

Put the receiver model together with the amplification arithmetic and the difference stops being quantitative.

A flooded unknown unicast costs N−1 copies of bandwidth and zero host CPU. The frame is discarded by an exact comparator in every card it does not belong to. The waste is confined to the network, and the network is the party that can measure it.

A broadcast costs N−1 copies of bandwidth and a full protocol-stack traversal on every station in the domain. Take a plausible cost of 5 µs per frame for an interrupt, a driver hand-off and a stack traversal to the point where the frame is recognised as uninteresting:

Broadcast rate reaching a stationHost CPU consumed
1 000 /s1000 × 5 µs = 0.5% of one core
10 000 /s5% of one core
100 000 /s50% of one core
1.4881 M /s — one port at line rate1.4881 M × 5 µs = 744%, or 7.4 cores

The bottom row is the one that reframes a broadcast storm. It is not a network that has become slow. It is every attached machine simultaneously spending several cores' worth of time discarding frames — which is why a broadcast storm takes down hosts, management interfaces and monitoring systems at the same moment the network degrades, and why the tools an operator would use to diagnose it are among the casualties.

The same storm, measured two ways

A broadcast storm is small in bits and enormous in frames, and every standard network instrument measures bits.

Broadcast rateAs Mb/s at 64 octets% of a gigabit linkHost CPU at 5 µs
1 000 /s0.7 Mb/s0.1%0.01 cores
10 000 /s6.7 Mb/s0.7%0.05 cores
50 000 /s33.6 Mb/s3.4%0.25 cores
100 000 /s67.2 Mb/s6.7%0.50 cores
500 000 /s336 Mb/s33.6%2.50 cores
1 488 095 /s1000 Mb/s100%7.44 cores

Read across the 100 000 row. A utilisation graph shows 6.7% — a link that looks almost idle. Every station in the domain is spending half a core discarding frames, and every monitoring system in the environment is reporting green.

The divergence is because broadcast's cost is per event and a link's utilisation is per bit. Section 8's 5 µs is an interrupt, a driver hand-off and a stack traversal, and it is the same 5 µs whether the frame is 64 octets or 1518. A storm made of minimum-length frames is the cheapest possible use of the link and the most expensive possible use of the hosts.

Which is why c_broadcast per port per second — a counter incremented on one address comparison — is the instrument, and utilisation is not.

9. RTL 4 — Measuring the Amplification the Switch Is Producing

The switch cannot see what flooding costs the stations. It can see exactly what flooding costs itself, and that number is not on any port statistic.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_cost_accountant -- ingress bits against egress bits, and the
// amplification factor between them.
//
// A port statistic reports bytes in and bytes out PER PORT. Neither
// reveals that one arriving frame became twenty-three departing ones. The
// amplification factor is a switch-level quantity and has to be computed
// deliberately.
// -----------------------------------------------------------------------
module flood_cost_accountant
  import flood_pkg::*;
#(
  parameter int N_PORTS = 24,
  parameter int CNT_W   = 40,
  parameter int WINDOW  = 1_000_000     // frames per evaluation window
)(
  input  logic             clk,
  input  logic             rst_n,

  input  logic             frame_in,
  input  logic [13:0]      frame_bits,        // on-wire bits, incl overhead
  input  flood_cause_e     cause,
  input  logic             is_flood,
  input  logic [7:0]       fanout,            // copies this flood produced

  input  logic             emit_copy,

  output logic [CNT_W-1:0] bits_in,
  output logic [CNT_W-1:0] bits_out,
  output logic [CNT_W-1:0] flood_bits_out,    // egress bits due to flooding
  output logic [CNT_W-1:0] c_frames_in,
  output logic [CNT_W-1:0] c_copies_out,

  output logic             window_valid,
  output logic [15:0]      amplification_x100, // egress/ingress, x100
  output logic [15:0]      flood_share_pct,    // flood bits / all egress bits
  output logic             amplification_high
);

  logic [CNT_W-1:0] win_frames;
  logic [13:0]      last_bits_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      bits_in            <= '0;
      bits_out           <= '0;
      flood_bits_out     <= '0;
      c_frames_in        <= '0;
      c_copies_out       <= '0;
      win_frames         <= '0;
      last_bits_q        <= '0;
      window_valid       <= 1'b0;
      amplification_x100 <= 16'd100;
      flood_share_pct    <= '0;
      amplification_high <= 1'b0;
    end else begin
      window_valid <= 1'b0;

      if (frame_in) begin
        bits_in     <= bits_in + CNT_W'(frame_bits);
        c_frames_in <= c_frames_in + 1'b1;
        win_frames  <= win_frames + 1'b1;
        last_bits_q <= frame_bits;
        // The flood's egress cost is charged at the moment the flood is
        // launched, using the fanout, so that the number is available
        // even if the drain is still in progress.
        if (is_flood)
          flood_bits_out <= flood_bits_out +
                            (CNT_W'(frame_bits) * CNT_W'(fanout));
      end

      if (emit_copy) begin
        bits_out     <= bits_out + CNT_W'(last_bits_q);
        c_copies_out <= c_copies_out + 1'b1;
      end

      if (win_frames >= CNT_W'(WINDOW)) begin
        // Egress bits per ingress bit, scaled by 100. A switch doing pure
        // unicast forwarding sits at 100. Every point above that is
        // replication.
        amplification_x100 <= (bits_in == '0) ? 16'd100
                            : 16'((bits_out * CNT_W'(100)) / bits_in);
        flood_share_pct    <= (bits_out == '0) ? 16'd0
                            : 16'((flood_bits_out * CNT_W'(100)) / bits_out);
        // Above 2x means more than half of all egress bits are replicas.
        amplification_high <= (bits_out > (bits_in << 1));
        win_frames         <= '0;
        window_valid       <= 1'b1;
      end
    end
  end

  wire _unused_ports = (N_PORTS != 0);
  wire _unused_cause = |cause;

endmodule

Classification: synthesizable, with the divides implemented as a multi-cycle sequence in a real design — they occur once per million frames and need no throughput at all.

What it teaches: that amplification is a switch-level quantity that no per-port statistic can express. Port 3 reports bytes in; ports 1, 2 and 4 through 24 each report bytes out. Nothing in that set says that port 3's arriving frame became twenty-three departing ones, and an operator summing the columns sees a switch emitting far more than it received without any indication of why.

And it teaches the calibration. A switch doing pure unicast forwarding sits at exactly 100 — one egress bit per ingress bit. Every point above 100 is replication, and on a healthy network the excess is small: a few percent of broadcast plus the occasional unknown-unicast flood before a station is learned.

Deliberately simplified: the flood's egress cost is charged at launch using the fanout rather than accumulated per copy, and last_bits_q approximates the emitted frame's size. A production accountant carries the size with the buffer handle, because floods and unicast forwards interleave and the last arriving frame is not the one being emitted.

Production implication: amplification_x100 is the single number that distinguishes the three network conditions an operator most often confuses. Near 100 with high utilisation is a busy network — nothing is wrong. Near 300 is a domain with heavy broadcast or a lot of unknown unicast, and flood_share_pct with Section 4's cause counters says which. Above 1000 on a 24-port switch means something close to one port's worth of continuous flooding, and Section 8's arithmetic says the attached hosts are already in trouble whether or not anybody has noticed.

10. The Domain-Size Limit

Broadcast is the only flood type that every station must process, so it is the one that sets an upper bound on how many stations may share a domain. The bound is arithmetic and it is smaller than people expect.

Every station in a broadcast domain receives every other station's broadcasts. With N stations each emitting B broadcasts per second, each station receives (N − 1) × B per second. Set a CPU budget in frames per second and solve for N:

Per-station broadcast rateBudget 500/sBudget 1000/sBudget 2000/s
2 /s — quiet hostsN ≤ 251N ≤ 501N ≤ 1001
5 /s — typicalN ≤ 101N ≤ 201N ≤ 401
10 /s — chattyN ≤ 51N ≤ 101N ≤ 201

The middle cell is the one that matches the rule of thumb every network engineer carries: a few hundred stations per broadcast domain. It is not folklore. It is budget ÷ B + 1, and the reason nobody quotes the derivation is that both inputs are estimates.

Notice what the limit does not depend on. Not the switch's capacity — a 48-port switch with a 96 Gb/s fabric hits the same wall. Not the link speed — the constraint is frames per second at the receiver's CPU, and a 10 Gb/s link does not make a host's interrupt handler faster. Not the table size — broadcast never consults the table.

The limit depends only on how many stations can hear each other, which is precisely what a broadcast domain is.

The domain is not the switch

Section 10's N is the number of stations that can hear each other, and that is almost never the port count of one switch.

A broadcast domain spans every switch reachable without passing through a device that stops broadcast. Four 24-port switches, each with one port used as an uplink, put 4 × 23 = 92 stations in one domain — and every one of them receives every other's broadcasts.

one switchfour switches, one domain
stations N2392
each station receives at B = 5/s22 × 5 = 110 /s91 × 5 = 455 /s
host CPU at 5 µs0.06% of a core0.2% of a core
the uplink carriesall 460 domain broadcasts/s, 0.31 Mb/s

The numbers are comfortable here and the shape is the point: N grows with the topology, not with any one device, so a network built by adding switches grows its broadcast load quadratically in aggregateN stations each receiving (N−1) × B.

And the uplink row is the one that surprises people. Every broadcast in the domain crosses every inter-switch link, exactly once per link. A trunk between two buildings carries the broadcast traffic of both, and at 92 stations that is trivial — at 2000 stations and B = 5/s it is 10 000 × 672 = 6.7 Mb/s of pure broadcast on every uplink, permanently, before any user traffic.

In a broadcast domain of N stations each emitting B broadcasts per second, every station receives N minus one times B broadcasts per second, and every one of them reaches that station's CPU because no hardware filter can discard a broadcast. Setting a per-station CPU budget in frames per second and solving gives N less than or equal to budget divided by B plus one, which for a typical five broadcasts per second per station and a thousand frames per second budget gives about two hundred stations. The limit does not depend on the switch's aggregate capacity, on the link speed, or on the forwarding table's size, because broadcast never consults the table and the constraint is interrupt rate at the receiver. The only lever is the number of stations that can hear one another, which is the definition of a broadcast domain and the reason segmentation is the remedy.N stationsone broadcast domainB broadcasts eachARP, discovery, control(N − 1) × B receivedby every stationAll reach the CPUno filter is possibleNot switch capacitya bigger fabric does nothelpNot link speedinterrupts, not bitsOnly lever: domainsizeChapter 13.1's subject12
Figure 3 — the domain-size limit comes from the receiver's CPU, not the switch's capacity; the only variable a designer controls is how many stations can hear one another.

11. RTL 5 — The One Mitigation Available at This Layer

A switch cannot reduce broadcast, cannot filter it, and cannot tell whether any copy was wanted. It can cap the rate at which any one port injects it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// broadcast_rate_limiter -- storm control, per port, per cause.
//
// This does not make the network correct. It makes a catastrophe bounded.
// Section 12 derives what a given cap actually delivers at the receiver,
// and the answer is that the settings real switches offer are one to two
// orders of magnitude coarser than the receiver budget requires.
// -----------------------------------------------------------------------
module broadcast_rate_limiter
  import flood_pkg::*;
#(
  parameter int N_PORTS       = 24,
  parameter int PORT_BITS     = 5,
  parameter int BC_PPS_LIMIT  = 1000,   // broadcast frames/s admitted per port
  parameter int MC_PPS_LIMIT  = 5000,   // multicast is usually capped higher
  parameter int UU_PPS_LIMIT  = 20000,  // unknown unicast -- self-limiting
  parameter int CNT_W         = 32
)(
  input  logic                 clk,
  input  logic                 rst_n,
  input  logic                 second_tick,

  input  logic                 flood_req,
  input  flood_cause_e         cause,
  input  logic [PORT_BITS-1:0] ingress_port,

  output logic                 flood_permit,
  output logic [CNT_W-1:0]     c_suppressed,
  output logic [CNT_W-1:0]     suppressed_by_port [N_PORTS],
  output logic [PORT_BITS-1:0] worst_port,
  output logic                 storm_active,
  output flood_cause_e         storm_cause
);

  logic [CNT_W-1:0] bc_this_sec [N_PORTS];
  logic [CNT_W-1:0] mc_this_sec [N_PORTS];
  logic [CNT_W-1:0] uu_this_sec [N_PORTS];

  // Per CAUSE, because the three have different costs and different
  // remedies. A single combined cap would let a burst of self-limiting
  // unknown unicast consume the allowance that broadcast -- the expensive
  // one -- needs.
  always_comb begin
    flood_permit = 1'b0;
    if (flood_req) begin
      unique case (cause)
        FC_BROADCAST:
          flood_permit = (bc_this_sec[ingress_port] < CNT_W'(BC_PPS_LIMIT));
        FC_MULTICAST:
          flood_permit = (mc_this_sec[ingress_port] < CNT_W'(MC_PPS_LIMIT));
        FC_UNKNOWN_UNICAST, FC_UNRESOLVED:
          flood_permit = (uu_this_sec[ingress_port] < CNT_W'(UU_PPS_LIMIT));
        default:
          flood_permit = 1'b1;
      endcase
    end
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      for (int p = 0; p < N_PORTS; p++) begin
        bc_this_sec[p] <= '0; mc_this_sec[p] <= '0; uu_this_sec[p] <= '0;
        suppressed_by_port[p] <= '0;
      end
      c_suppressed <= '0;
      worst_port   <= '0;
      storm_active <= 1'b0;
      storm_cause  <= FC_NONE;
    end else begin
      if (flood_req) begin
        if (flood_permit) begin
          unique case (cause)
            FC_BROADCAST: bc_this_sec[ingress_port] <= bc_this_sec[ingress_port] + 1'b1;
            FC_MULTICAST: mc_this_sec[ingress_port] <= mc_this_sec[ingress_port] + 1'b1;
            FC_UNKNOWN_UNICAST,
            FC_UNRESOLVED: uu_this_sec[ingress_port] <= uu_this_sec[ingress_port] + 1'b1;
            default: ;
          endcase
        end else begin
          if (!(&c_suppressed)) c_suppressed <= c_suppressed + 1'b1;
          suppressed_by_port[ingress_port] <=
            suppressed_by_port[ingress_port] + 1'b1;
          // Record WHICH cause is storming. "Storm control engaged" with
          // no cause is not actionable; broadcast and multicast storms
          // have entirely different investigations.
          storm_cause <= cause;
        end
      end

      if (second_tick) begin
        automatic logic [CNT_W-1:0] hi = '0;
        automatic logic [PORT_BITS-1:0] hp = '0;
        for (int p = 0; p < N_PORTS; p++) begin
          if (suppressed_by_port[p] > hi) begin
            hi = suppressed_by_port[p];
            hp = PORT_BITS'(p);
          end
          bc_this_sec[p] <= '0;
          mc_this_sec[p] <= '0;
          uu_this_sec[p] <= '0;
        end
        worst_port   <= hp;
        storm_active <= (hi != '0);
      end
    end
  end

endmodule

Classification: synthesizable.

What it teaches: that the cap is per cause and per port, and both qualifiers are load-bearing. Per cause, because unknown-unicast flooding is self-limiting and cheap at the receiver while broadcast is permanent and expensive — a combined budget lets the harmless one consume the allowance the harmful one needs. Per port, because a shared budget lets one storming port suppress every other port's legitimate broadcast, which would break ARP for the whole switch in order to protect it.

And it teaches that suppression is a discard, not a queue. A suppressed broadcast is gone. There is no retry, no backpressure and no notification — the sender does not know, the intended receivers do not know, and the only record is suppressed_by_port. That is the correct behaviour and it is worth stating plainly, because the alternative — queueing broadcasts until the budget refreshes — delays ARP replies past their usefulness while still delivering the storm.

Deliberately simplified: hard per-second counters with a sharp edge at the limit. Production storm control uses a token bucket so that a legitimate burst — a rack powering on and every host ARPing at once — passes while the long-term average is held.

Production implication: storm_cause is the field that makes the feature usable. A switch that reports "storm control engaged on port 7" has told an operator almost nothing: a broadcast storm points at a loop or a misbehaving host, a multicast storm points at a video source or a discovery protocol, and an unknown-unicast storm points at Chapter 12.2 §12's table-filling attack. Same feature, same counter, three unrelated investigations.

12. What a Storm-Control Setting Actually Delivers

Storm control is configured as a percentage of line rate per port, and Section 10's limit is expressed in frames per second at the receiver. Convert between them and the settings real switches offer are revealed for what they are.

On a 24-port gigabit switch, a station receives broadcast from 23 other ports. With each port capped at a percentage of its line rate:

Cap per portFrames/s admitted per portStation receivesHost CPU at 5 µs/frame
10%148 8103 422 619 /s1711% — 17 cores
5%74 4051 711 310 /s856% — 8.6 cores
1%14 881342 262 /s171% — 1.7 cores
0.5%7 440171 131 /s86% of a core
0.1%1 48834 226 /s17% of a core

Every commonly-offered setting still permits a station to lose a substantial fraction of a core to broadcast. At the typical default of 1%, a fully "protected" switch still allows every attached host to spend 1.7 cores discarding frames it did not want.

Now solve the other direction. To hold a station at Section 10's 1000 frames per second budget, each of 23 ports must be capped at 1000 ÷ 23 = 43.5 frames per second — which is 0.00292% of line rate, roughly 34 times finer than a 0.1% setting and far below the granularity most switches expose.

The conclusion is uncomfortable and worth stating directly: storm control is a catastrophe limiter, not a design tool. It converts a network that has completely collapsed into one that is merely badly degraded. It does not, and at the available granularity cannot, hold a broadcast domain inside the budget that Section 10 derived.

Which is the argument for segmentation, and it is an arithmetic argument rather than a stylistic one. The only variable in (N − 1) × B that a designer actually controls is N — and reducing N is what Chapter 13.1 is about.

13. RTL 6 — Multicast: The Case With No Local Fix

Multicast is the only flood type where the switch is doing something demonstrably wasteful and demonstrably correct, and where no information available to it could do better.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// multicast_flood_model -- what a basic switch knows about a group
// address, which is nothing, and what it would need to know to do better.
//
// The module deliberately computes the WASTE it is producing without
// being able to avoid it. Every quantity here is derivable from the
// switch's own ports; the one quantity that would fix the problem --
// which ports contain members -- is not derivable from anything the
// switch can observe.
// -----------------------------------------------------------------------
module multicast_flood_model
  import flood_pkg::*;
#(
  parameter int N_PORTS = 24,
  parameter int CNT_W   = 32
)(
  input  logic               clk,
  input  logic               rst_n,

  input  logic               mc_flood,
  input  logic [ADDR_W-1:0]  mc_group,
  input  logic [7:0]         fanout,

  // ORACLE ONLY -- not available to any real switch at this layer. Wired
  // from the testbench so the waste can be quantified in simulation.
  input  logic [N_PORTS-1:0] oracle_member_mask,

  output logic [CNT_W-1:0]   c_mc_floods,
  output logic [CNT_W-1:0]   c_copies_sent,
  output logic [CNT_W-1:0]   c_copies_wanted,
  output logic [CNT_W-1:0]   c_copies_wasted,
  output logic [15:0]        waste_pct,
  output logic [ADDR_W-1:0]  worst_group,
  output logic               membership_would_help
);

  logic [CNT_W-1:0] worst_waste_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_mc_floods           <= '0;
      c_copies_sent         <= '0;
      c_copies_wanted       <= '0;
      c_copies_wasted       <= '0;
      waste_pct             <= '0;
      worst_group           <= '0;
      worst_waste_q         <= '0;
      membership_would_help <= 1'b0;
    end else if (mc_flood) begin
      automatic logic [CNT_W-1:0] wanted;
      automatic logic [CNT_W-1:0] wasted;

      wanted = CNT_W'($countones(oracle_member_mask));
      wasted = CNT_W'(fanout) - wanted;

      if (!(&c_mc_floods)) c_mc_floods <= c_mc_floods + 1'b1;
      c_copies_sent   <= c_copies_sent   + CNT_W'(fanout);
      c_copies_wanted <= c_copies_wanted + wanted;
      c_copies_wasted <= c_copies_wasted + wasted;

      // The share of emitted copies that no station wanted. On a group
      // with three members on a 24-port switch this is 20 of 23 copies,
      // or 87%.
      waste_pct <= (fanout == 8'd0) ? 16'd0
                 : 16'((wasted * CNT_W'(100)) / CNT_W'(fanout));

      if (wasted > worst_waste_q) begin
        worst_waste_q <= wasted;
        worst_group   <= mc_group;
      end

      // Membership knowledge helps only when the group has FEWER members
      // than the flood has destinations. A group every station joined
      // costs nothing to flood, and no mechanism could improve it.
      membership_would_help <= (wanted < CNT_W'(fanout));
    end
  end

endmodule

Classification: partly modeloracle_member_mask has no source in real hardware and exists only to quantify, in simulation, a waste the switch cannot measure.

What it teaches: that the group's membership is the one input that would fix multicast flooding, and it is precisely the input a basic switch has no way to obtain. Chapter 12.2 §4's first eligibility rule forbids learning a group address, and the reason is not conservatism — nothing ever transmits from a group address, so no frame anywhere carries evidence of membership. The learning mechanism that populates the unicast table has no analogue here, and the absence is structural.

And it teaches the shape of the waste. A group with 3 members on a 24-port switch produces a flood of 23 copies of which 20 are unwanted — 87%. The 20 unwanted copies are discarded by the receivers' hash filters, mostly for free — but Section 7's table showed that "mostly" degrades sharply: a station that has joined 64 groups passes 63.5% of unwanted multicast to its CPU.

Deliberately simplified: the oracle mask is supplied per flood. A production study derives it from a group-membership protocol's state, and the switch feature that uses it — snooping a membership protocol to build a per-group egress mask — lives above the layer this track covers, which is why this chapter can quantify the problem and not solve it.

Production implication: waste_pct is the number that justifies the feature that fixes this, and it is the number an engineer needs before deciding whether to enable it. A network whose multicast groups have most stations as members gains nothing from membership snoopingmembership_would_help is low, and the flood was already close to optimal. A network with many small groups is emitting 85–95% waste, and the same feature is transformative. The switch can compute everything in that sentence except the one term that makes it actionable.

14. Correct and Wasteful at the Same Time

Hold two statements together, because the tension between them is the honest position and most explanations pick one.

Flooding a multicast frame is correct. The address names a set. The switch does not know which ports lead to members. Delivering to every port guarantees that every member receives it, and any narrower behaviour risks failing to deliver to a member — which is the one error that would make multicast unusable.

Flooding a multicast frame is wasteful. On a group with three members and twenty-four ports, twenty of twenty-three copies are unwanted, consuming egress bandwidth, receiver bandwidth, and — as the receiver's filter degrades with group count — receiver CPU.

Both are true, and the resolution is not a better switch. It is more information, and the information does not exist at this layer.

can the switch obtain itwhy not
which ports lead to a unicast stationyesits frames carry its source address — Chapter 12.2
which ports lead to a multicast group's membersnonothing transmits from a group address
which ports want a groupnowanting is a receiver-side state with no wire representation

The middle row is the structural fact. Chapter 12.2's entire mechanism rests on the observation that a frame's source address proves where its sender is. A group address is never a source, so the mechanism has nothing to work with — not because it was designed badly, but because there is no frame to learn from.

The bottom row is why the fix must come from above. Membership is something a station decides, and the only way a switch could know is if the station said so — which requires a protocol, and a protocol is exactly what this layer does not have.

How much is wasted, by group size

The waste is (fanout − members) ÷ fanout, and it is worst exactly where multicast is most useful.

Members of the groupCopies emittedWastedWaste
1232295.7%
2232191.3%
3232087.0%
6231773.9%
12231147.8%
1823521.7%
23 — everyone2300%

The bottom row is the case where flooding is already optimal, and it is why membership_would_help exists: a group every station joined costs nothing to flood, and no mechanism could improve on what the switch is already doing.

The top rows are the ones that matter, and they are the common case. A group with one or two members — a video stream to a pair of displays, a control protocol between two controllers, a clock distribution to a handful of devices — wastes over 90% of the copies it generates. Multicast exists precisely so that a sender can reach a small set without unicasting to each, and at this layer the small set is exactly where the mechanism is most wasteful.

Which sharpens Section 13's membership_would_help into an operational rule: the value of membership snooping is waste_pct, it is highest for small groups, and a network with many small multicast groups is the one where enabling it changes the numbers.

15. RTL 7 — The Flood-to-Forward Ratio

One number characterises whether a switch is behaving like a switch, and it is the ratio between the two things it does.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_ratio_monitor -- the health metric for a learning switch.
//
// Chapter 12.2 Section 18 introduced the insert-to-refresh ratio for the
// learning side. This is its forwarding-side counterpart, and the two
// together localise almost every switching complaint before a packet is
// captured.
// -----------------------------------------------------------------------
module flood_ratio_monitor
  import flood_pkg::*;
#(
  parameter int CNT_W  = 32,
  parameter int WINDOW = 1_000_000
)(
  input  logic             clk,
  input  logic             rst_n,

  input  logic             decision_valid,
  input  logic             was_forward,
  input  logic             was_flood,
  input  flood_cause_e     cause,

  output logic             window_valid,
  output logic [15:0]      flood_pct,
  output logic [15:0]      bcast_share_pct,     // of floods, how many broadcast
  output logic             hub_like,            // flooding dominates
  output logic             learning_suspect,    // unknown unicast dominates
  output logic             traffic_suspect      // broadcast dominates
);

  logic [CNT_W-1:0] n_fwd, n_flood, n_uu, n_bc, n_total;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_fwd <= '0; n_flood <= '0; n_uu <= '0; n_bc <= '0; n_total <= '0;
      window_valid     <= 1'b0;
      flood_pct        <= '0;
      bcast_share_pct  <= '0;
      hub_like         <= 1'b0;
      learning_suspect <= 1'b0;
      traffic_suspect  <= 1'b0;
    end else begin
      window_valid <= 1'b0;

      if (decision_valid) begin
        n_total <= n_total + 1'b1;
        if (was_forward) n_fwd   <= n_fwd + 1'b1;
        if (was_flood)   n_flood <= n_flood + 1'b1;
        if (was_flood && (cause == FC_UNKNOWN_UNICAST)) n_uu <= n_uu + 1'b1;
        if (was_flood && (cause == FC_BROADCAST))       n_bc <= n_bc + 1'b1;
      end

      if (n_total >= CNT_W'(WINDOW)) begin
        flood_pct       <= 16'((n_flood * CNT_W'(100)) / n_total);
        bcast_share_pct <= (n_flood == '0) ? 16'd0
                         : 16'((n_bc * CNT_W'(100)) / n_flood);

        // A switch flooding a quarter of its decisions is spending most
        // of its egress bandwidth on replicas -- Section 3's arithmetic.
        hub_like <= (n_flood > (n_total >> 2));

        // WHICH cause dominates decides which chapter's diagnosis
        // applies. Unknown unicast is a learning-side condition;
        // broadcast is a statement about the attached stations.
        learning_suspect <= (n_uu > (n_flood >> 1)) &&
                            (n_flood > (n_total >> 4));
        traffic_suspect  <= (n_bc > (n_flood >> 1)) &&
                            (n_flood > (n_total >> 4));

        n_fwd <= '0; n_flood <= '0; n_uu <= '0; n_bc <= '0; n_total <= '0;
        window_valid <= 1'b1;
      end
    end
  end

endmodule

Classification: synthesizable.

What it teaches: that the ratio's composition matters more than its value. A flood rate of 30% means very different things depending on whether the floods are unknown unicast or broadcast. learning_suspect and traffic_suspect are mutually exclusive diagnoses reached from the same headline number, and a monitor that reported only flood_pct would send an operator to Chapter 12.2's table-tuning remedies half the time it should send them to Section 12's rate limiter.

And it teaches the calibration. In a stable network the floods are broadcast, the occasional multicast, and the first frame to a station the switch has not heard from — a low single-digit percentage. Above a quarter, Section 3's arithmetic means most of the switch's egress bandwidth is replicas.

Deliberately simplified: a single global window over all ports. Production monitoring keeps the ratio per ingress port, because a domain-wide broadcast problem and one misbehaving host look identical in the aggregate and completely different per port.

Production implication: hub_like is deliberately named for what it means rather than for what it measures. A switch flooding more than a quarter of its decisions has given up most of Chapter 12.1's 48× advantage and is approaching the behaviour of the device switching replaced. That is the sentence an operator needs, and it is more useful than a percentage they have to calibrate for themselves.

16. RTL 8 — Conformance for a One-to-Many Operation

Every other operation in a switch produces at most one frame. A flood produces N−1, and the accounting that proves it correct is different in kind.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// -----------------------------------------------------------------------
// flood_conformance_monitor -- checks a one-to-many operation.
//
// Chapter 12.3's monitor could reconcile frames in against decisions out,
// one for one. Here one frame in produces many frames out, so the
// accounting is against the MASK: every set bit produced exactly one
// emission, every clear bit produced none, and the count balances.
// -----------------------------------------------------------------------
module flood_conformance_monitor
  import flood_pkg::*;
#(
  parameter int N_PORTS   = 24,
  parameter int PORT_BITS = 5,
  parameter int CNT_W     = 32
)(
  input  logic                 clk,
  input  logic                 rst_n,

  input  logic                 flood_start,
  input  logic [N_PORTS-1:0]   flood_mask,
  input  logic [PORT_BITS-1:0] ingress_port,
  input  logic [N_PORTS-1:0]   forwarding_mask,

  input  logic                 emit_accepted,
  input  logic [PORT_BITS-1:0] emit_port,
  input  logic                 release_buffer,

  output logic [CNT_W-1:0]     v_ingress_copy,     // a copy to the ingress port
  output logic [CNT_W-1:0]     v_blocked_copy,     // a copy to a blocked port
  output logic [CNT_W-1:0]     v_duplicate_copy,   // two copies to one port
  output logic [CNT_W-1:0]     v_missing_copy,     // released with bits pending
  output logic [CNT_W-1:0]     v_extra_copy,       // a copy not in the mask
  output logic [CNT_W-1:0]     c_floods_checked,
  output logic                 conformant
);

  logic [N_PORTS-1:0] expect_q;   // bits still owed
  logic [N_PORTS-1:0] seen_q;     // bits already emitted
  logic               active_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      expect_q         <= '0;
      seen_q           <= '0;
      active_q         <= 1'b0;
      v_ingress_copy   <= '0;
      v_blocked_copy   <= '0;
      v_duplicate_copy <= '0;
      v_missing_copy   <= '0;
      v_extra_copy     <= '0;
      c_floods_checked <= '0;
    end else begin
      if (flood_start) begin
        expect_q <= flood_mask;
        seen_q   <= '0;
        active_q <= 1'b1;
        if (!(&c_floods_checked)) c_floods_checked <= c_floods_checked + 1'b1;

        // THE MASK ITSELF, checked before a single copy is emitted.
        // Chapter 12.3's P10 and P11 restated where the replication
        // happens rather than where the mask was built.
        if (flood_mask[ingress_port])
          if (!(&v_ingress_copy)) v_ingress_copy <= v_ingress_copy + 1'b1;
        if ((flood_mask & ~forwarding_mask) != '0)
          if (!(&v_blocked_copy)) v_blocked_copy <= v_blocked_copy + 1'b1;
      end

      if (emit_accepted && active_q) begin
        // A SECOND copy to a port that already had one. Chapter 12.1's
        // P10, and the invariant a careless replicator breaks first.
        if (seen_q[emit_port])
          if (!(&v_duplicate_copy)) v_duplicate_copy <= v_duplicate_copy + 1'b1;
        // A copy to a port the mask never named.
        if (!expect_q[emit_port])
          if (!(&v_extra_copy)) v_extra_copy <= v_extra_copy + 1'b1;

        seen_q[emit_port]   <= 1'b1;
        expect_q[emit_port] <= 1'b0;
      end

      // THE COMPLETENESS CHECK. The buffer was released while bits were
      // still owed -- one or more stations silently did not get the
      // frame, and nothing else in the design would ever notice.
      if (release_buffer) begin
        if ((expect_q & ~(N_PORTS'(1) << emit_port)) != '0)
          if (!(&v_missing_copy)) v_missing_copy <= v_missing_copy + 1'b1;
        active_q <= 1'b0;
        expect_q <= '0;
      end
    end
  end

  assign conformant = (v_ingress_copy   == '0) &&
                      (v_blocked_copy   == '0) &&
                      (v_duplicate_copy == '0) &&
                      (v_missing_copy   == '0) &&
                      (v_extra_copy     == '0);

endmodule

Classification: synthesizable, and intended to stay in silicon.

What it teaches: that a one-to-many operation needs a per-destination ledger, not a counter. Chapter 12.3 §14 could reconcile frames in against decisions out one for one. Here the obligation is a set, and the only way to prove it was discharged is to track which bits were satisfied — a count of emissions would not detect two copies to port 3 and none to port 7.

And v_missing_copy is the check worth building even if nothing else here is. A dropped flood copy is the most silent failure in a switch: the sender does not know, the intended receiver does not know it should have received anything, no error counter moves, and — as Section 14 established — Ethernet provides no return path by which any of them could find out. The only place it can be caught is here, at the moment the buffer is released with bits still owed.

Deliberately simplified: one flood tracked at a time, matching Section 5's replicator. A production monitor keeps a small table of in-flight floods keyed by buffer handle, since several floods share the buffer concurrently.

Production implication: conformant here means the flood reached exactly the set of ports the mask named, once each. It does not mean the flood was useful — Section 13 showed 87% of a small group's copies are unwanted — and it does not mean any station received anything. The narrow claim is the only one available, and Section 17 is about why the broader one cannot be written at all.

A flood begins with a mask naming the ports that must each receive one copy, which is the switch's obligation. The monitor records the mask as expected and clears each bit as a copy is accepted by its egress port. A copy accepted for a port whose bit is already cleared is a duplicate, violating the invariant that no port receives two copies. A copy accepted for a port the mask never named is an extra copy. If the buffer is released while any bit remains set, one or more stations silently did not receive the frame, which is the most silent failure available in a switch because Ethernet provides no receiver report and no counter elsewhere would move. Counting emissions instead of tracking the set would fail to distinguish two copies to one port and none to another from the correct one copy each.Mask = obligationone copy owed per set bitLedger of owed bitscleared on acceptanceAll bits clearedobligation dischargedRelease the bufferexactly onceBit already cleara duplicate copyBit never seta copy to nobody's portReleased with bitsoweda station silentlyskipped12
Figure 4 — a one-to-many operation cannot be proved by counting; the obligation is a set of ports and the proof is a ledger showing every bit discharged exactly once.

17. Properties Worth Asserting, and One Worth Refusing

Every property here is about the copies the switch emitted. Not one is about what happened to them, and that boundary is this chapter's rejected class.

Classification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P1. Broadcast is classified BEFORE the group test. FF:FF:FF:FF:FF:FF
// has the I/G bit set and would otherwise count as multicast.
property p_broadcast_before_group;
  @(posedge clk) disable iff (!rst_n)
  (flood_valid && (dst == {ADDR_W{1'b1}})) |-> (cause == FC_BROADCAST);
endproperty
a_bcast_first: assert property (p_broadcast_before_group);

// P2. A group destination that is not broadcast is multicast.
property p_group_is_multicast;
  @(posedge clk) disable iff (!rst_n)
  (flood_valid && dst[40] && (dst != {ADDR_W{1'b1}}))
    |-> (cause == FC_MULTICAST);
endproperty
a_group_multicast: assert property (p_group_is_multicast);

// P3. An individual destination flooding means the lookup did not answer.
property p_individual_flood_is_a_miss;
  @(posedge clk) disable iff (!rst_n)
  (flood_valid && !dst[40])
    |-> (cause inside {FC_UNKNOWN_UNICAST, FC_UNRESOLVED});
endproperty
a_individual_miss: assert property (p_individual_flood_is_a_miss);

// P4. Multicast and broadcast are flagged as PERMANENT -- they will not
// stop when a station transmits, because no station transmits from them.
property p_permanent_flood_flagged;
  @(posedge clk) disable iff (!rst_n)
  (flood_valid && (cause inside {FC_MULTICAST, FC_BROADCAST}))
    |-> permanent_flood;
endproperty
a_permanent_flagged: assert property (p_permanent_flood_flagged);

// P5. Exactly one cause counter moves per flood.
property p_one_cause_counted;
  @(posedge clk) disable iff (!rst_n)
  flood_valid |=> ($changed(c_unknown_unicast) + $changed(c_multicast) +
                   $changed(c_broadcast) + $changed(c_unresolved)) == 1;
endproperty
a_one_cause: assert property (p_one_cause_counted);

Replication

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P6. THE COMPLETENESS PROPERTY. Every set bit in the mask produces
// exactly one emission before the buffer is released.
property p_every_masked_port_served;
  @(posedge clk) disable iff (!rst_n)
  release_buffer |-> (expect_q == '0);
endproperty
a_all_ports_served: assert property (p_every_masked_port_served);

// P7. NEVER twice to one port -- Chapter 12.1's P10, at the point where
// replication actually happens.
property p_no_duplicate_copy;
  @(posedge clk) disable iff (!rst_n)
  (emit_accepted && active_q) |-> !seen_q[emit_port];
endproperty
a_no_dup_copy: assert property (p_no_duplicate_copy);

// P8. NEVER to the ingress port.
property p_no_copy_to_ingress;
  @(posedge clk) disable iff (!rst_n)
  flood_start |-> !flood_mask[ingress_port];
endproperty
a_no_ingress_copy: assert property (p_no_copy_to_ingress);

// P9. NEVER to a port that may not forward -- Chapter 12.3's P11.
property p_no_copy_to_blocked;
  @(posedge clk) disable iff (!rst_n)
  flood_start |-> ((flood_mask & ~forwarding_mask) == '0);
endproperty
a_no_blocked_copy: assert property (p_no_copy_to_blocked);

// P10. The pending bit clears on ACCEPTANCE, not on issue. Clearing on
// issue counts a copy the egress refused as emitted, and the lost copy
// is a station that silently did not get the frame.
property p_clear_on_accept_only;
  @(posedge clk) disable iff (!rst_n)
  (emit_valid && !emit_ready) |=> $stable(pending_q);
endproperty
a_clear_on_accept: assert property (p_clear_on_accept_only);

// P11. A new flood is not accepted while one is draining -- accepting it
// would silently discard the remaining copies of the first.
property p_no_overlapping_floods;
  @(posedge clk) disable iff (!rst_n)
  (flood_start && busy) |=> $stable(pending_q);
endproperty
a_no_overlap: assert property (p_no_overlapping_floods);

// P12. The frame is stored ONCE. The handle emitted with every copy is
// the same handle.
property p_single_stored_copy;
  @(posedge clk) disable iff (!rst_n)
  (emit_valid && busy) |-> (emit_handle == handle_q);
endproperty
a_one_buffer: assert property (p_single_stored_copy);

// P13. The buffer is released exactly once, on the last copy.
property p_release_once_on_last;
  @(posedge clk) disable iff (!rst_n)
  release_buffer |-> $past(emit_valid && emit_ready &&
                           ($countones(pending_q) == 1));
endproperty
a_release_last: assert property (p_release_once_on_last);

Accounting

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P14. Emitted copies equal the sum of fanouts. A shortfall is a copy
// that vanished, and nothing else in the system would report it.
property p_copies_match_fanout;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> (c_copies_out >= c_frames_in);
endproperty
a_copies_ge_frames: assert property (p_copies_match_fanout);

// P15. Amplification is at least 100 -- a switch cannot emit fewer bits
// than it received unless it is discarding, which is counted elsewhere.
property p_amplification_at_least_unity;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> (amplification_x100 >= 16'd100);
endproperty
a_amp_ge_unity: assert property (p_amplification_at_least_unity);

// P16. The fanout never exceeds N-1.
property p_fanout_bounded;
  @(posedge clk) disable iff (!rst_n)
  flood_start |-> (8'($countones(flood_mask)) <= 8'(N_PORTS - 1));
endproperty
a_fanout_bounded: assert property (p_fanout_bounded);

// P17. The flood share of egress bits is attributed, so that a high
// amplification can be traced to flooding rather than guessed at.
property p_flood_share_computed;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> (flood_share_pct <= 16'd100);
endproperty
a_flood_share: assert property (p_flood_share_computed);

Rate limiting

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P18. The cap is PER CAUSE. A burst of self-limiting unknown unicast
// must not consume the allowance broadcast needs.
property p_per_cause_budget;
  @(posedge clk) disable iff (!rst_n)
  (flood_req && (cause == FC_BROADCAST) && !flood_permit)
    |-> (bc_this_sec[ingress_port] >= CNT_W'(BC_PPS_LIMIT));
endproperty
a_per_cause: assert property (p_per_cause_budget);

// P19. The cap is PER PORT. One storming port must not suppress another
// port's legitimate broadcast.
property p_per_port_budget;
  @(posedge clk) disable iff (!rst_n)
  (flood_req && !flood_permit)
    |-> (suppressed_by_port[ingress_port] != $past(suppressed_by_port[ingress_port]));
endproperty
a_per_port: assert property (p_per_port_budget);

// P20. A suppressed flood is DISCARDED, never queued. A delayed ARP
// reply is worse than an absent one.
property p_suppressed_not_queued;
  @(posedge clk) disable iff (!rst_n)
  (flood_req && !flood_permit) |=> !flood_start;
endproperty
a_suppress_discards: assert property (p_suppressed_not_queued);

// P21. A storm names its CAUSE. "Storm control engaged" without a cause
// is three unrelated investigations collapsed into one alert.
property p_storm_names_cause;
  @(posedge clk) disable iff (!rst_n)
  storm_active |-> (storm_cause != FC_NONE);
endproperty
a_storm_cause: assert property (p_storm_names_cause);

// P22. A storm names its PORT.
property p_storm_names_port;
  @(posedge clk) disable iff (!rst_n)
  storm_active |-> (suppressed_by_port[worst_port] != '0);
endproperty
a_storm_port: assert property (p_storm_names_port);

Diagnosis

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// P23. The flood ratio's COMPOSITION is reported, not only its value --
// the same percentage means different things for different causes.
property p_ratio_composition_reported;
  @(posedge clk) disable iff (!rst_n)
  window_valid |-> !(learning_suspect && traffic_suspect);
endproperty
a_diagnosis_exclusive: assert property (p_ratio_composition_reported);

// P24. hub_like implies the flood share is above a quarter.
property p_hub_like_definition;
  @(posedge clk) disable iff (!rst_n)
  (window_valid && hub_like) |-> (flood_pct > 16'd25);
endproperty
a_hub_like_def: assert property (p_hub_like_definition);

// P25. Multicast waste is computed but NEVER acted on -- the oracle is
// a measurement aid and must not reach the forwarding path.
property p_oracle_does_not_gate_forwarding;
  @(posedge clk) disable iff (!rst_n)
  mc_flood |-> (fanout == $past(fanout_from_mask));
endproperty
a_oracle_advisory: assert property (p_oracle_does_not_gate_forwarding);

// P26. Conformance is the conjunction of the five ledger violations, and
// it means the mask was discharged -- never that anybody received
// anything.
property p_conformant_definition;
  @(posedge clk) disable iff (!rst_n)
  conformant |-> ((v_ingress_copy == '0) && (v_blocked_copy == '0) &&
                  (v_duplicate_copy == '0) && (v_missing_copy == '0) &&
                  (v_extra_copy == '0));
endproperty
a_conformant_def: assert property (p_conformant_definition);

// P27. Every flood is checked, so a flood that bypassed the monitor is
// itself detectable.
property p_every_flood_checked;
  @(posedge clk) disable iff (!rst_n)
  flood_start |=> (c_floods_checked > $past(c_floods_checked));
endproperty
a_all_floods_checked: assert property (p_every_flood_checked);

// P28. The frame emitted on every port is byte-identical to the frame
// received. A flood replicates; it does not rewrite.
property p_copies_are_identical;
  @(posedge clk) disable iff (!rst_n)
  (emit_accepted && active_q) |-> (emit_handle == handle_q);
endproperty
a_copies_identical: assert property (p_copies_are_identical);

// P29. Broadcast reaches the CPU unconditionally in the receiver model.
// There is no configuration, filter or card that changes this, and the
// property exists to make the absence of an alternative explicit.
property p_broadcast_always_to_cpu;
  @(posedge clk) disable iff (!rst_n)
  (rx_valid && (rx_dst == {ADDR_W{1'b1}})) |-> (fate == RX_TO_CPU);
endproperty
a_bcast_to_cpu: assert property (p_broadcast_always_to_cpu);

// P30. A unicast frame not addressed to this station NEVER reaches the
// CPU -- the exact 48-bit compare is why unknown-unicast flooding costs
// bandwidth and nothing else.
property p_foreign_unicast_never_to_cpu;
  @(posedge clk) disable iff (!rst_n)
  (rx_valid && !rx_dst[40] && (rx_dst != my_address)) |-> !to_cpu;
endproperty
a_foreign_unicast_dropped: assert property (p_foreign_unicast_never_to_cpu);

18. Verification Scenarios

Sixty-three scenarios. The ledger scenarios have no acceptable failure; the cost scenarios have expected values that include large amounts of waste.

Classification

#ScenarioExpected
1Destination FF:FF:FF:FF:FF:FFFC_BROADCAST — not multicast
2Destination 01:00:5E:00:00:01FC_MULTICAST
3Destination 01:00:00:00:00:00FC_MULTICAST — the group bit alone
4Destination FE:FF:FF:FF:FF:FFindividual — I/G clear, so a miss, not multicast
5Individual destination, lookup missedFC_UNKNOWN_UNICAST
6Individual destination, lookup timed outFC_UNRESOLVEDChapter 12.3's deadline
7Any floodexactly one cause counter moves
8Broadcast or multicastpermanent_flood asserts
9Unknown unicastpermanent_flood low — it will stop
10Station transmits after an unknown-unicast floodthe next frame to it forwards, flood ends

Replication ledger

#ScenarioExpected
11Flood mask with 23 bits, 24-port switchexactly 23 emissions, one per port
12Any floodno copy to the ingress port
13Flood with 8 of 24 ports forwarding7 emissions; last_fanout = 7
14Flood with only the ingress port forwardingzero emissions, mask empty
15Egress applies backpressure mid-drainthe pending bit stays set; the copy is retried
16Egress backpressure held for 100 cyclesno copy lost, busy stays high
17Second flood_start while drainingignored — the first flood's copies are not discarded
18Any floodemit_handle identical on every copy — one stored frame
19Last copy acceptedrelease_buffer asserts exactly once
20Buffer released with bits pendingv_missing_copy increments
21Two copies emitted to one portv_duplicate_copy increments
22A copy to a port not in the maskv_extra_copy increments
23Mask containing the ingress bitv_ingress_copy increments at flood_start
24Mask containing a non-forwarding portv_blocked_copy increments
251000 floods, healthy designconformant high throughout

Amplification and cost

#ScenarioExpected
26Pure unicast forwarding, no floodsamplification_x100 = 100
27One port flooding at line rate, 24-port switch23 Gb/s egress demand — 47.9% of aggregate
28Same, minimum-length frames34.23 M emissions/s — 95.9% of the frame budget
298-port switch, same conditions7 copies, 43.8% of aggregate
3048-port switch, same conditions47 copies, 49.0% — the share converges on one half
3150% floods by frame countamplification_high asserts
32flood_share_pct under mixed trafficaccounts for the excess above 100

Receiver model

#ScenarioExpected
33Unknown-unicast copy at a non-addresseeRX_DROP_EXACT — zero host cost
34Unicast copy at its addresseeRX_WANTED
35Broadcast copy at any stationRX_TO_CPU — always, no exceptions
36Multicast, group not in an enabled bucketRX_DROP_HASH
37Multicast, group hashing to an enabled bucketRX_TO_CPU — a false accept
38Station with 1 joined group, 64 buckets~1.6% of unwanted groups reach the CPU
39Station with 16 joined groups~22.3%
40Station with 256 joined groups~98.2% — the filter has effectively stopped working
411.4881 M broadcasts/s at 5 µs each744% of a core — 7.4 cores per station

Rate limiting

#ScenarioExpected
42Broadcast at 2× the cap on port 3suppressed, worst_port = 3, storm_cause = FC_BROADCAST
43Broadcast at the cap exactlyall permitted
44Broadcast storm on port 3, ARP on port 7port 7 unaffected — per-port budget
45Unknown-unicast burst while broadcast is near its capbroadcast still permitted — per-cause budgets
46A suppressed broadcastdiscarded, never queued or retried
47Storm endsstorm_active deasserts on the next second tick
48Cap at 1% of line rate, 24 portsa station still receives 342 262/s — 1.7 cores
49Cap needed for a 1000/s station budget43.5 pps per port — 0.00292% of line rate

Multicast waste and diagnosis

#ScenarioExpected
50Group with 3 members, 24-port switch23 copies, 20 wasted — waste_pct = 87
51Group with 23 memberswaste_pct = 0, membership_would_help low
52Floods at 30%, mostly unknown unicastlearning_suspectChapter 12.2's remedies apply
53Floods at 30%, mostly broadcasttraffic_suspect — a statement about the stations
54Floods above 25% of decisionshub_like asserts
55Domain of 92 stations across 4 switcheseach receives 455 broadcasts/s; the uplink carries all 460
56100 000 broadcasts/s of 64-octet frameslink utilisation 6.7%, hosts at 0.5 cores
57Same rate with 1518-octet frameslink utilisation 123% — impossible, so the storm is small frames
58amplification_x100 on a pure-unicast runexactly 100
59A flood on a switch where every port is blocked but the ingressmask empty, zero emissions, release_buffer still asserts
60Storm control disabled, line-rate broadcasthub_like, amplification_high, and Section 8's host collapse
61Multicast group with 1 member on a 24-port switchwaste_pct = 95.7
62Multicast group with 12 memberswaste_pct = 47.8, membership_would_help high
63Every station joined the groupwaste_pct = 0, membership_would_help low

19. Debugging Flooding

Every row produces valid links, zero frame errors, and a switch forwarding correctly. The third column is what separates them, and the first two columns are frequently confused with each other.

SymptomLikely causeThe observable that decides it
Everything slow for everyone at oncebroadcast stormc_broadcast rate, storm_active, storm_cause = FC_BROADCAST
Everything slow, hosts also unresponsivebroadcast storm, hosts CPU-boundSection 8's arithmetic — the hosts are the casualty, not the network
Flood rate high, table not fullmany unlearned stationslearning_suspect, and Chapter 12.2 §18's remedies
Flood rate high, c_broadcast dominantthe stations are chattytraffic_suspectno switch change will help
Flood rate high, c_unresolved non-zerothe lookup engine is over budgetChapter 12.3 §11 — not a flooding problem at all
Egress bytes far exceed ingress bytesreplication, which is expectedamplification_x100 and flood_share_pct say how much and why
One host misses some multicast, others fineits hash filter is saturatedits joined-group count against Section 7's table
One host has high CPU, network is finebroadcast reaching it, correctlyc_broadcast — the switch is working; the domain is too big
A multicast group's members stop receivinga group source address was learnedChapter 12.2 §5 — LE_GROUP_SRC, and the lookup now hits
Storm control engaged, no idea whythe alert lacks a causestorm_cause and worst_port — three unrelated investigations
ARP intermittently failing after enabling storm controlthe cap is below legitimate broadcastsuppressed_by_port non-zero on a port with no storm
One station never receives floodsa copy is being lost in the drainv_missing_copy — Section 18's directed test
Link utilisation normal, every host at high CPUbroadcast storm of minimum-length framesc_broadcast per port — utilisation reads 6.7% at 0.5 cores per host
Broadcast load rises when a switch is addedthe domain grew, not the trafficN is topological — Section 10's uplink row
A flood emits nothing at allevery port except the ingress is blockedlast_fanout = 0 with release_buffer still asserting — correct

20. Common Misconceptions

1 — "Flooding is a failure."

The wrong model: a switch that floods has failed to do its job, and the flood rate should be driven toward zero.

What it costs: it produces Chapter 12.2's catalogue of bad remedies — hold entries longer, refresh on lookup, never expire — every one of which trades wasted bandwidth for silent loss. It also misreads broadcast entirely, since broadcast flooding is not a failure to know anything.

The corrected model: flooding is the switch declining to guess, and it is correct under all five of Chapter 12.3 §2's reasons for a miss. The flood is also the mechanism that ends the flood — it reaches the station, the station replies, and the reply teaches the switch. A high flood rate is a cost to understand, not an error to eliminate.

2 — "A broadcast storm is a bandwidth problem."

The wrong model: too many frames, the links saturate, everything slows down.

What it costs: the diagnosis goes to utilisation graphs, which read 6.7% on a gigabit link at a storm rate that is already costing every host half a core. And the remedy goes to capacity — a faster link, a bigger switch — neither of which touches the actual constraint, since the cost is per frame at the receiver's CPU and a 10 Gb/s link does not make an interrupt handler faster.

The corrected model: a broadcast storm is a CPU problem at every station simultaneously. At 1.4881 M frames/s and 5 µs each it is 7.4 cores per host, which is why management agents, monitoring and remote access fail before the network does — and why the instruments an operator would reach for are among the first casualties.

3 — "A bigger MAC table would reduce flooding."

The wrong model: floods come from table misses, so more table means fewer floods.

What it costs: money, and no improvement. The table affects exactly one of the three causes — and it is the self-limiting one that ends on the station's next frame anyway. Multicast can never be in the table (Chapter 12.2 §4 forbids learning a group address, because nothing transmits from one) and broadcast never consults it (Chapter 12.3 §10 skips the lookup entirely).

The corrected model: check c_unknown_unicast against c_broadcast and c_multicast before buying anything. A bigger table helps only if the first dominates, and if it does, Chapter 12.2 §12's rate limiter and ageing interval are cheaper places to look first.

4 — "Unknown-unicast flooding and broadcast flooding cost the same."

The wrong model: both produce N−1 copies, so both cost N−1 copies.

What it costs: the two are conflated in a single "flood" counter, and an operator cannot tell a transient learning condition from a permanent load on every host. Inside the switch they genuinely are identical — same mask, same replication, same egress bandwidth — which is why the mistake is so easy to make.

The corrected model: they diverge completely at the receiver. An unknown-unicast copy dies in a 48-bit hardware comparator at zero host cost. A broadcast copy always reaches the CPU. Same bandwidth, and a difference of several orders of magnitude in the cost that actually limits the network.

5 — "Storm control solves broadcast."

The wrong model: enable storm control at the default and the domain is protected.

What it costs: false confidence. Section 12's arithmetic: at the common 1% default on a 24-port switch, a station still receives 342 262 broadcasts per second — 1.7 cores. And the cap that would actually meet a 1000/s budget is 43.5 frames per second per port, 0.00292% of line rate, roughly 34 times finer than a 0.1% setting and below the granularity most switches expose.

The corrected model: storm control is a catastrophe limiter. It turns a collapsed network into a badly degraded one and cannot hold a domain inside its budget. The design tool is segmentation — reducing N in (N − 1) × B — which discards nothing and is Chapter 13.1's subject.

6 — "Multicast flooding is a switch defect."

The wrong model: the switch should know which ports have members and send only there.

What it costs: engineering effort aimed at the wrong layer, and — if anyone succeeds in making the switch guess — a switch that sometimes fails to deliver to a member, which is the single error multicast cannot tolerate.

The corrected model: the switch flooding a multicast frame is simultaneously wasteful and correct, and the missing input is structural rather than an oversight. Chapter 12.2's learning works because a frame's source address proves where its sender is; a group address is never a source, so there is no frame to learn from and no evidence to gather. Membership is a receiver-side decision with no wire representation at this layer, and the mechanism that fixes it necessarily lives above it.

21. Interview Reasoning

Q1 — "One port on a 24-port gigabit switch is flooding at line rate. Quantify what that does to the switch."

Reason through it. Each flooded frame is replicated to 23 ports. In bits: 1 Gb/s in becomes 23 Gb/s of egress demand — 47.9% of the switch's 48 Gb/s aggregate, from one port. In frames, at minimum length: 1.4881 M × 23 = 34.23 M emissions per second, against Chapter 12.1 §12's total budget of 35.71 Mpps — 95.9% of the switch's entire frame-handling capacity. The strong answer adds that a bigger switch does not help, because the amplification scales with the port count: (N−1) ÷ 2N converges on one half regardless of N, so a 48-port switch suffers the same proportional damage and inflicts it on twice as many stations.

Q2 — "Why does a broadcast storm take down hosts before it takes down the network?"

Reason through it. Because the two costs scale differently and land in different places. Bandwidth degrades gradually — Section 3's amplification takes up to half the aggregate, so other traffic slows but continues. Host CPU collapses abruptly, because a network card cannot filter broadcast: the address means everyone, and discarding it would break ARP and DHCP. Every broadcast reaches every host's CPU, at roughly 5 µs of interrupt and stack traversal each, so 100 000 frames per second is half a core and line rate is 7.4 cores. The strong answer notes the operational consequence: management agents, monitoring and remote sessions fail first, so the operator loses their instruments before they lose connectivity — and a utilisation graph reads 6.7% throughout, because 100 000 minimum-length frames is only 67 Mb/s.

Q3 — "Derive the maximum number of stations in a broadcast domain."

Reason through it. Every station receives every other station's broadcasts, so with N stations each emitting B per second, each receives (N − 1) × B. Set a per-station CPU budget in frames per second and solve: N ≤ budget ÷ B + 1. At a typical B = 5/s and a budget of 1000/s, N ≤ 201 — which is exactly the "few hundred stations per domain" rule of thumb, derived rather than recited. The strong answer names what the limit does not depend on: not the switch's capacity, not the link speed, not the table size — because broadcast never consults the table and the constraint is interrupt rate at the receiver. The only lever is N, which is why segmentation and not capacity is the remedy.

Q4 — "Your switch floods a multicast frame to 23 ports when the group has 3 members. Is that a bug?"

Reason through it. No — it is simultaneously wasteful and correct, and the resolution is not a better switch. The switch has no way to know which ports lead to members. Chapter 12.2's learning works because a frame's source address proves where its sender is; nothing ever transmits from a group address, so there is no frame carrying evidence of membership and the learning mechanism has nothing to work with. The strong answer quantifies the waste — 20 of 23 copies, 87% — and then states the asymmetry that resolves the tension: the cost of being wastefully right is bounded bandwidth; the cost of being efficiently wrong is a member that silently stops receiving, which is the one error multicast cannot tolerate. The fix requires membership information, which is a receiver-side state with no wire representation at this layer, so it necessarily comes from a protocol above it.

Q5 — "Why can't you write an assertion that a flooded copy was useful?"

Reason through it. Because Ethernet has no return path. A frame is transmitted and the transmitter learns nothing — no acknowledgement, no receiver report, no completion, no negative acknowledgement. Chapter 1.2's collision detection was the last mechanism by which a transmitter learned anything about its own transmission's fate, and Chapter 1.5 removed it. So the property's subject is unobservable by construction of the protocol, not by an omission in the design — it would remain unobservable on perfect silicon. The strong answer names what to assert instead: the ledger, which proves every masked port got exactly one copy and none was lost in the drain, and the local arithmetic — amplification, flood share, cause composition, rate limits — all computed from the switch's own ports. And it names the honest way to get what the property wanted: a measurement campaign correlating captures against known membership, which is not an assertion.

Q6 — "A monitoring system reports a gigabit link at 6.7% utilisation. Every host on that segment is at 50% CPU. Reconcile these."

Reason through it. The two are consistent, and the reconciliation is that broadcast costs are per frame while utilisation is per bit. 6.7% of a gigabit link, if the frames are minimum-length, is 0.067 × 1e9 ÷ 672 = about 100 000 frames per second — and at roughly 5 µs of interrupt and stack traversal each, that is 0.5 of a core on every station in the domain. The strong answer names why no filter helps: a network card cannot discard broadcast, because the address means everyone and discarding it would break ARP and DHCP, so every one of those 100 000 frames reaches every host's CPU. It then names the instrument that would have shown it — broadcast frames per second per port, a counter incremented on one address comparison — and observes that it is absent from most switch statistics pages precisely because every other port metric is a volume.

22. Understanding Check

23. What's Next

This chapter priced the flood. It has said nothing about how often one happens, and that is a property of the structure Chapter 12.3 §11 called the only scarce resource in the decision.

Every unknown-unicast flood in this chapter began as a table miss, and the table's capacity, its collision behaviour and its ageing sweep decide how many misses a network produces. Chapter 12.5 — The MAC Table builds it: CAM against hashed set-associative, what a hash collision costs, why a table can refuse an insert while it is 50% empty, and where Chapter 12.3's 28 ns deadline is actually met or missed.

Then Chapter 12.6 — Store-and-Forward against Cut-Through returns to Chapter 12.1 §10's choice with the full six-gate decision in view, because a cut-through switch must run every gate before the frame has finished arriving — and one of the six needs the last four octets.

And Chapter 13.1 — Why VLANs Exist takes up the conclusion Section 12 reached and could not act on. The only variable in (N − 1) × B a designer controls is N, and reducing it is what segmentation does — at a cost this chapter has not yet counted.

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.