Skip to content
VLSI Mentor

Ethernet · Module 23

A High-Rate Data-Centre NIC

Descriptors are 38% of the payload at minimum frame size and break a Gen5 host interface at 400 Gb/s — and the part's dominant state is a context cache with a cliff at 39.4%.

Chapter 23.3 faced sixty-four ports sharing one buffer. A NIC faces one port sharing a host, and every number in it is set by two quantities: 6.72 nanoseconds and the width of a PCIe link.

At 100 Gb/s, minimum framesValue
packet rate148.81 Mpps
time per packet6.72 ns — 6.7 cycles at 1 GHz
payload across the host interface100 Gb/s
descriptor fetch and writeback, 16 octets each38.1 Gb/s — 38.1% of the payload
total host bandwidth needed138.1 Gb/s
PCIe Gen5 ×16, at 75% TLP efficiency378 Gb/s

Row four is the chapter's first result and it surprises people. A descriptor is sixteen octets and a minimum frame is sixty-four; fetching one descriptor and writing one back costs thirty-two octets per packet, which is half the frame and 38% of the link's payload bandwidth in DMA traffic that carries no data at all.

And it scales the wrong way.

Line ratePackets/sPayloadDescriptorsTotalGen5 ×16 gives 378
100 Gb/s148.81 M10038138fits
200 Gb/s297.62 M20076276fits
400 Gb/s595.24 M400152552does not fit

At 400 Gb/s a NIC's descriptor traffic alone is 152 Gb/s, and payload plus descriptors exceed a Gen5 ×16 host interface by 46%. The descriptors are why, not the payload.

Then the structural finding, which is the opposite of a switch's.

A 100 Gb/s NIC's on-die stateBCE× a MAC datapathShare
connection contexts, 16k × 256 octets3.36 × 10⁷118.466.2%
packet buffer, 2 MB1.60 × 10⁷56.531.6%
queue state, 2 048 queues1.05 × 10⁶3.72.1%
reorder buffer, 12 KiB — Chapter 19.6 §49.83 × 10⁴0.30.2%
total5.07 × 10⁷179.0

Chapter 23.3 §4 found a switch that was 91% packet buffer. A NIC is 66% connection context and eleven times smaller in total, and the difference decides everything about how it fails.

Because context is a cache, and a cache has a cliff.

Value
host bandwidth left after payload and descriptors239.9 Gb/s
a context miss costs a fill and an eviction writeback512 octets
at 148.81 Mpps, a 100% miss rate609.5 Gb/s
the miss rate the host interface can sustain39.4%
Chapter 23.2 §11's striping ×32 — 32 768 connections against a 16k cache50.0% miss

Row five is Chapter 23.2 §11's warning, derived. That chapter's cheapest remedy for a training cluster — stripe each ring link into 32 connectionsputs twice the working set into the cache and pushes the miss rate past the cliff, producing a throughput collapse that looks exactly like the fabric problem striping was deployed to fix.


1. Scope — One Port, One Host, and 6.72 Nanoseconds

Scope: a fixed-function high-rate NIC — its per-packet budget, its host interface, its queues, and the state that dominates it.

Not in scope: programmable offload. What a NIC does when it can run arbitrary code is Chapter 23.5's. This chapter's part parses, classifies, checksums, steers and moves bytes, all in fixed logic, and the interesting constraints turn out to be about neither logic nor bytes.

Two numbers govern the chapter.

QuantityConsequence
16.72 ns per packeteverything per-packet must fit in 6.7 cycles
2378 Gb/s of usable host bandwidthpayload, descriptors and context misses share it

Number 1 is the same constraint Chapter 23.3 §6 met and resolved differently. A switch has 64 ports and made its pipeline 8 wide; a NIC has one port and 6.72 ns is a real per-packet budget rather than an aggregate one. The NIC cannot go wide — there is only one packet stream — so it goes deep, and its pipeline is a few tens of stages of fixed work.

Number 2 is the one this chapter is really about. A switch's constraint is on-die memory; a NIC's is the pipe to the host. Everything a NIC does that touches host memory — fetching a descriptor, writing one back, filling a context, evicting one, moving the payload — competes for the same 378 Gb/s, and Sections 4, 6 and 12 are three different ways of running out of it.

Three things the chapter establishes.

ResultSection
descriptors are 38% of the payload and scale with packet rate4 and 6
a NIC's dominant state is connection context, not packet buffer10 and 17
context is a cache, so throughput is a step function of the working set12 and 20

2. The Per-Packet Budget, and What Fits In It

Derive the budget and then list what has to happen inside it.

minimum frame plus preamble and gap = 84 octets = 672 bits

at 100 Gb/s: 672 ÷ 100 × 10⁹ = 6.72 ns

Line ratePackets/sns per packetCycles at 1 GHz
10 Gb/s14.88 M67.267
100 Gb/s148.81 M6.726.7
200 Gb/s297.62 M3.363.4
400 Gb/s595.24 M1.681.7

Row four is under two cycles, which is why a 400 Gb/s NIC's receive path is pipelined and partly replicated rather than iterative — the same answer Chapter 23.3 §6 reached for a switch, arrived at from the opposite direction.

What must happen in those 6.7 cycles, per packet.

StepWhat it needs
parse the headersfixed logic, a few stages
verify the check sequenceChapter 6.4's parallel engine
compute the RSS hashChapter 18.7 §8's engine
steer to a queueChapter 18.7 §9's indirection table
verify or compute a checksumChapter 18.7 §3's receive engine
obtain a free descriptorChapter 18.2 §3's prefetch store — a host memory read
write the payload to host memoryChapter 19.6's interface
write the descriptor backanother host memory write
possibly signal an interruptmoderated — Section 13

Rows one to five are logic and fit comfortably. Chapter 18.7 §18 priced the whole offload set and it is a few thousand flops; at 6.72 ns per packet the work is pipelined and the throughput is one packet per cycle regardless of depth.

Rows six to eight are the problem, and they are not logic at all. Each is a host memory transaction with a latency of hundreds of nanoseconds to microseconds, against a budget of 6.72. They cannot be done inside the budget; they must be done ahead of it, which is Chapter 18.2 §3's prefetch store and Section 7's in-flight model.

Nothing a NIC does per packet takes longer than 6.72 nanoseconds. Everything it does that touches the host takes a hundred times that, and the entire design is about hiding the difference.

Which gives the chapter's organising split.

On the NICAcross the host interface
latencya few nanosecondshundreds of nanoseconds to microseconds
bandwidtheffectively unlimited378 Gb/s, shared by everything
costlogic — a few thousand flopsstate to hide the latency, and the bandwidth itself
what limits itnothing, at these ratesSections 4, 6, 8 and 12

3. RTL 1 — The NIC Package and the Host Bandwidth Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// dcnic_pkg -- a 100 Gb/s fixed-function NIC, and the one resource
// everything in it competes for.
//
// Chapter 23.3's package held an area model because a switch's problem is
// on-die memory. This one holds a BANDWIDTH model, because a NIC's
// problem is the pipe to the host -- and Section 12 shows the two are
// connected through a cache.
// ---------------------------------------------------------------------
package dcnic_pkg;

  // ---- the wire ---------------------------------------------------------
  localparam int RATE_GBPS      = 100;
  localparam int MIN_WIRE_OCTETS= 84;          // frame + preamble + IFG
  localparam int PPS_MILLION    = 148;         // 148.81, floored
  localparam int NS_PER_PKT_X100= 672;         // 6.72 ns

  // ---- the host interface ------------------------------------------------
  // PCIe Gen5 x16: 32 GT/s x 128/130 x 16 lanes = 504.1 Gb/s raw.
  // TLP headers, ACKs and flow control take about a quarter.
  localparam int HOST_RAW_GBPS  = 504;
  localparam int TLP_EFF_PCT    = 75;
  localparam int HOST_USABLE_GBPS = (HOST_RAW_GBPS * TLP_EFF_PCT) / 100;  // 378

  // ---- descriptors -------------------------------------------------------
  // Chapter 18.2's ring. A fetch and a writeback per packet.
  localparam int DESC_OCTETS    = 16;
  localparam int DESC_PER_PKT   = 2;           // fetch + writeback

  // ---- connection context -------------------------------------------------
  // The state a NIC holds per connection: send and receive queue
  // pointers, a congestion-control context, a reassembly pointer.
  localparam int CTX_OCTETS     = 256;
  localparam int CTX_CACHE_ENTRIES = 16 * 1024;
  // A miss costs a fill AND an eviction writeback.
  localparam int CTX_MISS_OCTETS = CTX_OCTETS * 2;

  // ---- queues --------------------------------------------------------------
  localparam int QUEUES         = 2048;
  localparam int Q_STATE_BITS   = 512;

  // ---- the area unit, from Chapter 23.3 Section 2 --------------------------
  localparam int BCE_PER_FLOP   = 20;
  localparam int DATAPATH_BCE   = 283320;

  // ---- the arithmetic that decides the chapter ----------------------------
  function automatic int desc_gbps();
    // pps x 32 octets x 8 bits, in Gb/s
    return (PPS_MILLION * DESC_OCTETS * DESC_PER_PKT * 8) / 1000;
  endfunction

  function automatic int ctx_gbps_at_miss_pct(int miss_pct);
    return (PPS_MILLION * CTX_MISS_OCTETS * 8 * miss_pct) / 100000;
  endfunction

  function automatic int host_headroom_gbps();
    return HOST_USABLE_GBPS - RATE_GBPS - desc_gbps();
  endfunction

  // Section 12: the miss rate at which payload + descriptors + context
  // traffic exactly fills the host interface.
  function automatic int critical_miss_pct();
    automatic int full = (PPS_MILLION * CTX_MISS_OCTETS * 8) / 1000;
    if (full == 0) return 100;
    return (host_headroom_gbps() * 100) / full;
  endfunction

endpackage

Classification: a package whose central function returns a miss rate rather than a size.

What it teaches: that host_headroom_gbps() is what everything else in the NIC spends. After the payload's 100 Gb/s and the descriptors' 38, a Gen5 ×16 link has 239.9 Gb/s left, and that surplus is the budget for context fills, evictions, interrupt writes, doorbell reads and every other host transaction the NIC makes. A switch's headroom is measured in megabytes of buffer; a NIC's is measured in gigabits per second of a shared pipe.

And it teaches that CTX_MISS_OCTETS is twice CTX_OCTETS. A cache miss is not one transfer — it is a fill and, if the evicted line is dirty, a writeback, and a connection context is almost always dirty because the connection just advanced. That factor of two halves the sustainable miss rate, from 78.7% to 39.4%, and it is the difference between a cliff a workload can reach and one it cannot.

Deliberately simplified: TLP_EFF_PCT = 75 is a single number where real PCIe efficiency depends on payload size — a 64-octet write is far less efficient than a 512-octet one, and a NIC moving minimum frames gets worse efficiency exactly when it needs more. PPS_MILLION floors 148.81 to 148, a 0.5% error carried through every function. CTX_OCTETS = 256 is a plausible figure and a real context depends entirely on the transport. And DESC_PER_PKT = 2 ignores batching — Section 5's subject.

Production implication: the payload-size dependence of TLP_EFF_PCT is the simplification that most flatters the design, and it fails in the same direction as everything else in this chapter. A 64-octet payload write with a 24-octet TLP header is 73% efficient; a 512-octet write is 95%. So a NIC receiving minimum-size frames faces both the highest packet rate and the worst host-interface efficiency, and the two multiply. At 148.81 Mpps with 64-octet writes the effective usable bandwidth is nearer 368 Gb/s than 378, and the critical miss rate falls with it. Model the efficiency as a function of the transfer size, because the workload that stresses the packet rate is the same one that ruins the efficiency.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// host_bandwidth_model -- everything that crosses the host interface,
// added up, against what the link provides.
//
// The block exists so that "the NIC does not reach line rate" becomes a
// bandwidth statement with named contributors rather than a mystery.
// ---------------------------------------------------------------------
module host_bandwidth_model
  import dcnic_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] line_rate_gbps,
  input  logic [15:0] pps_million,
  input  logic [7:0]  ctx_miss_pct,
  input  logic [7:0]  desc_batch,        // descriptors per DMA transaction
  input  logic [15:0] interrupt_rate_khz,

  output logic [15:0] payload_gbps,
  output logic [15:0] descriptor_gbps,
  output logic [15:0] context_gbps,
  output logic [15:0] interrupt_gbps,
  output logic [15:0] total_gbps,
  output logic [15:0] headroom_gbps,
  output logic        fits,
  output logic [7:0]  critical_miss_pct_o,
  output logic [15:0] largest_contributor
);
  always_comb begin
    payload_gbps = line_rate_gbps;

    // Batching amortises the DMA transaction over several descriptors
    // but does NOT reduce the bytes moved. Section 5.
    descriptor_gbps = 16'((32'(pps_million) * DESC_OCTETS
                           * DESC_PER_PKT * 8) / 1000);

    context_gbps = 16'((32'(pps_million) * CTX_MISS_OCTETS * 8
                        * 32'(ctx_miss_pct)) / 100000);

    // An interrupt is a small write plus the host's read of the ring.
    interrupt_gbps = 16'((32'(interrupt_rate_khz) * 64 * 8) / 1_000_000);

    total_gbps   = payload_gbps + descriptor_gbps + context_gbps
                 + interrupt_gbps;
    headroom_gbps = (total_gbps < 16'(HOST_USABLE_GBPS))
                  ? (16'(HOST_USABLE_GBPS) - total_gbps) : 16'd0;
    fits = (total_gbps <= 16'(HOST_USABLE_GBPS));

    critical_miss_pct_o = 8'(critical_miss_pct());

    largest_contributor = (context_gbps > descriptor_gbps)
                        ? context_gbps : descriptor_gbps;
  end
endmodule

Classification: a bandwidth budget with four named consumers, of which only one carries data.

What it teaches: that desc_batch is an input the block deliberately ignores in descriptor_gbps. Batching descriptors — fetching four in one 64-octet transaction — amortises the per-transaction overhead and does not reduce the bytes. Chapter 18.2 §14's cache line holds four descriptors, so batching improves TLP efficiency and leaves the 38.1% figure alone. That distinction is the one most often got wrong when somebody proposes batching as a fix.

And it teaches that largest_contributor is usually context_gbps rather than descriptor_gbps. At the critical miss rate of 39.4%, context traffic is 239.9 Gb/s against the descriptors' 38.1 — six times larger — which means a NIC in trouble is almost always in trouble about cache, not about rings.

Deliberately simplified: interrupt_gbps models an interrupt as a 64-octet write, ignoring the host's subsequent ring reads which are usually larger. Nothing here models the direction of traffic, and PCIe's two directions are independent — a receive-heavy NIC saturates one and leaves the other idle. ctx_miss_pct is an input rather than derived from a working set, which Section 11 supplies. And fits is a steady-state statement that says nothing about a burst.

Production implication: the missing directionality is a real 2× and it is worth exploiting rather than merely noting. PCIe is full duplex: a receive path writes payload to the host and reads descriptors from it, so the two flows use opposite directions. Payload writes are 100 Gb/s upstream; descriptor fetches are 19 Gb/s downstream and writebacks 19 upstream — so the upstream direction carries 119 and the downstream 19. A model that adds them into one 138 figure over-constrains the design by nearly a factor of two on the lighter direction, and a design that sizes both directions from the sum buys lanes it will never use. Model the two directions separately; the critical miss rate on the busier one is what matters.


4. The Descriptors Cost 38% of the Payload

A network interface card at a hundred gigabits per second with minimum size frames handles one hundred and forty eight point eight one million packets per second. Each packet costs a descriptor fetch of sixteen octets and a descriptor writeback of sixteen, which is thirty two octets per packet of host interface traffic carrying no data at all. At that packet rate the descriptors are thirty eight point one gigabits per second, which is thirty eight point one per cent of the payload and twenty seven point six per cent of the total host demand. The overhead is per packet while the payload is per octet, so it collapses with frame size: at five hundred and twelve octets it is six point zero per cent, at fifteen hundred and eighteen it is two point one, and at nine thousand it is zero point three five, a factor of one hundred and nine across the range. The workload that produces minimum size frames, such as an all to all exchange or a remote procedure call storm, is exactly the one that produces the highest packet rate, so the descriptor overhead is maximal precisely when the card is busiest. Four things get proposed as fixes and only two work. Batching four descriptors into one transaction moves the same sixty four octets and improves transaction layer packet efficiency from forty to seventy two point seven per cent, changing nothing about the bytes. A smaller descriptor helps linearly. Eliminating the fetch entirely with a pre posted fixed size buffer pool halves the overhead at the cost of per packet flexibility. And larger frames help linearly in the packet rate.148.81 Mpps100 Gb/s, 64-octetframesFetch 16 octetsper packetWriteback 16octetsper packet38.1 Gb/s38.1% of the payload1 518 octets:2.1%a factor of 18Batching ×4same bytes, better TLPNo fetch at allhalves it — a fixedpool12
Figure 1 — bookkeeping that carries no data, and it scales with packets rather than octets.

A descriptor carries no data and it is 38% of the link's payload bandwidth. Derive it, and then check what does and does not reduce it.

The arithmetic.

per packet: one descriptor fetched (16 octets) + one written back (16) = 32 octets

at 148.81 Mpps: 148.81 × 10⁶ × 32 × 8 = 38.1 Gb/s

Value
payload100 Gb/s
descriptors38.1 Gb/s
descriptor overhead38.1% of the payload
as a fraction of the total27.6%

And it is worse than it looks, because the overhead is per packet and the payload is per octet.

Frame sizePackets/sPayloadDescriptorsOverhead
64 octets148.81 M100 Gb/s38.1 Gb/s38.1%
512 octets23.50 M100 Gb/s6.0 Gb/s6.0%
1 518 octets8.13 M100 Gb/s2.1 Gb/s2.1%
9 000 octets1.39 M100 Gb/s0.35 Gb/s0.35%

A factor of ninety-five between the extremes, and the workload that produces minimum frames — an all-to-all, an RPC storm, a storage metadata burst — is exactly the one that also produces the highest packet rate. The descriptor overhead is maximal precisely when the NIC is busiest.

Four things that are proposed as fixes, and what each actually does.

ProposalEffect on bytesEffect on efficiency
batch descriptors, 4 per 64-octet linenonebetter TLP efficiency
smaller descriptorslinearnone
descriptor-free receive (a fixed buffer pool)eliminates the fetchlarge
larger frameslinear in packet rate

Row one is the one that gets proposed and it does not reduce the 38.1%. Chapter 18.2 §14's cache line holds four 16-octet descriptors; fetching four in one transaction moves the same 64 octets and saves the per-transaction header. That is a real improvement in TLP efficiency and it leaves the byte count untouched.

Row three is the one that works and it changes the programming model. A receive path that draws from a pre-posted buffer pool rather than a per-packet descriptor eliminates the fetch entirely, halving the overhead to 19 Gb/s — at the cost of the flexibility a descriptor gives, since every buffer must then be the same size and the software must post them in advance.

Descriptors are the price of a flexible receive path, and at minimum frame size the price is 38% of the link.


5. RTL 2 — The Descriptor Rate Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// descriptor_rate_model -- Chapter 18.2's ring, at 148.81 million
// packets per second.
//
// The block's purpose is to separate two things that are constantly
// conflated: the BYTES a descriptor scheme moves, which batching does
// not change, and the TRANSACTIONS it makes, which batching does.
// ---------------------------------------------------------------------
module descriptor_rate_model
  import dcnic_pkg::*;
#(
  parameter int BATCH = 4          // descriptors per host transaction
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        pkt_arrives,
  input  logic [15:0] pps_million,
  input  logic [15:0] frame_octets,

  output logic [15:0] desc_octets_per_pkt,
  output logic [15:0] desc_gbps_o,
  output logic [15:0] desc_overhead_pct,
  output logic [31:0] transactions_per_second,
  output logic [15:0] tlp_efficiency_pct,
  output logic        batching_reduces_bytes,
  output logic [31:0] c_fetches,
  output logic [31:0] c_writebacks,
  output logic [31:0] c_prefetch_underruns
);
  logic [15:0] payload_gbps;

  always_comb begin
    desc_octets_per_pkt = 16'(DESC_OCTETS * DESC_PER_PKT);   // 32

    desc_gbps_o = 16'((32'(pps_million) * 32'(desc_octets_per_pkt) * 8)
                      / 1000);

    payload_gbps = 16'((32'(pps_million) * 32'(frame_octets) * 8) / 1000);
    desc_overhead_pct = (payload_gbps == 0) ? 16'd0
                      : (desc_gbps_o * 16'd100) / payload_gbps;

    // Batching changes THIS and not desc_gbps_o.
    transactions_per_second = (32'(pps_million) * 1_000_000 * DESC_PER_PKT)
                              / BATCH;

    // A 64-octet TLP carries 24 octets of header; a 256-octet one
    // carries the same 24.
    tlp_efficiency_pct = 16'((BATCH * DESC_OCTETS * 100)
                             / ((BATCH * DESC_OCTETS) + 24));

    // Always false, and it is the point.
    batching_reduces_bytes = 1'b0;
  end

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

Classification: a rate model whose most useful output is a hard-wired 1'b0.

What it teaches: that batching_reduces_bytes is false and tlp_efficiency_pct is what batching moves. Fetching four 16-octet descriptors in one transaction moves 64 octets instead of four lots of 16 — the same 64 — and pays one 24-octet TLP header instead of four. Efficiency goes from 40.0% to 72.7%; the descriptor bandwidth stays at 38.1 Gb/s. Proposing batching as a fix for the 38% is the commonest error in this area and the block exists to refuse it.

And it teaches that c_prefetch_underruns is the counter that matters and is never incremented here. Chapter 18.2 §3's prefetch store holds descriptors fetched ahead of need; an underrun means a packet arrived and no descriptor was ready, which at 6.72 ns per packet and a microsecond of host latency is a 149-packet hole. The model does not produce it because it has no latency; Section 7 supplies the missing half.

Deliberately simplified: BATCH is a compile-time constant where a real prefetcher batches opportunistically. tlp_efficiency_pct assumes a 24-octet header, which is right for a 64-bit-addressed memory write and wrong for other transaction types. desc_overhead_pct divides by payload rather than by the total, so it reads 38% rather than 27.6% — both are used in practice and the block should say which. And the two counters increment together, so a design that writes back in batches is not modelled.

Production implication: the prefetch store's depth is set by the same Little's-law argument as everything else at this boundary, and getting it wrong produces a failure that looks like a fabric problem. At 148.81 Mpps and 1 µs of host read latency the NIC needs 149 descriptors in flight to never underrun; Chapter 18.2 §3's store holds eight. A store sized for a 10 Gb/s part — where the requirement is 15 — underruns constantly at 100 Gb/s, and the symptom is receive drops at line rate with a host that is not busy and a ring that is not full. Size the prefetch store from rate × latency, and count the underruns.


6. At 400 Gb/s the Descriptors Break the Host Interface

Running the descriptor arithmetic forward through three line rates against three generations of host interface produces a wall that is not where people expect it. A hundred gigabit card needs a hundred gigabits of payload plus thirty eight point one of descriptors, which is one hundred and thirty eight point one in total. A two hundred gigabit card needs two hundred plus seventy six point two, which is two hundred and seventy six point two. A four hundred gigabit card needs four hundred plus one hundred and fifty two point four, which is five hundred and fifty two point four. An eight hundred gigabit card needs one thousand one hundred and four point eight. Peripheral component interconnect express generation four by sixteen provides two hundred and fifty two point one gigabits raw, which is one hundred and eighty nine usable at seventy five per cent transaction layer efficiency. Generation five by sixteen provides five hundred and four point one raw and three hundred and seventy eight usable. Generation six by sixteen provides one thousand and twenty four raw and seven hundred and sixty eight usable. So the hundred and two hundred gigabit parts fit generation five, the four hundred gigabit part misses it by forty six per cent, and the eight hundred gigabit part misses generation six. The four hundred gigabit case is the striking one because the payload alone, at four hundred gigabits, would fit within three hundred and seventy eight with nothing to spare only if the descriptors did not exist; it is the one hundred and fifty two gigabits of bookkeeping that breaks it. And even at maximum frame size the four hundred gigabit part needs four hundred and eight point three against three hundred and seventy eight, so it misses by eight per cent, which is why such parts are either generation six or generation five with a frame size caveat buried in a footnote.100 Gb/sneeds 138.1200 Gb/sneeds 276.2400 Gb/sneeds 552.4Gen5 ×16378 usableMisses by 46%152.4 of it isdescriptorsEven at 1 518octets408.3 — misses by 8%Gen6 ×16768 — fits with 216spare12
Figure 2 — the payload would fit; the bookkeeping is what does not.

Run Section 4's arithmetic forward through three generations of line rate and one of PCIe, and the result is a wall that is not where people expect it.

Line ratePackets/sPayloadDescriptorsTotal
100 Gb/s148.81 M10038.1138.1
200 Gb/s297.62 M20076.2276.2
400 Gb/s595.24 M400152.4552.4
800 Gb/s1 190.48 M800304.81 104.8

Against the host interface.

PCIeRaw, ×16At 75% TLP efficiency
Gen4252.1 Gb/s189
Gen5504.1 Gb/s378
Gen61 024.0 Gb/s768

Crossing the two tables.

Line rateNeedsGen4 ×16Gen5 ×16Gen6 ×16
100 Gb/s138.1noyesyes
200 Gb/s276.2noyesyes
400 Gb/s552.4nonoyes
800 Gb/s1 104.8nonono

Row three is the finding. A 400 Gb/s NIC needs 552.4 Gb/s of host bandwidth and a Gen5 ×16 link provides 378it misses by 46%and 152.4 of those gigabits are descriptors. Without the descriptor overhead the payload alone would fit with room to spare.

A 400 Gb/s NIC does not fit on PCIe Gen5 ×16 because of its descriptors, not because of its payload. The data needs 400 and the bookkeeping needs 152.

Three responses, and their costs.

ResponseWhat it doesCost
move to Gen6 ×16768 Gb/s — fits with 216 sparea generation, and its power
assume larger framesat 1 518 octets the descriptors are 2.1%assumes the workload
eliminate the descriptor fetchhalves the overhead to 76.2 Gb/sa fixed-size buffer pool
batch descriptorsnothing — Section 5

Row two is what datasheets do and it is worth being explicit about. A 400 Gb/s NIC's specification is usually written against large frames, where the packet rate is 32.8 Mpps and the descriptor overhead is 8.4 Gb/s — 2.1%. That configuration fits Gen5 ×16 comfortably. The minimum-frame configuration does not fit at all, and both are the same part.

At 400 Gb/s64-octet frames1 518-octet frames
packets/s595.24 M32.51 M
descriptors152.4 Gb/s8.3 Gb/s
total host bandwidth552.4408.3
Gen5 ×16 at 378misses by 46%misses by 8%

Even at maximum frame size a 400 Gb/s NIC is 8% over a Gen5 ×16 link, which is why 400 Gb/s parts are Gen5 ×16 with a caveat or Gen6, and why the caveat is usually a frame-size assumption buried in a footnote.


7. RTL 3 — The In-Flight Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// inflight_model -- Little's law at the host boundary, for the third
// time in the track.
//
// Chapter 19.6 Section 4 applied it to a memory interface and got 12 KiB
// at 100 Gb/s and 1 us. Chapter 21.8 Section 5 applied it to a throughput
// ceiling. Here it sets the depth of every prefetch store and every
// outstanding-request tracker on the part.
// ---------------------------------------------------------------------
module inflight_model
  import dcnic_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] pps_million,
  input  logic [15:0] host_latency_ns,
  input  logic [15:0] rate_gbps,
  input  logic [15:0] prefetch_depth,
  input  logic [15:0] outstanding_limit,

  output logic [15:0] descriptors_in_flight,
  output logic [31:0] payload_in_flight_bytes,
  output logic [15:0] prefetch_shortfall,
  output logic        underrun_possible,
  output logic [15:0] achievable_pps_million,
  output logic [15:0] achievable_gbps,
  output logic [31:0] c_underruns
);
  always_comb begin
    // Little's law: in flight = rate x latency.
    descriptors_in_flight = 16'((32'(pps_million) * 32'(host_latency_ns))
                                / 1000);

    payload_in_flight_bytes = (32'(rate_gbps) * 32'(host_latency_ns)) / 8;

    prefetch_shortfall = (descriptors_in_flight > prefetch_depth)
                       ? (descriptors_in_flight - prefetch_depth) : 16'd0;
    underrun_possible  = (prefetch_shortfall != 16'd0);

    // Chapter 21.8 Section 5's ceiling, at this boundary: if the design
    // cannot hold enough in flight, the rate it achieves is the count
    // divided by the latency -- and the latency is the HOST's.
    achievable_pps_million = (host_latency_ns == 0) ? pps_million
                           : 16'((32'(outstanding_limit) * 1000)
                                 / 32'(host_latency_ns));
    achievable_gbps = 16'((32'(achievable_pps_million) * 672) / 1000);
  end

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

Classification: Little's law, for the third time in the track, at a boundary the design does not own either end of.

What it teaches: that achievable_gbps is Chapter 21.8 §5's ceiling arriving at a new interface. A NIC that can hold outstanding_limit descriptor requests in flight across a host latency of host_latency_ns achieves outstanding / latency packets per second, whatever its line rate is. With 64 outstanding and 1 µs of latency that is 64 Mpps — 43 Gb/s of a 100 Gb/s port, and the port is healthy throughout.

And it teaches that payload_in_flight_bytes reproduces Chapter 19.6 §4 exactly. 100 Gb/s across 1 µs is 12.5 kB = 12.2 KiB, and that chapter's reorder buffer was 12 KiB. The same arithmetic, the same answer, a different block — which is the point of noticing that Little's law appears at every boundary where a rate meets a latency the design does not own.

Deliberately simplified: host_latency_ns is a constant where real host latency has a long tail — a TLB miss, a memory-controller conflict or a power-state transition can multiply it — and a design sized for the mean underruns on the tail. achievable_pps_million assumes every outstanding slot is productive, ignoring the ones spent on retries. And nothing here models the variance, which is what actually sizes a prefetch store: a store sized for the mean latency underruns half the time.

Production implication: the latency tail is what turns a correct Little's-law calculation into an underrunning design, and it is the host's property rather than the NIC's. A prefetch store sized for a 1 µs mean underruns whenever the host delivers 3 µs, which happens on a TLB miss or a contended memory controller; at 148.81 Mpps a 2 µs excursion is a 298-descriptor hole. The defence is to size the store against a high percentile rather than the mean — 298 or 450 descriptors rather than 149 — which is 4.8 or 7.2 kB of on-die store — 38 144 to 57 600 BCE, 0.13 to 0.20 of a MAC datapath. It is the cheapest possible insurance against a failure whose symptom is receive drops at line rate with no visible cause.


8. Little's Law at the Host Boundary, Again

The same relation has now produced a design constraint at four different interfaces in this track. This section collects them, because the pattern is more useful than any one instance.

in flight = rate × latency

ChapterThe boundaryRateLatencyWhat it sizes
Chapter 19.6 §4MAC to memory100 Gb/s1 µsa 12 KiB reorder buffer
Chapter 21.8 §5the same, read as a ceiling8 × 1 KiB1 µsa 65.5 Gb/s throughput bound
Chapter 19.6 §4outstanding bursts100 Gb/s1 µs13 bursts of 1 KiB, or 7 of 2 KiB
this chapter, Section 7NIC to host, descriptors148.81 Mpps1 µs149 descriptors in flight

Row four is the same calculation with packets as the unit rather than octets, and it produces a number that is easy to get wrong by an order of magnitude: Chapter 18.2 §3's prefetch store holds eight.

Line ratePackets/sDescriptors in flight at 1 µsChapter 18.2 §3's store
1 Gb/s1.49 M1.58 — ample
10 Gb/s14.88 M14.98 — marginal
100 Gb/s148.81 M148.88 — short by 19×
400 Gb/s595.24 M595.28 — short by 74×

A store sized for a gigabit part is short by nineteen times at a hundred, and the failure is not graceful: when the store is empty and a packet arrives, the packet is dropped, at line rate, with a host that is idle and a ring that is not full.

And the same relation gives the throughput ceiling when the store cannot be made large enough.

achievable packets/s = outstanding ÷ latency

OutstandingAt 1 µsAs a line rateOf a 100 Gb/s port
88 Mpps5.4 Gb/s5.4%
6464 Mpps43.0 Gb/s43.0%
149149 Mpps100.1 Gb/s100%
256256 Mpps172.0 Gb/scapped by the wire

Row one is Chapter 21.8's ceiling in its most extreme form — a 100 Gb/s port delivering 5.4% because of a prefetch store sized for a previous generation — and it is invisible to every counter on the link.

Every boundary in this track where a rate meets a latency the design does not own has produced the same calculation and the same failure: a structure sized for the previous generation, and a ceiling nobody computed.

One qualification that matters, and it is the one Section 7's production note raised. The latency in these tables is a mean, and the structure must be sized against a tail. A 3 µs excursion at 148.81 Mpps is a 446-descriptor hole, so the honest sizing is three times the mean's answer — and that is still only 7.2 kB of on-die store, 0.20 of a MAC receive datapath. The structure is cheap; the arithmetic is what gets skipped.


9. RTL 4 — The Connection Context Cache

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// context_cache -- the state a NIC holds per connection, and the
// reason a NIC's dominant structure is a CACHE where a switch's is a
// BUFFER.
//
// Chapter 23.3 Section 4 found a switch that is 91% packet buffer.
// Section 10 finds a NIC that is 66% connection context, and the
// difference is that a buffer holds what arrived while a cache holds
// what it guessed would be needed.
// ---------------------------------------------------------------------
module context_cache
  import dcnic_pkg::*;
#(
  parameter int ENTRIES = CTX_CACHE_ENTRIES,    // 16 384
  parameter int WAYS    = 8
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        lookup,
  input  logic [31:0] conn_id,
  input  logic [31:0] working_set,       // distinct connections in use

  output logic        hit,
  output logic        miss,
  output logic        evict_dirty,
  output logic [15:0] miss_pct,
  output logic [31:0] fill_octets,
  output logic [31:0] writeback_octets,
  output logic [15:0] miss_bandwidth_gbps,
  output logic        exceeds_capacity,
  output logic [31:0] c_lookups,
  output logic [31:0] c_misses,
  output logic [31:0] c_dirty_evictions
);
  // The model is a capacity model rather than a tag array: for a
  // working set W against a capacity C with a reasonably random access
  // pattern, the hit rate is about min(1, C/W). That approximation is
  // what makes Section 12's cliff computable.
  always_comb begin
    exceeds_capacity = (working_set > 32'(ENTRIES));

    miss_pct = exceeds_capacity
             ? 16'(((working_set - 32'(ENTRIES)) * 100) / working_set)
             : 16'd0;

    hit  = lookup && !exceeds_capacity;
    miss = lookup &&  exceeds_capacity;

    // A connection whose context was just used has almost certainly
    // been advanced, so the evicted line is dirty. That doubles the
    // cost of a miss and halves the sustainable miss rate.
    evict_dirty = miss;

    fill_octets      = miss ? 32'(CTX_OCTETS) : 32'd0;
    writeback_octets = evict_dirty ? 32'(CTX_OCTETS) : 32'd0;

    miss_bandwidth_gbps = 16'((32'(PPS_MILLION) * 32'(CTX_MISS_OCTETS)
                               * 8 * 32'(miss_pct)) / 100000);
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      c_lookups <= '0; c_misses <= '0; c_dirty_evictions <= '0;
    end else begin
      if (lookup)      c_lookups         <= c_lookups + 32'd1;
      if (miss)        c_misses          <= c_misses + 32'd1;
      if (evict_dirty) c_dirty_evictions <= c_dirty_evictions + 32'd1;
    end
  end
endmodule

Classification: a capacity model rather than a cache, because the chapter's subject is the cliff and not the tags.

What it teaches: that evict_dirty equals miss, and that equality is what makes the cliff reachable. A connection whose context was just fetched has been advanced — a sequence number moved, a window updated, a queue pointer incremented — so the line it evicts is dirty and must be written back. A miss therefore costs 512 octets rather than 256, and the sustainable miss rate halves from 78.7% to 39.4%.

And it teaches that exceeds_capacity is a step rather than a slope. While the working set fits, the miss rate is zero; the moment it does not, misses appear in proportion to the excess. Throughput is continuous in the NIC's parameters and discontinuous in the workload's, which is Section 20's refused class stated as a signal.

Deliberately simplified: the hit rate is min(1, C/W), which is right for a uniformly random access pattern and optimistic for a skewed one and pessimistic for a perfectly looping one. There are no tags, no ways and no replacement policyWAYS is declared and unused — so conflict misses are absent and only capacity misses are modelled. miss_pct is recomputed combinationally from a working_set input the NIC cannot actually observe. And the model has no warm-up, so a cold cache reads as a perfectly warm one.

Production implication: the NIC's inability to observe its own working set is what makes this failure hard to diagnose in the field, and the fix is a counter rather than a mechanism. A NIC knows its miss count and its lookup count and therefore its miss rate; what it does not know is why, and the two candidates — a working set that outgrew the cache, and a skewed access pattern causing conflict misses — have different remedies. Publishing both the miss rate and the number of distinct connection identifiers seen in a window separates them: a high miss rate with a large distinct count is capacity, and a high miss rate with a small one is conflict. A 4 096-entry approximate distinct counter is a few kilobits — under 0.02 of a MAC datapath — and it converts the NIC is slow into the working set is 32 768 and the cache is 16 384.


10. A NIC's Dominant State Is Context, Not Buffer

Accounting a network interface card in the bitcell equivalent unit gives a total of fifty point seven million, which is one hundred and seventy nine media access control receive datapaths and about eleven times smaller than the six point four terabit switch of the previous chapter. But the composition is completely different. The card's connection context cache of sixteen thousand three hundred and eighty four entries at two hundred and fifty six octets each is thirty three point five million bitcell equivalents, which is one hundred and eighteen datapaths and sixty six point two per cent of the part. Its packet buffer of two megabytes is sixteen million, fifty six point five datapaths and thirty one point six per cent. Queue state for two thousand and forty eight queues is one million, three point seven datapaths. The reorder buffer of twelve kibibytes and the descriptor prefetch store together are under half a datapath. The switch by contrast was ninety one per cent packet buffer. Per gigabit of line rate the card carries five hundred and seven thousand bitcell equivalents against the switch's eighty seven thousand nine hundred, so it is five point eight times more state hungry per gigabit despite being the smaller part. The difference matters because of what each dominant structure holds and how it fails. A packet buffer holds things that have definitely arrived, scales with line rate times congestion duration, and running out of it produces a drop that every counter names. A context cache holds what the design predicted would be needed, scales with the number of connections rather than the rate, and running out of it produces a host memory fetch that consumes the very bandwidth everything else is competing for. So a switch that is overloaded drops packets and says so, while a card that is overloaded slows down and says nothing, and the drops appear on somebody else's counters.A 100 Gb/s NIC5.07e7 BCE — 179datapathsContext cache66.2% — 118 datapathsPacket buffer 2MB31.6% — 56.5 datapathsThe switch91% packet bufferHolds apredictionscales withconnectionsMisses → a hostfetchspends the shared pipeFills → a dropcounted andtaxonomisedSet by thesoftwarenot by the network12
Figure 3 — a switch holds what arrived; a NIC holds what it guessed would be needed.

Chapter 23.3 §4 accounted a switch in BCE and found it 91% packet buffer. The same accounting on a NIC gives a different answer, and the difference is the chapter.

A 100 Gb/s NIC's on-die stateBitsBCE× a MAC datapathShare
connection contexts, 16k × 256 octets3.36 × 10⁷3.36 × 10⁷118.466.2%
packet buffer, 2 MB1.60 × 10⁷1.60 × 10⁷56.531.6%
queue state, 2 048 × 512 bits1.05 × 10⁶1.05 × 10⁶3.72.1%
reorder buffer, 12 KiB9.83 × 10⁴9.83 × 10⁴0.30.2%
descriptor prefetch, 450 × 16 octets5.76 × 10⁴5.76 × 10⁴0.20.1%
total5.07 × 10⁷179.0

Set against the switch.

Chapter 23.3's 6.4 Tb/s switchThis 100 Gb/s NIC
total state5.62 × 10⁸ BCE5.07 × 10⁷ BCE
× a MAC datapath1 985179
dominant structurepacket buffer, 91.0%connection context, 66.2%
what that structure holdswhat arrivedwhat it guessed would be needed
how it failsit fills, and dropsit misses, and stalls

Row four is the distinction that matters and it is not about size. A packet buffer holds things that have definitely arrived; running out of it produces a drop, which is Chapter 21.6's well-mapped territory with counters and a taxonomy. A context cache holds things the design predicted would be needed; running out of it produces a fetch across the host interface, which consumes the resource Section 3 showed everything else is already competing for.

A switch's dominant structure fails by filling. A NIC's fails by missing, and a miss does not drop a packet — it spends host bandwidth.

And the sizes scale differently with the thing they serve.

Scales withAt 4× the line rate
a switch's packet bufferthe line rate × the congestion duration4× — 256 MB
a NIC's context cachethe number of connections, not the rateunchanged
a NIC's descriptor prefetchrate × host latency4× — 1 786 descriptors

Row two is the useful one. A 400 Gb/s NIC serving the same application mix needs the same context cache as a 100 Gb/s one, because the working set is a property of the software. So the context cache's share of a faster NIC falls, and the descriptor and buffer structures grow around it — but the cliff does not move, because the cliff is set by the working set and the host bandwidth, both of which the NIC does not control.


11. RTL 5 — The Miss-Rate Collapse

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// context_miss_model -- where the cliff is, and what falls off it.
//
// Section 3's host interface has 239.9 Gb/s of headroom after payload
// and descriptors. A context miss costs 512 octets. At 148.81 million
// packets per second that headroom buys a miss rate of 39.4% and no
// more.
// ---------------------------------------------------------------------
module context_miss_model
  import dcnic_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] miss_pct,
  input  logic [15:0] line_rate_gbps,
  input  logic [15:0] pps_million,

  output logic [15:0] ctx_gbps,
  output logic [15:0] total_host_gbps,
  output logic [15:0] headroom_gbps,
  output logic [7:0]  critical_miss_pct_o,
  output logic        past_the_cliff,
  output logic [15:0] achieved_rate_gbps,
  output logic [15:0] achieved_pct,
  output logic [31:0] c_cliff_cycles
);
  logic [15:0] desc_gbps_l;
  logic [15:0] full_miss_gbps;

  always_comb begin
    desc_gbps_l = 16'((32'(pps_million) * DESC_OCTETS * DESC_PER_PKT * 8)
                      / 1000);

    full_miss_gbps = 16'((32'(pps_million) * CTX_MISS_OCTETS * 8) / 1000);

    ctx_gbps = 16'((32'(full_miss_gbps) * 32'(miss_pct)) / 100);

    total_host_gbps = line_rate_gbps + desc_gbps_l + ctx_gbps;

    headroom_gbps = (16'(HOST_USABLE_GBPS) > (line_rate_gbps + desc_gbps_l))
                  ? (16'(HOST_USABLE_GBPS) - line_rate_gbps - desc_gbps_l)
                  : 16'd0;

    critical_miss_pct_o = (full_miss_gbps == 0) ? 8'd100
                        : 8'((32'(headroom_gbps) * 100)
                             / 32'(full_miss_gbps));

    past_the_cliff = (total_host_gbps > 16'(HOST_USABLE_GBPS));

    // Past the cliff the NIC is host-bandwidth limited, and the rate it
    // achieves is what is left after the bookkeeping takes its share.
    achieved_rate_gbps = past_the_cliff
      ? ((16'(HOST_USABLE_GBPS) > (desc_gbps_l + ctx_gbps))
          ? (16'(HOST_USABLE_GBPS) - desc_gbps_l - ctx_gbps) : 16'd0)
      : line_rate_gbps;

    achieved_pct = (line_rate_gbps == 0) ? 16'd0
                 : (achieved_rate_gbps * 16'd100) / line_rate_gbps;
  end

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

Classification: a bandwidth budget with a threshold, and the threshold is the chapter's headline number.

What it teaches: that achieved_rate_gbps falls linearly past the cliff while miss_pct rises, which makes the collapse steep rather than gradual. At a 39.4% miss rate the NIC delivers 99.7 Gb/s; at 50% it delivers 35.1; at 55.7% it delivers nothing at all — the descriptors and context fills have taken the entire host interface. The whole collapse is sixteen percentage points wide, and the NIC is not broken at any point on it.

And it teaches that past_the_cliff is the only Boolean in the chapter that a design can act on. Everything else — miss rates, working sets, bandwidths — is a continuous quantity that a control loop could respond to. Crossing the cliff is a state, and the right response is not to tune but to shed: reduce the number of active connections, or accept a lower rate deliberately rather than by collapse.

Deliberately simplified: the model assumes host bandwidth is shared fairly between payload, descriptors and context fills, where a real interface arbitrates by transaction and a burst of context fills can starve the payload entirely. achieved_rate_gbps subtracts the full context traffic even past the cliff, where in reality fewer packets are processed so fewer misses occur — the model overstates the collapse's steepness and gets its location right. And nothing models the queueing delay a saturated host interface adds, which is what the application actually observes.

Production implication: the model's assumption of fair arbitration is the one that matters for the failure's shape, and reality is worse. PCIe does not arbitrate between a NIC's own transaction types — the NIC does, in its own request scheduler — and a naive scheduler that issues context fills as they are needed will starve payload writes, because a fill blocks a packet and a payload write does not. The result is that crossing the cliff produces a receive-buffer overflow rather than a graceful rate reduction, and the symptom is drops rather than slowness. Give payload writes priority over context fills and accept the stalls, so that the failure is a rate reduction the application can observe rather than a drop it cannot.


12. Thirty-Nine Per Cent, and Why Chapter 23.2's Remedy Breaks It

The previous chapter measured five remedies for a ring all reduce running at twenty eight point four per cent of its uplink group, and ranked striping each ring link into thirty two connections at ninety three point eight per cent fabric efficiency, the best striping result available. This chapter's arithmetic says the network interface card cannot deliver it. After payload at a hundred gigabits and descriptors at thirty eight point one, a generation five by sixteen host interface has two hundred and thirty nine point nine gigabits of headroom. A context cache miss costs five hundred and twelve octets, because it is a fill plus a dirty writeback, and a context that was just used has just been advanced so the evicted line is always dirty. At one hundred and forty eight point eight one million packets per second a hundred per cent miss rate would be six hundred and nine point five gigabits, so the sustainable miss rate is thirty nine point four per cent. Striping by thirty two on a one thousand and twenty four participant job creates thirty two thousand seven hundred and sixty eight connections against a sixteen thousand three hundred and eighty four entry cache, which is a fifty per cent miss rate, past the cliff, and the card delivers thirty five point one gigabits instead of a hundred. Striping by eight creates eight thousand one hundred and ninety two connections, fits the cache with room, and delivers seventy seven point one per cent fabric efficiency at full card rate. Striping by sixteen exactly fills the cache with no margin. The rule that finds this is stripes less than or equal to context cache entries divided by participants: one division, belonging in the collective library's configuration, currently performed by nobody, because the fabric's efficiency curve and the card's datasheet each lack the other's term.Stripes ×88 192 conns — fitsStripes ×1616 384 — exactly fullStripes ×3232 768 — twice thecache50% miss ratecliff is at 39.4%Fabric says 93.8%23.2 §12's beststripingNIC delivers 35.1of 100 Gb/sstripes ≤ entries÷ participantsone division12
Figure 4 — the fabric's best configuration and the NIC's are not the same configuration.

Chapter 23.2 §11's production note warned that striping past the NIC's context cache produces a collapse that looks like the fabric problem it was deployed to fix. This section derives it.

The cliff, from Section 11.

Value
usable host bandwidth, Gen5 ×16 at 75%378 Gb/s
payload at line rate100 Gb/s
descriptors at 148.81 Mpps38.1 Gb/s
headroom for context traffic239.9 Gb/s
a miss costs a fill plus a dirty writeback512 octets
context traffic at a 100% miss rate609.5 Gb/s
sustainable miss rate39.4%

And the working set, from Chapter 23.2.

ConfigurationConnectionsAgainst a 16 384-entry cacheMiss rate
one connection per ring link1 024fits with room0%
striped ×44 096fits0%
striped ×88 192fits0%
striped ×1616 384exactly full0%, and no margin
striped ×3232 768twice the cache50.0%

Row five is past the cliff. Chapter 23.2 §12 measured striping ×32 as taking a ring all-reduce from 29.6 Gb/s to 93.8the best fabric-side result in that chapterand this chapter's arithmetic says the NIC cannot deliver it, because a 50% context miss rate needs 304.8 Gb/s of host bandwidth against 239.9 available.

Crossing the two chapters.

StripesChapter 23.2 §12's ring rateConnectionsMiss rateNIC delivers
129.6 Gb/s1 0240%100 Gb/s — no limit
877.1 Gb/s8 1920%100 Gb/s — no limit
1687.9 Gb/s16 3840%100 Gb/s — exactly at the edge
3293.8 Gb/s32 76850.0%35.1 Gb/s — collapse

So the optimum is at ×16 and not ×32, and the reason is in a different chapter, in a different part, measured in a different unit. Striping ×8 delivers 77.1 Gb/s comfortably; ×16 delivers 87.9 with no margin; ×32 delivers 93.8 on paper and 35.1 in practice.

The fabric's best configuration and the NIC's are not the same configuration, and neither part can see the other's constraint.

Which gives the practical rule this chapter exists to state.

stripes ≤ context cache entries ÷ participants

Cache1 024 participants4 096 participants
4 096 entries≤ 4 stripes≤ 1
16 384 entries≤ 16≤ 4
65 536 entries≤ 64≤ 16

And the rule has a consequence for Chapter 23.2's ranking that is worth stating plainly. That chapter ranked five remedies and put load-aware path selection and hierarchical reduction at 100 Gb/s, striping ×8 at 77.1. Striping's ceiling is not the efficiency curve — it is the NIC's context cache, and the two remedies that reach 100 Gb/s do not create connections at all. Hierarchical reduction sends one flow per leaf; load-aware selection places the flows that already existed. Neither touches the cache.

RemedyFabric rateConnections createdNIC-side risk
hierarchical reduction100 Gb/sfewer — one per leafnone
load-aware selection100 Gb/snonenone
striping ×877.1 Gb/snone at 1 024 participants
striping ×3293.8 Gb/s on paper32×collapse

Rows one and two were already ranked first in Chapter 23.2 §12 on fabric grounds. This chapter adds a second, independent reason to prefer them, and it is the stronger one: they cost nothing at the host.


13. RTL 6 — The Queue Set and Interrupt Moderation

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// queue_set_model -- 2048 queues, one host, and the arithmetic of not
// interrupting it to death.
//
// Chapter 18.7 Section 11 built a queue set and Section 9 its indirection
// table. This block is what happens to both at 148.81 million packets
// per second, where an interrupt per packet is not merely wasteful --
// it is impossible.
// ---------------------------------------------------------------------
module queue_set_model
  import dcnic_pkg::*;
#(
  parameter int Q = QUEUES              // 2048
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        pkt_arrives,
  input  logic [10:0] pkt_queue,
  input  logic [15:0] moderation_us,
  input  logic [15:0] moderation_frames,
  input  logic [15:0] pps_million,

  output logic        interrupt,
  output logic [31:0] interrupts_per_second,
  output logic [15:0] packets_per_interrupt,
  output logic [31:0] added_latency_ns,
  output logic [15:0] host_cycles_per_interrupt,
  output logic [15:0] host_cores_needed_x10,
  output logic [31:0] c_interrupts,
  output logic [31:0] c_packets
);
  logic [15:0] pending;
  logic [31:0] timer_ns;

  always_comb begin
    // Two conditions, whichever fires first: a frame count or a timer.
    interrupt = (pending >= moderation_frames) ||
                ((timer_ns >= (32'(moderation_us) * 1000)) &&
                 (pending != 16'd0));

    interrupts_per_second = (moderation_us == 0) ? 32'hFFFF_FFFF
                          : (1_000_000 / 32'(moderation_us));

    packets_per_interrupt = (interrupts_per_second == 0) ? 16'd0
                          : 16'((32'(pps_million) * 1_000_000)
                                / interrupts_per_second);

    // The cost of moderation: a packet may wait a whole window.
    added_latency_ns = 32'(moderation_us) * 1000;

    // An interrupt costs the host an entry, a ring walk and a return.
    host_cycles_per_interrupt = 16'd2000;

    // At 3 GHz, what fraction of a core the interrupts alone consume.
    host_cores_needed_x10 = 16'((interrupts_per_second
                                 * 32'(host_cycles_per_interrupt) * 10)
                                / 3_000_000_000);
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      pending <= '0; timer_ns <= '0; c_interrupts <= '0; c_packets <= '0;
    end else begin
      if (pkt_arrives) begin
        pending  <= pending + 16'd1;
        c_packets <= c_packets + 32'd1;
      end
      timer_ns <= timer_ns + 32'd1;
      if (interrupt) begin
        pending      <= '0;
        timer_ns     <= '0;
        c_interrupts <= c_interrupts + 32'd1;
      end
    end
  end
endmodule

Classification: a moderation timer, and its outputs are a latency and a fraction of a CPU core.

What it teaches: that an interrupt per packet is arithmetically impossible at these rates. At 148.81 Mpps and 2 000 host cycles per interrupt, an unmoderated NIC would demand 2.98 × 10¹¹ cycles per second — ninety-nine 3 GHz cores doing nothing but entering and leaving interrupt handlers. Moderation is not an optimisation; it is the only way the arrangement works at all.

And it teaches the trade in one pair of numbers. A 50 µs moderation window costs 50 µs of added latency and reduces the interrupt rate to 20 000 per second — 0.013 of a core. A 5 µs window costs 5 µs and needs 0.13 of a core. The latency added is exactly the window, and the CPU saved is inversely proportional to it.

Deliberately simplified: host_cycles_per_interrupt = 2000 is a single constant covering entry, the ring walk, the packet's delivery and the return — it varies by more than an order of magnitude with the packets per interrupt. The timer increments once per clock as if the clock were 1 GHz. pending counts packets across all 2 048 queues where moderation is per queue, which is the block's largest omission: 2 048 queues each moderated at 50 µs produce 40.96 million interrupts per second, not 20 000.

Production implication: that per-queue omission inverts the block's conclusion and is the reason multi-queue NICs moderate the way they do. Moderation is per queue, so the interrupt rate is queues ÷ window — at 2 048 queues and a 50 µs window that is 40.96 M/s, which is 27 cores of pure interrupt overhead. Real designs avoid it in three ways: only queues with traffic generate interrupts (so the rate follows the active queue count, not the configured one); a single queue's window is adaptive, lengthening under load; and busy-polling drivers disable interrupts entirely above a rate threshold. The last is the one that matters at 100 Gb/s — a polled receive path has no interrupts at all — and it means the moderation arithmetic above describes the idle and lightly loaded regime, which is where latency matters and throughput does not.


14. What a NIC Must Never Do

Five prohibitions, each derived earlier.

One — never quote a line rate without a frame size.

At 100 Gb/s64 octets1 518 octets
packets/s148.81 M8.13 M
descriptor bandwidth38.1 Gb/s2.1 Gb/s
host bandwidth needed138.1102.1
overhead38.1%2.1%

A factor of eighteen in the overhead between two configurations of the same part.

Two — never propose batching as a fix for descriptor bandwidth.

Section 5: batching four 16-octet descriptors into one transaction moves the same 64 octets. It improves TLP efficiency from 40.0% to 72.7% and leaves the 38.1 Gb/s untouched. What reduces the bytes is a smaller descriptor or no descriptor at all.

Three — never size a prefetch store from the mean host latency.

Descriptors in flight needed
1 µs mean149
2 µs excursion298
3 µs tail446

A store sized for the mean underruns on the tail, and the symptom is receive drops at line rate with an idle host and a non-full ring — which looks like a fabric problem and is not.

Four — never let a context miss take priority over a payload write.

Section 11's production note. A context fill blocks a packet and a payload write does not, so a naive request scheduler issues fills first and starves the payload — turning a rate reduction into a receive-buffer overflow. Give payload writes priority and accept the stalls, so that crossing the cliff is observable as slowness rather than as loss.

Five — never let a fabric-side optimisation choose the connection count without checking the cache.

StripesFabric rateConnectionsNIC delivers
877.1 Gb/s8 192100 Gb/s
1687.9 Gb/s16 384100 Gb/s, no margin
3293.8 Gb/s32 76835.1 Gb/s

Section 12's rule: stripes ≤ context cache entries ÷ participants. It is one division, it belongs in the collective library's configuration, and nobody currently performs it.


15. RTL 7 — NIC Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// nic_telemetry -- what the part knows about itself, and the two fields
// that separate the chapter's three failure modes.
//
// Those two are the context miss rate and the distinct-connection
// count. Together they say whether a slow NIC is short of cache, short
// of prefetch, or short of host bandwidth -- and no other pair does.
// ---------------------------------------------------------------------
module nic_telemetry
  import dcnic_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        snapshot,

  input  logic [31:0] i_packets,
  input  logic [31:0] i_ctx_lookups,
  input  logic [31:0] i_ctx_misses,
  input  logic [31:0] i_distinct_conns,
  input  logic [31:0] i_prefetch_underruns,
  input  logic [31:0] i_desc_fetches,
  input  logic [15:0] i_host_gbps_used,
  input  logic [15:0] i_achieved_gbps,
  input  logic [15:0] i_line_rate_gbps,
  input  logic [31:0] i_interrupts,

  output logic [31:0] o_packets,
  output logic [15:0] o_ctx_miss_pct,
  output logic [31:0] o_distinct_conns,
  output logic [15:0] o_cache_pressure_x10,
  output logic [15:0] o_underrun_ppm,
  output logic [15:0] o_host_utilisation_pct,
  output logic [15:0] o_achieved_pct,
  output logic [31:0] o_packets_per_interrupt,
  output logic [2:0]  o_limiter,
  output logic [2:0]  o_caveats
);
  localparam int LIM_NONE     = 3'd0;
  localparam int LIM_CACHE    = 3'd1;
  localparam int LIM_PREFETCH = 3'd2;
  localparam int LIM_HOST     = 3'd3;

  localparam int C_PAST_CLIFF = 0;
  localparam int C_UNDERRUN   = 1;
  localparam int C_NO_DISTINCT= 2;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      o_packets <= '0; o_ctx_miss_pct <= '0; o_distinct_conns <= '0;
      o_cache_pressure_x10 <= '0; o_underrun_ppm <= '0;
      o_host_utilisation_pct <= '0; o_achieved_pct <= '0;
      o_packets_per_interrupt <= '0; o_limiter <= 3'd0; o_caveats <= '0;
    end else if (snapshot) begin
      o_packets        <= i_packets;
      o_distinct_conns <= i_distinct_conns;

      if (i_ctx_lookups != 0)
        o_ctx_miss_pct <= 16'((i_ctx_misses * 100) / i_ctx_lookups);
      if (i_packets != 0)
        o_underrun_ppm <= 16'((i_prefetch_underruns * 1_000_000)
                              / i_packets);
      if (i_interrupts != 0)
        o_packets_per_interrupt <= i_packets / i_interrupts;

      // The field that distinguishes a capacity miss from a conflict
      // miss: how many distinct connections the NIC actually saw.
      o_cache_pressure_x10 <= 16'((i_distinct_conns * 10)
                                  / 32'(CTX_CACHE_ENTRIES));

      o_host_utilisation_pct <= 16'((32'(i_host_gbps_used) * 100)
                                    / 32'(HOST_USABLE_GBPS));
      if (i_line_rate_gbps != 0)
        o_achieved_pct <= (i_achieved_gbps * 16'd100) / i_line_rate_gbps;

      // The single most useful output: which of three things is
      // limiting the part.
      if (i_achieved_gbps >= i_line_rate_gbps)          o_limiter <= 3'(LIM_NONE);
      else if (o_cache_pressure_x10 > 16'd10)           o_limiter <= 3'(LIM_CACHE);
      else if (i_prefetch_underruns != 0)               o_limiter <= 3'(LIM_PREFETCH);
      else                                              o_limiter <= 3'(LIM_HOST);

      o_caveats[C_PAST_CLIFF]  <= (o_ctx_miss_pct > 16'd39);
      o_caveats[C_UNDERRUN]    <= (i_prefetch_underruns != 0);
      o_caveats[C_NO_DISTINCT] <= (i_distinct_conns == 0);
    end
  end
endmodule

Classification: a snapshot bank whose last useful output names which of three constraints is binding.

What it teaches: that o_cache_pressure_x10 is the field that makes the miss rate actionable. A high miss rate has two causes with different remedies: a working set larger than the cache (reduce the connection count — Section 12's rule) and a skewed access pattern producing conflict misses (change the hash or the associativity). The distinct-connection count separates them in one comparison, and without it the miss rate alone is a symptom with two candidate diseases.

And it teaches that o_limiter is a three-way verdict the part can reach and an operator cannot. From outside, all three failures look identical: the NIC does not reach line rate. From inside, cache pressure above 1.0, a non-zero underrun count and a saturated host interface are three distinct, mutually exclusive observations — and reporting which one holds turns an investigation into a lookup.

Deliberately simplified: o_limiter is a priority ladder rather than a measurement, so two simultaneous constraints report only the first. i_distinct_conns requires an approximate distinct counter the block does not contain. No read-clear, and i_packets at 148.81 Mpps wraps a 32-bit counter in 28.9 seconds — worse than Chapter 23.3 §15's switch, because a NIC's counters are per port rather than aggregated. And o_host_utilisation_pct uses a nominal 378 Gb/s rather than a measured one.

Production implication: the 28.9-second wrap is shorter than most telemetry polling intervals and it makes the whole block's packet-derived fields useless if ignored. A 32-bit packet counter at 148.81 Mpps wraps in 28.9 seconds; a system polling every 30 seconds reads a wrapped value more often than not, and every rate it computes is wrong by an unknown multiple of 2³². A 48-bit counter wraps in 21.9 days and a 64-bit one in 3 900 years. Widening the fifty or so packet-derived counters on a NIC from 32 to 48 bits costs 800 flops — 16 000 BCE, 0.06 of a MAC receive datapath, 0.03% of the part. There is no defensible reason not to.


16. RTL 8 — The NIC Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// nic_conformance_monitor -- properties for a fixed-function NIC.
//
// Properties 1 to 10 hold at every point on Section 11's collapse
// curve, including the point where the part delivers 35 Gb/s of a
// 100 Gb/s port. Properties 11 and 12 are the two that do not.
// ---------------------------------------------------------------------
module nic_conformance_monitor
  import dcnic_pkg::*;
(
  input logic        clk,
  input logic        rst_n,

  input logic [15:0] payload_gbps,
  input logic [15:0] descriptor_gbps,
  input logic [15:0] context_gbps,
  input logic [15:0] total_gbps,
  input logic        fits,
  input logic        past_the_cliff,
  input logic [15:0] achieved_gbps,
  input logic [15:0] line_rate_gbps,
  input logic [15:0] miss_pct,
  input logic [7:0]  critical_miss_pct_i,
  input logic [31:0] working_set,
  input logic        exceeds_capacity,
  input logic        batching_reduces_bytes,
  input logic [15:0] descriptors_in_flight,
  input logic [15:0] prefetch_depth,
  input logic        underrun_possible,
  input logic        interrupt,
  input logic [15:0] pending_frames,
  input logic [15:0] moderation_frames,
  input logic        claim_line_rate
);
  // 1. The host budget is the sum of its named parts.
  p_nc_budget: assert property (@(posedge clk) disable iff (!rst_n)
    total_gbps == (payload_gbps + descriptor_gbps + context_gbps));

  // 2. Fitting means the total is within the link.
  p_nc_fits: assert property (@(posedge clk) disable iff (!rst_n)
    fits == (total_gbps <= 16'(HOST_USABLE_GBPS)));

  // 3. Past the cliff is the negation of fitting.
  p_nc_cliff: assert property (@(posedge clk) disable iff (!rst_n)
    past_the_cliff == !fits);

  // 4. Batching never reduces descriptor bytes.
  p_nc_batching: assert property (@(posedge clk) disable iff (!rst_n)
    !batching_reduces_bytes);

  // 5. A miss occurs only when the working set exceeds the cache.
  p_nc_miss_cause: assert property (@(posedge clk) disable iff (!rst_n)
    (miss_pct != 0) |-> exceeds_capacity);

  // 6. The critical miss rate is headroom over full-miss traffic.
  p_nc_critical: assert property (@(posedge clk) disable iff (!rst_n)
    (miss_pct <= 16'(critical_miss_pct_i)) |-> fits);

  // 7. In-flight descriptors follow Little's law.
  p_nc_little: assert property (@(posedge clk) disable iff (!rst_n)
    underrun_possible == (descriptors_in_flight > prefetch_depth));

  // 8. An interrupt fires only with packets pending.
  p_nc_int_pending: assert property (@(posedge clk) disable iff (!rst_n)
    interrupt |-> (pending_frames != 16'd0));

  // 9. Moderation is honoured: no interrupt below the frame threshold
  //    unless the timer expired.
  p_nc_moderation: assert property (@(posedge clk) disable iff (!rst_n)
    (interrupt && (pending_frames < moderation_frames)) |-> timer_expired);

  // 10. Achieved never exceeds the line rate.
  p_nc_achieved_bounded: assert property (@(posedge clk) disable iff (!rst_n)
    achieved_gbps <= line_rate_gbps);

  // 11. A LINE-RATE CLAIM requires the working set to fit. This is the
  //     property Section 20 is about.
  p_nc_claim_needs_fit: assert property (@(posedge clk) disable iff (!rst_n)
    claim_line_rate |-> !exceeds_capacity);

  // 12. And it requires the host budget to close.
  p_nc_claim_needs_budget: assert property (@(posedge clk) disable iff (!rst_n)
    claim_line_rate |-> fits);
endmodule

Classification: twelve properties, ten about mechanism and two that gate a performance claim on a workload property.

What it teaches: that properties 11 and 12 constrain a claim using a quantity the NIC does not control. exceeds_capacity is a function of the working set, which belongs to the software; fits is a function of the host interface, which belongs to the platform. A line-rate claim is therefore conditional on two things outside the part, and the honest specification says so rather than quoting a number.

And it teaches that properties 1 to 10 hold at 35 Gb/s. The budget sums correctly, the cliff is detected, batching is honest, misses are caused by capacity, Little's law holds, moderation is honoured and the achieved rate is bounded. Every mechanism in the part is working exactly as designed while it delivers a third of its line rate, which is Section 20's subject.

Deliberately simplified: property 9 references a timer_expired signal the port list does not declare. Property 5's implication is one-directional — it catches a miss without a cause and not a cause without a miss. Property 6 is a restatement of the model rather than an independent check. And property 10's bound is trivially true by construction in the model, where in a real part an achieved rate above the line rate would indicate a measurement error worth catching.

Production implication: property 11's shape is the one to carry into a datasheet, and it inverts how NIC performance is usually specified. A specification that says 100 Gb/s line rate is making an unconditional claim that the part cannot support; one that says 100 Gb/s line rate for working sets up to 16 384 connections on a host interface providing 378 Gb/s is making a claim that is true. The two extra clauses are the entire content of this chapter, they are both measurable, and a customer who reads them can check their own workload against them before buying.


17. A NIC Against a Switch, in BCE

Chapter 23.3 §2 defined the unit so that two parts with different dominant structures could be compared. This is the comparison.

A 6.4 Tb/s switchA 100 Gb/s NICRatio
total on-die state5.62 × 10⁸ BCE5.07 × 10⁷ BCE11.1×
× a MAC receive datapath1 98517911.1×
aggregate line rate6 400 Gb/s100 Gb/s64×
BCE per Gb/s of line rate87 900507 0000.17×

Row four is the one that is not obvious. Per gigabit of line rate a NIC carries 5.8 times more on-die state than a switch does507 000 BCE per Gb/s against 87 900 — which inverts the intuition that a switch, being the larger part, must be the more state-hungry one.

The reason is in what each structure is for.

The switch's dominant structureThe NIC's
what it isa 64 MB packet buffera 16k-entry context cache
share of the part91.0%66.2%
scales withline rate × congestion durationthe number of connections
what it holdswhat arrivedwhat it predicted would be needed
running out producesa dropa host memory fetch
whose property is the driverthe network'sthe software's

Row six is the deepest difference. A switch's buffer requirement comes from traffic — a quantity the network produces and a network engineer can reason about. A NIC's context requirement comes from the application's connection count, which is chosen by software written by somebody who has never seen the NIC. Chapter 23.2 §12's striping decision is exactly that: a collective library's configuration parameter that sets a NIC's cache pressure, with neither side aware of the other.

And the two parts' failure modes follow.

SwitchNIC
overload symptomdrops, counted, taxonomised in Chapter 21.2a rate collapse with no drops at all
detectable byevery counter in Chapter 19.7a miss rate, if anyone exports it
how the operator sees itpacket loss"the NIC is slow"
where the remedy livesbuffer, or less trafficthe application's connection count

Row one is worth sitting with. A NIC past Section 11's cliff drops nothing. It processes fewer packets per second because each one waits for a context fill, so the receive path applies backpressure, the link's flow control engages or the switch upstream buffers, and the loss — if any — happens somewhere else entirely. Chapter 21.6's method looks for the drop at the point of loss; here the point of loss is a switch, and the cause is a cache two hops away.

A switch that is overloaded drops packets and says so. A NIC that is overloaded slows down and says nothing, and the drops appear on somebody else's counters.

One closing comparison, to place both against the rest of the track.

BCE× a MAC datapath
Chapter 22.1's single-pair PHY front end4.65 × 10⁴0.16
Chapter 19.7 §19's MAC receive datapath2.83 × 10⁵1.00
Chapter 23.3's switch pipeline logic2.37 × 10⁵0.84
this chapter's NIC5.07 × 10⁷179
Chapter 23.3's whole switch5.62 × 10⁸1 985

Rows two, three and four span four orders of magnitude and the unit handles all of them, which is what Chapter 23.3 §2 was for.


18. What the NIC Assumes

Seven premises. Four are about the host and three about the software, and the part owns none of them.

AssumptionIf it is false
the host interface provides 378 Gb/sSection 6 — a 400 Gb/s part needs 552
host read latency is about 1 µsSection 7 — a 3 µs tail is a 446-descriptor hole
TLP efficiency is 75%Section 3 — a 64-octet write is 73%, and that is the busy case
the two PCIe directions are independentthey are, and a model that adds them over-constrains by 2×
the working set fits the context cacheSection 12 — 50% miss at twice the capacity
the frame size is not minimumSection 4 — an eighteenfold difference in overhead
the driver posts buffers fast enoughthe ring empties and the NIC drops with an idle host

Rows five and six are the two that decide whether a part reaches its number, and both belong to software.

Row five's specific shape is worth restating because it is a cliff. The assumption is not the working set is small; it is the working set is under 16 384, and the difference between 16 000 and 20 000 connections is the difference between 100 Gb/s and 100 Gb/s — but the difference between 16 384 and 32 768 is 100 Gb/s and 35.1. The assumption has no gradient, which is Section 20's subject.

Row six is the one datasheets handle by omission. A 400 Gb/s NIC's specification is written against a frame-size distribution, usually IMIX or maximum-size, and the minimum-frame configuration of the same part does not fit its host interface at all — Section 6's table. The assumption is stated in a footnote if it is stated at all.

And one premise that is not on the list because the part cannot even express it.

That the connection count the software chooses has anything to do with the cache the hardware provides. Chapter 23.2 §12's striping parameter is chosen to optimise a fabric efficiency curve, by a collective library, with no input from the NIC and no interface through which the NIC could supply one. Section 12's rule — stripes ≤ cache entries ÷ participantsis one division that nobody currently performs, because the two quantities live in different products.


19. The Cost, Accounted — in BCE

Chapter 23.3 §2's unit, applied to this chapter's blocks and to the part around them.

BlockFlopsSRAM bitsBCE× a MAC datapath
host_bandwidth_model0 — combinational00
descriptor_rate_model9601 9200.007
inflight_model3206400.002
context_cache control9601 9200.007
the cache it manages03.36 × 10⁷3.36 × 10⁷118.4
context_miss_model3206400.002
queue_set_model4809600.003
the queue state it manages01.05 × 10⁶1.05 × 10⁶3.7
nic_telemetry26005 2000.018
nic_conformance_monitor0 — assertions00
all control logic56411 2800.040
all memory5.07 × 10⁷5.07 × 10⁷179.0

The ratio of memory to logic is 4 495 : 1, which is nearly twice Chapter 23.3 §19's switch figure of 2 371. A NIC is even more memory-dominated than a switch, relative to the logic that manages it — because the cache's control logic is a few hundred flops and the cache is thirty-three megabits.

Which makes the instrumentation argument even more one-sided than it was in Chapter 23.3.

MechanismBCE× a MAC datapath× this NIC
this chapter's telemetry block5 2000.0180.010%
a 4 096-entry distinct-connection counter≈ 4 0960.0140.008%
widening 50 counters 32 → 48 bits16 0000.0560.032%
all three together25 2960.0890.050%

Five hundredths of one per cent of the part, and between them they supply the miss rate, the distinct-connection count, the underrun count and rates that do not wrap in 28.9 seconds — which is everything Section 22's debugging procedure needs.

And the comparison that closes the chapter's accounting.

Value
context cache3.36 × 10⁷ BCE — 66.2% of the part
doubling it to 32 768 entries+3.36 × 10⁷ BCE — a 66% larger part
what doubling it buys at 32 768 connections0% miss instead of 50% — 100 Gb/s instead of 35.1
what Section 12's rule buys insteadthe same, for one division in a library

Row two against row four is the chapter's practical conclusion. The hardware fix for Chapter 23.2's striping collapse is a 66% larger NIC; the software fix is a division. Chapter 23.2 §19 reached the same shape — 512 flops against 3.5× the fabric — and here the ratio is starker still, because the cheap side costs nothing at all.


20. Properties Worth Asserting, and One Worth Refusing

Six groups. All thirty-four hold at a 50% context miss rate, where the part delivers 35.1 Gb/s of a 100 Gb/s port.

Group A — the host budget.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A1. The total is the sum of its named parts.
p_hb_sum: assert property (@(posedge clk) disable iff (!rst_n)
  total_gbps == (payload_gbps + descriptor_gbps + context_gbps
                 + interrupt_gbps));

// A2. Fitting means the total is within the link.
p_hb_fits: assert property (@(posedge clk) disable iff (!rst_n)
  fits == (total_gbps <= 16'(HOST_USABLE_GBPS)));

// A3. Past the cliff is the negation of fitting.
p_hb_cliff: assert property (@(posedge clk) disable iff (!rst_n)
  past_the_cliff == !fits);

// A4. Headroom is never negative.
p_hb_headroom: assert property (@(posedge clk) disable iff (!rst_n)
  headroom_gbps >= 16'd0);

// A5. Payload never exceeds the line rate.
p_hb_payload: assert property (@(posedge clk) disable iff (!rst_n)
  payload_gbps <= line_rate_gbps);

// A6. The critical miss rate is headroom over full-miss traffic.
p_hb_critical: assert property (@(posedge clk) disable iff (!rst_n)
  (miss_pct <= 16'(critical_miss_pct_i)) |-> fits);

Group B — descriptors.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// B1. Thirty-two octets per packet, fetch plus writeback.
p_ds_octets: assert property (@(posedge clk) disable iff (!rst_n)
  desc_octets_per_pkt == 16'(DESC_OCTETS * DESC_PER_PKT));

// B2. Batching never reduces bytes.
p_ds_batching: assert property (@(posedge clk) disable iff (!rst_n)
  !batching_reduces_bytes);

// B3. Batching does improve TLP efficiency.
p_ds_tlp: assert property (@(posedge clk) disable iff (!rst_n)
  tlp_efficiency_pct ==
    16'((BATCH * DESC_OCTETS * 100) / ((BATCH * DESC_OCTETS) + 24)));

// B4. Fetches and writebacks are one-for-one per packet.
p_ds_pair: assert property (@(posedge clk) disable iff (!rst_n)
  c_fetches == c_writebacks);

// B5. Descriptor bandwidth scales with the packet rate, not the octet
//     rate.
p_ds_scales: assert property (@(posedge clk) disable iff (!rst_n)
  desc_gbps_o == 16'((pps_million * desc_octets_per_pkt * 8) / 1000));

Group C — in flight.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// C1. Little's law: in flight is rate times latency.
p_if_little: assert property (@(posedge clk) disable iff (!rst_n)
  descriptors_in_flight == 16'((pps_million * host_latency_ns) / 1000));

// C2. An underrun is possible exactly when the store is short.
p_if_underrun: assert property (@(posedge clk) disable iff (!rst_n)
  underrun_possible == (descriptors_in_flight > prefetch_depth));

// C3. The achievable rate is outstanding over latency.
p_if_achievable: assert property (@(posedge clk) disable iff (!rst_n)
  (host_latency_ns != 0) |->
    (achievable_pps_million ==
      16'((outstanding_limit * 1000) / host_latency_ns)));

// C4. Payload in flight matches Chapter 19.6 Section 4's figure.
p_if_payload: assert property (@(posedge clk) disable iff (!rst_n)
  payload_in_flight_bytes == ((rate_gbps * host_latency_ns) / 8));

// C5. The shortfall is never negative.
p_if_shortfall: assert property (@(posedge clk) disable iff (!rst_n)
  prefetch_shortfall >= 16'd0);

Group D — the context cache.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// D1. A miss occurs only when the working set exceeds the cache.
p_cx_cause: assert property (@(posedge clk) disable iff (!rst_n)
  miss |-> exceeds_capacity);

// D2. Every miss evicts a dirty line.
p_cx_dirty: assert property (@(posedge clk) disable iff (!rst_n)
  miss |-> evict_dirty);

// D3. A miss costs a fill and a writeback.
p_cx_octets: assert property (@(posedge clk) disable iff (!rst_n)
  miss |-> ((fill_octets + writeback_octets) == 32'(CTX_MISS_OCTETS)));

// D4. Hit and miss are mutually exclusive.
p_cx_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
  !(hit && miss));

// D5. The miss rate follows the capacity model.
p_cx_rate: assert property (@(posedge clk) disable iff (!rst_n)
  exceeds_capacity |->
    (miss_pct == 16'(((working_set - ENTRIES) * 100) / working_set)));

// D6. Dirty evictions never exceed misses.
p_cx_evictions: assert property (@(posedge clk) disable iff (!rst_n)
  c_dirty_evictions <= c_misses);

Group E — queues and moderation.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// E1. An interrupt fires only with packets pending.
p_qs_pending: assert property (@(posedge clk) disable iff (!rst_n)
  interrupt |-> (pending != 16'd0));

// E2. Moderation is honoured.
p_qs_moderation: assert property (@(posedge clk) disable iff (!rst_n)
  (interrupt && (pending < moderation_frames)) |-> timer_expired);

// E3. An interrupt clears the pending count.
p_qs_clears: assert property (@(posedge clk) disable iff (!rst_n)
  interrupt |=> (pending == 16'd0));

// E4. The added latency is the moderation window.
p_qs_latency: assert property (@(posedge clk) disable iff (!rst_n)
  added_latency_ns == (moderation_us * 1000));

// E5. A queue index is inside the queue set.
p_qs_index: assert property (@(posedge clk) disable iff (!rst_n)
  pkt_arrives |-> (pkt_queue < 11'(QUEUES)));

// E6. Packets counted equal packets arrived.
p_qs_count: assert property (@(posedge clk) disable iff (!rst_n)
  pkt_arrives |=> (c_packets == $past(c_packets) + 1));

Group F — the claim.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// F1. A line-rate claim requires the working set to fit.
p_cl_fit: assert property (@(posedge clk) disable iff (!rst_n)
  claim_line_rate |-> !exceeds_capacity);

// F2. And the host budget to close.
p_cl_budget: assert property (@(posedge clk) disable iff (!rst_n)
  claim_line_rate |-> fits);

// F3. And a frame size the claim was measured at.
p_cl_frame_size: assert property (@(posedge clk) disable iff (!rst_n)
  claim_line_rate |-> (claimed_frame_octets != 16'd0));

// F4. The achieved rate never exceeds the line rate.
p_cl_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  achieved_gbps <= line_rate_gbps);

// F5. The limiter verdict names a constraint that is actually binding.
p_cl_limiter: assert property (@(posedge clk) disable iff (!rst_n)
  (o_limiter == LIM_CACHE) |-> exceeds_capacity);

// F6. A past-the-cliff state is reported, not smoothed.
p_cl_reported: assert property (@(posedge clk) disable iff (!rst_n)
  past_the_cliff |-> caveat_past_cliff);

Thirty-four properties, and every one holds at both ends of Section 11's collapse.


21. Verification Scenarios

Fifty-eight scenarios. Group 4's require a workload rather than a stimulus, and they are the only ones that reach the cliff.

Group 1 — the per-packet budget (8).

#ScenarioExpect
1100 Gb/s, minimum frames148.81 Mpps, 6.72 ns per packet
2400 Gb/s, minimum frames595.24 Mpps, 1.68 ns — under two cycles
310 Gb/s, minimum frames14.88 Mpps, 67.2 ns — comfortable
4the offload set in the budgetfits: pipelined, one packet per cycle
5a host memory read in the budgetdoes not fit — 1 µs against 6.72 ns
61 518-octet frames at 100 Gb/s8.13 Mpps, 123 ns per packet
79 000-octet frames1.39 Mpps
8a per-packet host transaction attempted inlinethroughput collapses to 1 Mpps

Group 2 — descriptors and the host interface (12).

#ScenarioExpect
916-octet descriptors, fetch and writeback32 octets per packet
10at 148.81 Mpps38.1 Gb/s — 38.1% of the payload
11at 1 518-octet frames2.1 Gb/s — 2.1%
12at 9 000-octet frames0.35 Gb/s — a factor of 109 against row 10
13batching ×4bytes unchanged; TLP efficiency 40.0% → 72.7%
14batching ×16bytes unchanged; efficiency 91.4%
15100 Gb/s total host demand138.1 Gb/s against 378 — fits
16200 Gb/s276.2 — fits
17400 Gb/s552.4 — misses Gen5 ×16 by 46%
18400 Gb/s with 1 518-octet frames408.3 — still misses, by 8%
19800 Gb/s on Gen6 ×161 104.8 against 768 — does not fit
20batching_reduces_bytes forced truep_ds_batching fires

Group 3 — in flight (10).

#ScenarioExpect
21148.81 Mpps, 1 µs latency149 descriptors in flight
22a prefetch store of 8short by 19×; underruns
23a store of 149no underrun at the mean
24a 3 µs latency excursion446 needed; a store of 149 underruns
25a store of 450rides the excursion; 7.2 kB, 0.20 datapaths
268 outstanding at 1 µs8 Mpps — 5.4 Gb/s of a 100 Gb/s port
2764 outstanding43.0 Gb/s
28100 Gb/s payload across 1 µs12.2 KiB — Chapter 19.6 §4's figure exactly
29underrun with the store fullp_if_underrun fires
30a 400 Gb/s part with a 149-deep store595 needed; underruns at line rate

Group 4 — the context cache and the cliff, which need a workload (14).

#ScenarioExpect
311 024 connections, 16 384-entry cache0% miss; 100 Gb/s
328 192 connections0% miss; 100 Gb/s — indistinguishable from row 31
3316 384 connections0% miss; 100 Gb/s; no margin
3420 000 connections18.1% miss; still 100 Gb/s
3527 018 connections39.4% miss — exactly the cliff
3632 768 connections50.0% miss; 35.1 Gb/s
3737 038 connections55.8% miss; 0 Gb/s
38rows 31 to 34 comparedno measurable difference — the margin is invisible
39extrapolating from rows 31 and 32 to row 36predicts 100 Gb/s; delivers 35.1
40every property in Section 20 during row 36all 34 hold
41a miss with the working set inside the cachep_cx_cause fires
42a clean evictionp_cx_dirty fires — every eviction is dirty
43doubling the cache to 32 768row 36 becomes 0% miss; the part is 66% larger
44Section 12's rule applied insteadstripes ≤ 16; the same result for a division

Group 5 — queues and moderation (8).

#ScenarioExpect
45no moderation at 148.81 Mpps2.98 × 10¹¹ host cycles/s — 99 cores
4650 µs moderation, one queue20 000 interrupts/s, 0.013 of a core, 50 µs latency
475 µs moderation, one queue200 000/s, 0.13 of a core, 5 µs latency
4850 µs moderation, 2 048 queues40.96 M/s — 27 cores; the per-queue trap
49only active queues interruptingthe rate follows traffic, not configuration
50a polled driverno interrupts at all
51an interrupt with nothing pendingp_qs_pending fires
52a queue index of 2 048p_qs_index fires

Group 6 — telemetry and the claim (6).

#ScenarioExpect
53a 32-bit packet counter at 148.81 Mppswraps in 28.9 s — shorter than most polling
5448 bits21.9 days
55high miss rate, high distinct counto_limiter = capacity
56high miss rate, low distinct countconflict misses — a different remedy
57a line-rate claim with the working set over capacityp_cl_fit fires
58a line-rate claim with no frame size statedp_cl_frame_size fires

Scenario 38 is the one that makes the class visible and scenario 39 is the one that explains every surprised programme.

The directed test random stimulus will not produce

This test requires two measurements that are identical and one prediction from them that is wrong by a factor of three.

The arrangement:

StepAction
1a 100 Gb/s NIC with a 16 384-entry context cache and a Gen5 ×16 host interface
2run A: 8 192 distinct connections, minimum frames, full line rate offered
3run B: 16 000 distinct connections, otherwise identical
4measure achieved rate, miss rate, host utilisation and latency in both
5extrapolate linearly from A and B to 32 768 connections
6run C: 32 768 distinct connections, otherwise identical

Random stimulus will not produce this because a constrained-random generator randomises connection identifiers and not the number of distinct ones. A generator drawing five-tuples from a large space produces a working set equal to the number of packets it has sent — which passes through 8 192 and 16 384 and 32 768 in the first second and spends almost all of its time far above all three. The test needs the distinct count held constant at three specific values, which is a workload parameter rather than a randomisation.

The oracle is in four parts and the third is the finding.

PartSignalRequired valueWhy alone it is not enough
1 — runs A and B are identicalrate, miss %, host utilisation100 Gb/s, 0%, 36.5% in boththis is the trap, not the result
2 — the extrapolationpredicted rate at 32 768100 Gb/sit is what any curve fit gives
3 — run Cachieved rate35.1 Gb/sa factor of 2.85 from the prediction
4 — every assertion in Section 20all 34hold in all three runsnothing is broken

Part 1 is the assertion that two measurements are the same, and it is the heart of the test. A verification plan that samples the workload space and checks that each sample passes will sample A and B, find them identical and confident, and conclude that the part is insensitive to the connection count. It is insensitive — over the interval it sampled.

Part 3 needs no assertion to be interesting; it needs the run to happen. Which is why Section 20's fourth replacement property is a cover: working_set > 2 × CTX_CACHE_ENTRIES. A coverage goal forces the visit; an assertion cannot, because there is nothing to assert about a region the test never enters.


22. Debugging a NIC That Does Not Reach Line Rate

A procedure, ordered so that the three constraints are separated before anything is changed.

StepDoBecause
1read the frame-size distributionSection 4 — an eighteenfold difference in descriptor overhead
2read the context miss rateSection 12 — the cliff is at 39.4%
3read the distinct-connection countSection 9 — it separates capacity from conflict misses
4read the prefetch underrun countSection 8 — a store sized for a previous generation
5read host-interface utilisationSection 6 — the budget may simply not close
6read o_limiterthe part's own three-way verdict
7ask what chose the connection countSection 12 — usually a library, for a fabric reason
8only now look at the link and the switcha NIC past the cliff drops nothing

Step 8 is last for a specific reason. A NIC past Section 11's cliff does not drop packets — it slows down, applies backpressure, and the drops appear on the upstream switch's counters. Chapter 21.6's method finds the drop at the point of loss; here the point of loss is a different device and the cause is a cache two hops away.

Four signatures.

SignatureCause
rate falls sharply at a connection count and is flat either sidethe context cache cliff — Section 12
drops at line rate with an idle host and a non-full ringprefetch underrun — Section 8
rate scales with frame size far more than payload alone predictsdescriptor overhead — Section 4
the NIC is fine and the upstream switch is droppingthe NIC is past the cliff and backpressuring

Row one is diagnosable in two measurements and is almost never measured that way. The distinguishing feature is flatness on both sides: a NIC limited by host bandwidth degrades smoothly with load, and one limited by its cache is flat, then a cliff, then flat. Sweeping the connection count — not the packet rate — finds it in minutes, and it is not a sweep anybody runs by habit.

Row four is the one that sends the investigation to the wrong device. The switch reports drops, its counters are correct, and its buffers are genuinely overflowing — because the NIC below it stopped accepting at line rate. Every instrument points at the switch and the cause is in a NIC's context cache, which nothing in Module 21's method would ever reach.


23. Misconceptions

Six, in wrong-model / what-it-costs / corrected-model form.

Misconception 1 — "the host interface carries the payload."

Wrong model: a 100 Gb/s NIC needs 100 Gb/s of host bandwidth.

What it costs: 38% of the budget, and a 400 Gb/s design. Section 4: a descriptor fetch and writeback is 32 octets per packet, and at 148.81 Mpps that is 38.1 Gb/s — bookkeeping that carries no data. At 400 Gb/s it is 152.4 Gb/s, and payload plus descriptors are 552.4 against a Gen5 ×16 link's 378.

Corrected model: the host interface carries payload, descriptors, context fills, context writebacks and interrupts, and only the first is data. A 400 Gb/s NIC does not fit on Gen5 ×16 because of its descriptors, and the specification that says it does is written against a frame size stated in a footnote.

Misconception 2 — "batch the descriptors."

Wrong model: fetching four descriptors in one transaction reduces descriptor bandwidth.

What it costs: the actual fix. Section 5: four 16-octet descriptors in one transaction move 64 octets, which is the same 64 octets. What batching improves is TLP efficiency — 40.0% to 72.7% at ×4, 91.4% at ×16 — by amortising one 24-octet header.

Corrected model: batching changes the transaction count and not the byte count. Reducing the bytes needs a smaller descriptor (linear) or no descriptor fetch at all (a pre-posted fixed-size buffer pool, which halves the overhead and costs the flexibility a descriptor provides).

Misconception 3 — "a NIC is mostly a packet buffer, like a switch."

Wrong model: the dominant structure is the same in both parts.

What it costs: the whole failure model. Section 10: a NIC is 66.2% connection context and 31.6% packet buffer; Chapter 23.3 §4's switch is 91.0% packet buffer. And per gigabit of line rate the NIC carries 5.8 times more state — 507 000 BCE per Gb/s against 87 900.

Corrected model: a switch's dominant structure holds what arrived and fails by filling, which produces a drop with a counter. A NIC's holds what it predicted would be needed and fails by missing, which produces a host memory fetch — and running out of it slows the part down without dropping anything.

Misconception 4 — "measure it at 95% of capacity and extrapolate."

Wrong model: performance degrades smoothly, so a measurement near the limit predicts behaviour past it.

What it costs: a factor of 2.85. Section 20's table: 8 192 connections and 16 000 connections both deliver 100 Gb/s with a 0% miss rate and no measurable difference — and 32 768 delivers 35.1. The two passing measurements contain no information about the third.

Corrected model: a cache makes throughput a step function of the working set. The part is at line rate until about 27 000 connections and at zero by 37 000 — a 10 000-connection window in which everything happens. Curve-fitting across the flat region predicts the flat region, and the only way to find the cliff is to visit the far side deliberately — which is why Section 20's fourth replacement property is a cover rather than an assert.

Misconception 5 — "more stripes, more throughput."

Wrong model: Chapter 23.2 §12 measured striping ×32 at 93.8% fabric efficiency, so use 32.

What it costs: the whole gain, and more. Section 12: 32 stripes on a 1 024-participant job is 32 768 connections against a 16 384-entry cache — a 50% miss rate, past the 39.4% cliff — and the NIC delivers 35.1 Gb/s. Striping ×8 delivers 77.1 Gb/s of fabric efficiency at 8 192 connections and 100 Gb/s of NIC, which is better in every respect.

Corrected model: stripes ≤ context cache entries ÷ participants. It is one division, it belongs in the collective library's configuration, and neither the fabric's efficiency curve nor the NIC's datasheet contains the other's term. Chapter 23.2 §12's two best remedies — hierarchical reduction and load-aware placementcreate no connections at all, which is a second and independent reason to prefer them.

Misconception 6 — "the NIC is fine; the switch is dropping."

Wrong model: the drops are on the switch, so the problem is on the switch.

What it costs: the investigation. Section 22: a NIC past the cliff drops nothing. It processes fewer packets per second, applies backpressure, and the upstream switch's buffers overflow — so every counter, every instrument and Chapter 21.6's entire method point at the switch, whose counters are correct and whose buffers are genuinely full.

Corrected model: the point of loss and the point of cause are different devices. The cause is a context cache two hops away, and the only evidence is inside the NIC — its miss rate, its distinct-connection count and its own three-way limiter verdict. None of those crosses the wire unless somebody exports them, and the three together cost 0.05% of the part.


24. Interview Questions

Six, with what a strong answer contains.

1. How much host bandwidth does a 100 Gb/s NIC need?

138.1 Gb/s at minimum frame size, of which 38.1 is descriptors. A fetch and a writeback is 32 octets per packet and the packet rate is 148.81 Mpps, so the bookkeeping is 38.1% of the payload and carries no data. A strong answer notes the frame-size dependence — 2.1% at 1 518 octets, a factor of eighteen — and the consequence: at 400 Gb/s the total is 552.4 Gb/s against a Gen5 ×16 link's 378, and the descriptors are why.

2. Does batching descriptors help?

It improves efficiency and not bandwidth. Four 16-octet descriptors in one transaction move the same 64 octets and pay one 24-octet TLP header instead of four — efficiency from 40.0% to 72.7%, bandwidth unchanged at 38.1 Gb/s. A strong answer names what does reduce the bytes: a smaller descriptor, or eliminating the fetch entirely with a pre-posted fixed-size buffer pool, which halves the overhead at the cost of per-packet flexibility.

3. What is a NIC's dominant on-die structure, and why does it matter?

A connection context cache — 66.2% of the part, against a switch's 91.0% packet buffer. It matters because of how each fails: a buffer holds what arrived and fails by filling, producing a drop with a counter; a cache holds what it predicted would be needed and fails by missing, producing a host memory fetch that consumes the bandwidth everything else is already competing for. A strong answer adds that a NIC carries 5.8× more state per gigabit of line rate than a switch does.

4. Where is the cliff and what puts a workload over it?

39.4% context miss rate. After payload (100) and descriptors (38.1), a Gen5 ×16 link has 239.9 Gb/s of headroom; a miss costs 512 octets — a fill and a dirty writeback — and at 148.81 Mpps a 100% miss rate is 609.5 Gb/s. A strong answer names the workload that crosses it: Chapter 23.2 §12's striping ×32 puts 32 768 connections against a 16 384-entry cache — a 50% miss rate — and the part falls from 100 Gb/s to 35.1.

5. Why can't you extrapolate a NIC's performance from measurements below its limit?

Because a cache makes throughput a step function of the working set. Measurements at 8 192 and 16 000 connections are identical — 100 Gb/s, 0% miss, the same host utilisation — and contain no information about 32 768. A strong answer names the shape: flat to about 27 000 connections, zero by 37 000, so the entire transition is a 10 000-connection window that a sweep of the packet rate never enters. The only way to find it is to sweep the connection count, which is a coverage goal rather than an assertion.

6. A NIC is slow and the upstream switch is dropping. Where do you look?

Inside the NIC, because a NIC past its cliff drops nothing. It slows, backpressures, and the switch's buffers overflow — so the drops, the counters and Chapter 21.6's whole method point at a device that is behaving correctly. A strong answer names the three fields that resolve it: the context miss rate, the distinct-connection count (which separates capacity from conflict misses) and the prefetch underrun count — and notes that all three together cost 0.05% of the part and are frequently not exported.


25. Questions and Answers


26. What's Next

The fixed-function NIC is done. What happens when the same part can run arbitrary code is next.

This chapter established
the per-packet budget6.72 ns at 100 Gb/s, 1.68 at 400
the descriptor overhead38.1% of the payload at minimum frame size
the host-interface wall552.4 Gb/s needed at 400 Gb/s against 378 available
the dominant structureconnection context, 66.2% — a cache, not a buffer
the cliff39.4% miss, crossed by Chapter 23.2's striping ×32
the Little's-law store149 descriptors at 100 Gb/s, against Chapter 18.2 §3's eight
the refused propertya performance claim with an unstated workload domain

And the chapter's most portable result is Section 12's rule. stripes ≤ context cache entries ÷ participants is one division, and it connects a decision made in a collective library to a structure on a NIC — two products, two teams, and no interface between them. Chapter 23.2 §14 asked for an interface through which a collective declares itself; this chapter is the second independent argument for the same thing.

Chapter 23.5 makes the part programmable. A SmartNIC runs code on the datapath, which moves work off the host and changes every assumption in this chapter: the per-packet budget becomes a per-packet instruction budget, the fixed-function offloads become programs, and the question what does this part do stops having a design-time answer. The questions are what work actually moves, what the per-packet instruction budget is at 148.81 million packets per second, what a programmable datapath costs in Chapter 23.3 §2's unit against the fixed-function blocks it replaces, and what happens to verification when the device's behaviour is decided after it ships.

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.