Skip to content
VLSI Mentor

Ethernet · Module 25

"Ethernet Equals TCP/IP"

A receive MAC parses 18 octets of a 1 518-octet frame, Ethernet names 64 000 protocols of which IP is two, and 66.2% of a modern NIC is the cost of crossing the boundary.

A myth chapter is not a lighter chapter. This one meets the same floor as every chapter in Module 23 and does the same thing: it derives the number that kills the myth rather than asserting the correction.

The myth, stated as the people who hold it would state it. Ethernet is the network. A network carries IP packets, and on those packets TCP provides reliable connections. So an Ethernet interface is a TCP/IP interface and the terms are interchangeable in ordinary conversation.

Three numbers dispose of it and this chapter derives all three.

The numberSection
what a receive MAC actually parses18 octets of a 1 518-octet frame — 1.19%2
how many protocol identifiers Ethernet can carry64 000, of which IPv4 and IPv6 are two4
what crossing the boundary costs, in one real part66.2% of Chapter 23.4's NIC10

Row three is the one worth stating first, because it converts a definitional argument into a silicon one. Chapter 23.4 §9 derived that a high-rate NIC's connection context cache is 3.356 × 10⁷ BCE — 118 MAC receive datapaths, 66.2% of the whole part. Every bit of it is above the Ethernet boundary, and a pure MAC holds none of it.

The myth is not merely imprecise. It is the reason two thirds of a modern network interface card exists, and it costs 118 MAC datapaths to act on.


1. Scope — The Myth, and the Eighteen Octets That Kill It

This chapter owns four derivations and a boundary built in RTL.

What is derived
Sections 2 to 3the parse window: 18 octets, 1.19% of a maximum frame, and zero edges above offset 21
Sections 4 to 7the EtherType space, and what a MAC structurally cannot see
Sections 8 to 9the one place the two layers genuinely touch — the MTU — and what the coupling costs
Sections 10 to 13what crossing the boundary costs: a 9.8× parser window and two thirds of a NIC

What this chapter does not own. It is not a TCP/IP tutorial: congestion control, the three-way handshake, routing protocols and the socket API are all outside it. It uses exactly as much of the upper layers as the boundary argument needs.

And it does not repeat Chapter 2.4. That chapter established where Ethernet stops and built an opaque payload path whose port list makes interpretation impossible. This chapter starts from that boundary and asks what it costs to cross it, which is a silicon question that chapter did not ask.


2. What a MAC Actually Reads

A frame's offsets, with the media access controller's parse window marked. Octets zero to five are the destination address, used by the receive address filter. Octets six to eleven are the source address, used by a switch for learning and by an end station for nothing. Octets twelve and thirteen are the EtherType or length, used to decide which consumer receives the payload. From octet fourteen onward is the payload, which is moved and not read. The last four octets are the frame check sequence, which is verified and discarded. That is eighteen octets in total, which is one point one nine per cent of a fifteen hundred and eighteen octet frame. The Internet Protocol version four header begins at octet fourteen and the transmission control protocol header at octet thirty four, so forty octets of headers sit immediately above a window that ends at octet thirteen. With an eight zero two point one Q tag the window ends at octet twenty one and the Internet Protocol header begins at twenty two, and there is still no overlap.Octets 0–5destination — filterOctets 6–11source — learningOctets 12–13type — demultiplexOctet 14 onwardmoved, not readLast 4 octetsFCS — verify, discard18 octets parsed1.19% of a max frameIPv4 at octet 1420 octetsTCP at octet 3420 more12
Figure 1 — the MAC's window ends at octet 13, and the IPv4 header begins at 14. They do not overlap in either the tagged or the untagged case.

Count the octets. A receive MAC touches eighteen of them and the count does not grow with the frame.

FieldOffsetOctetsWhat the MAC does with it
destination address0–56Chapter 7.4's filter — accept or discard
source address6–116Chapter 12.2's learning, in a switch; nothing, in an end station
EtherType or length12–132Chapter 5.5 — decide which consumer gets the payload
payload14 …46–1 500NOTHING — it is moved, not read
frame check sequencelast 44Chapter 5.8 — verify and discard

Eighteen octets, and every one of them is a property of the container.

FrameOctets parsedShare of the frameShare of the wire slot
64 octets1828.12%21.43%
1 518 octets181.19%1.17%
9 018 octets180.20%0.20%

On a maximum-size frame a receive MAC reads 1.19% of what arrives, and the 98.81% it does not read is where TCP/IP lives.

Now locate the upper layers against those offsets.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
octet 0                 6                12      14                34
      +-----------------+----------------+-------+-----------------+
      | destination     | source         | type  | IPv4 header ... | TCP ...
      +-----------------+----------------+-------+-----------------+
      |<------ the MAC's last parsed octet is 13 ------>|
                                                  |<-- 20 octets -->|<-- 20 -->|

With Chapter 13.2's 802.1Q tag the MAC's window extends to octet 21 and the IPv4 header begins at 22. In neither case does it overlap.

UntaggedTagged
MAC's last parsed octet1321
IPv4 header begins at1422
TCP header begins at3442
overlapnonenone

And the forty octets of IPv4 and TCP minimum headers are forty octets the MAC's state machine has no state for. Chapter 19.2 §4's receive parser is a state machine over the offsets above; there is no transition in it conditioned on an octet above 21, and adding one would be the boundary violation Chapter 2.4 §7's opaque payload path was built to make structurally impossible.


3. RTL 1 — The Myth Package and the Parse-Window Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// layermyth_pkg -- the offsets and sizes a layer-boundary argument
// needs, every one of them derived rather than written as a literal.
//
// Unit: Chapter 23.3 Section 2's bitcell equivalent.
//   1 BCE = one bit of usable on-die SRAM = 0.35 GE
//   1 flip-flop = 20 BCE
//   Chapter 19.7 Section 19's MAC receive datapath = 283 320 BCE
//   Chapter 23.4's NIC = 5.07e7 BCE, of which 66.2% is context cache
// ---------------------------------------------------------------------
package layermyth_pkg;

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

  // ---- what a MAC reads -------------------------------------------------
  localparam int unsigned OCT_DA        = 6;
  localparam int unsigned OCT_SA        = 6;
  localparam int unsigned OCT_TYPE      = 2;
  localparam int unsigned OCT_FCS       = 4;
  localparam int unsigned OCT_VLAN_TAG  = 4;    // Chapter 13.2
  localparam int unsigned MAC_PARSED    = OCT_DA + OCT_SA + OCT_TYPE
                                        + OCT_FCS;             // 18

  // The last octet offset a MAC's state machine may branch on.
  localparam int unsigned MAC_LAST_OFF_UNTAGGED = 13;
  localparam int unsigned MAC_LAST_OFF_TAGGED   = 21;

  // ---- what is above it --------------------------------------------------
  localparam int unsigned OCT_IPV4_MIN  = 20;
  localparam int unsigned OCT_IPV4_MAX  = 60;   // with options
  localparam int unsigned OCT_IPV6_FIX  = 40;
  localparam int unsigned OCT_TCP_MIN   = 20;
  localparam int unsigned OCT_TCP_MAX   = 60;   // with options
  localparam int unsigned OCT_UDP       = 8;

  // ---- the EtherType space ----------------------------------------------
  // Values at or below 1500 are a length (802.3); 1536 and above are a
  // type. So the type space is 65536 - 1536.
  localparam int unsigned TYPE_SPACE_LOW  = 1536;
  localparam int unsigned TYPE_SPACE_SIZE = 65_536 - TYPE_SPACE_LOW;  // 64000

  typedef enum logic [15:0] {
    ET_IPV4     = 16'h0800,
    ET_ARP      = 16'h0806,
    ET_VLAN     = 16'h8100,
    ET_IPV6     = 16'h86DD,
    ET_LACP     = 16'h8809,
    ET_PPPOE_D  = 16'h8863,
    ET_PPPOE_S  = 16'h8864,
    ET_PROFINET = 16'h8892,
    ET_ECAT     = 16'h88A4,
    ET_MACSEC   = 16'h88E5,
    ET_LLDP     = 16'h88CC,
    ET_PTP      = 16'h88F7,
    ET_FCOE     = 16'h8906,
    ET_ROCE     = 16'h8915
  } ethertype_e;

  localparam int unsigned COMMON_TYPES = 14;
  localparam int unsigned IP_TYPES     = 2;    // IPv4 and IPv6

  // ---- derived: the parse window ----------------------------------------
  function automatic int unsigned mac_window(bit tagged);
    return tagged ? (MAC_LAST_OFF_TAGGED + 1)
                  : (MAC_LAST_OFF_UNTAGGED + 1);
  endfunction

  function automatic int unsigned offload_window(int unsigned tunnels);
    // Ethernet header, then IPv4 with options and TCP with options,
    // then one more (outer IP, UDP, inner Ethernet) per tunnel.
    int unsigned w;
    w = (OCT_DA + OCT_SA + OCT_TYPE) + OCT_IPV4_MAX + OCT_TCP_MAX;
    for (int i = 0; i < tunnels; i++)
      w = w + OCT_IPV4_MAX + OCT_UDP + (OCT_DA + OCT_SA + OCT_TYPE);
    return w;
  endfunction

  function automatic int unsigned window_bce(int unsigned octets);
    return octets * 8 * BCE_PER_FLOP;     // held in registers
  endfunction

  // ---- derived: share of a frame ----------------------------------------
  function automatic int unsigned parsed_ppm(int unsigned frame_octets);
    return (MAC_PARSED * 1_000_000) / frame_octets;
  endfunction

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

endpackage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// parse_window_model -- how far into a frame a design must look, and
// what that costs, at each of three positions on the boundary.
//
// The output that matters is highest_offset_branched_on. For a MAC it
// is 13, or 21 with a tag. Anything above that is a design that has
// decided to cross the boundary, and Section 11 prices the decision.
// ---------------------------------------------------------------------
module parse_window_model
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        tagged,
  input  logic        crosses_boundary,
  input  logic [15:0] tunnel_levels,
  input  logic [15:0] frame_octets,

  output logic [15:0] window_octets,
  output logic [15:0] highest_offset_branched_on,
  output logic [31:0] window_bce_o,
  output logic [31:0] window_dp_milli,
  output logic [15:0] growth_x10,
  output logic [31:0] parsed_ppm_o,
  output logic        mac_remains_opaque,
  output logic        payload_is_read,
  output logic [31:0] c_frames_parsed
);
  logic [15:0] base_window;

  always_comb begin
    base_window = 16'(mac_window(tagged));

    window_octets = crosses_boundary
                  ? 16'(offload_window(int'(tunnel_levels)))
                  : base_window;

    // The one number that says which side of the boundary a design is on.
    highest_offset_branched_on = window_octets - 16'd1;

    window_bce_o    = 32'(window_bce(int'(window_octets)));
    window_dp_milli = 32'(datapaths_milli(window_bce_o));

    growth_x10 = (base_window == 0) ? 16'd0
               : (window_octets * 16'd10) / base_window;

    parsed_ppm_o = (frame_octets == 0) ? 32'd0
                 : 32'(parsed_ppm(int'(frame_octets)));

    // A MAC that has not crossed the boundary reads the container and
    // moves the contents -- Chapter 2.4 Section 7's opaque path.
    mac_remains_opaque = !crosses_boundary;
    payload_is_read    = crosses_boundary;
  end

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

Classification: an offset model whose single output decides which layer a design belongs to.

What it teaches: that highest_offset_branched_on is the operational definition of the boundary, and it is 13 or 21. A design that branches on octet 22 of an untagged frame is reading an IP header, whatever its documentation says, and Chapter 2.4 §7's opaque payload path is the structure that makes the number unable to rise.

And it teaches that growth_x10 reports 61 and 98 for the two offload cases. A tagged MAC holds 22 octets — 176 bits, 3 520 BCE; a design that offloads TCP checksum over IPv4 with options holds 134 octets — 21 440 BCE, 6.1×; one that must see through a tunnel holds 216 octets — 34 560 BCE, 9.8×. Those are small absolute numbers and a large ratio, and the ratio is what the myth costs a parser.

Deliberately simplified: offload_window assumes worst-case IPv4 and TCP option lengths, where most traffic carries neither and a real parser is opportunistic — it holds what it has seen and stalls if a field turns out to be further in. IPv6 extension headers are not modelled and they are unbounded in principle, which is the case that breaks every fixed-window parser. And the window is charged as registers at 20 BCE per bit, where a design that buffers the header in a small SRAM pays 1 BCE per bit and a cycle of latency.

Production implication: the unbounded case is the one that ships as a vulnerability rather than as a bug. An IPv6 extension-header chain has no specified maximum length, so a parser with a fixed window must decide what to do when the chain runs past it: stop parsing and forward without offload, or drop. Designs that silently forward with an incorrect checksum are the common failure, and the symptom is corrupted data on one traffic class that no counter reports, because the design believes it computed a checksum. The correct behaviour is to expose a parse_incomplete bit per frame and let the software decide — one bit in the receive descriptor, and it turns a silent corruption into a handled case.


4. The EtherType Space, and How Much of It Is IP

The EtherType space and what an ordinary port carries. The field is sixteen bits, so sixty five thousand five hundred and thirty six values exist. Values at or below fifteen hundred are a length in the eight zero two point three sense, so the type space is sixty four thousand values from fifteen hundred and thirty six upward. Internet Protocol version four at hexadecimal zero eight zero zero and version six at hexadecimal eight six D D are two of them, which is zero point zero zero three one three per cent. Of the thirteen EtherTypes an ordinary port actually sees, two are Internet Protocol and eleven are not: address resolution, virtual local area network tagging, link aggregation control, point to point over Ethernet, PROFINET, EtherCAT, link layer discovery, media access control security, precision time protocol, Fibre Channel over Ethernet and remote direct memory access over converged Ethernet version one. Precision time protocol skips Internet Protocol because a routed hop's variable delay is the error its servo must estimate, and link layer discovery must not be routable at all because a message describing this link would describe the wrong link after a router.16-bit field65 536 values≤ 1500a length — 802.3≥ 153664 000 type valuesIPv4 and IPv62 of 64 00013 seen on a port2 are IPPTP 0x88F7skips IP for accuracyLLDP 0x88CCmust not be routableRoCEv1 0x8915a whole transport12
Figure 2 — two of thirteen common EtherTypes carry IP, and several of the other eleven skip it deliberately.

Ethernet's payload-type field is sixteen bits. Count what that means and the myth's second half disposes of itself.

Chapter 5.5 established the encoding: a value at or below 1 500 is a length in the 802.3 sense, and 1 536 or above is a type. So:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
type values = 65 536 - 1 536 = 64 000 distinct protocol identifiers
IPv4 (0x0800) and IPv6 (0x86DD) are two of them
2 / 64 000 = 0.00313%

Ethernet can name sixty-four thousand payload protocols. IP is two of them, and the frame format has no opinion about which.

The counting argument is weak on its own — most of the space is unallocated — so count what an ordinary port actually sees.

EtherTypeProtocolCarries an IP octet?
0x0800IPv4yes
0x0806ARPno
0x8100802.1Q VLAN tag — Chapter 13.2it is a shim, not a payload
0x86DDIPv6yes
0x8809LACP and other slow protocols — Chapter 15.3no
0x8863 / 0x8864PPPoE discovery and sessionencapsulates, does not contain
0x8892PROFINETno
0x88A4EtherCATno
0x88CCLLDPno
0x88E5MACsecno — it encrypts what follows
0x88F7PTP — Chapter 16.2no, in its layer-2 mapping
0x8906FCoEno
0x8915RoCEv1 — Chapter 24.2 §10no

Two of thirteen carry IP — 15.4% — and the other eleven are protocols a real network carries daily.

Three of them are worth a sentence because they are this track's own subjects.

Chapter 16.2's precision time protocol has a layer-2 mapping — EtherType 0x88F7 — and it is the one deployments use when they care about Chapter 16.5's accuracy limits, because every IP hop adds variable delay that the servo must then estimate. So the highest-accuracy time distribution on Ethernet deliberately does not use IP.

Chapter 24.2 §10's RoCEv1 is an EtherType. An entire remote-DMA transport, moving the traffic Chapter 23.2's clusters run on, rides Ethernet with no IP header at all.

And MACsec encrypts everything above itself, including any IP header there may be. A switch that classifies on IP fields sees nothing on a MACsec-protected link, which is the point of MACsec and a surprise to designs that assumed the fields were there.


5. RTL 2 — The EtherType Demultiplexer, Counted

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// ethertype_demux -- Chapter 2.4 Section 3's demultiplexer, with the
// counters that make the myth falsifiable on a live port.
//
// The output that matters is ip_share_ppm. Run this on a real port for
// an hour and the myth is settled empirically rather than by argument.
// ---------------------------------------------------------------------
module ethertype_demux
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        clear,

  input  logic        type_valid,
  input  logic [15:0] ethertype,
  input  logic [15:0] frame_octets,

  output logic        is_length_field,
  output logic        is_ip,
  output logic        is_known_non_ip,
  output logic        is_unknown,
  output logic  [3:0] consumer_id,
  output logic [47:0] c_frames,
  output logic [47:0] c_ip_frames,
  output logic [47:0] c_non_ip_frames,
  output logic [47:0] c_unknown_frames,
  output logic [47:0] c_ip_octets,
  output logic [47:0] c_non_ip_octets,
  output logic [31:0] ip_share_ppm,
  output logic [31:0] ip_octet_share_ppm,
  output logic [31:0] type_space_size
);
  always_comb begin
    // Chapter 5.5's encoding: at or below 1500 it is a length.
    is_length_field = (ethertype <= 16'd1500);

    is_ip = type_valid && !is_length_field
         && ((ethertype == 16'(ET_IPV4)) || (ethertype == 16'(ET_IPV6)));

    is_known_non_ip = type_valid && !is_length_field && !is_ip
      && ((ethertype == 16'(ET_ARP))      || (ethertype == 16'(ET_VLAN))    ||
          (ethertype == 16'(ET_LACP))     || (ethertype == 16'(ET_PPPOE_D)) ||
          (ethertype == 16'(ET_PPPOE_S))  || (ethertype == 16'(ET_PROFINET))||
          (ethertype == 16'(ET_ECAT))     || (ethertype == 16'(ET_MACSEC))  ||
          (ethertype == 16'(ET_LLDP))     || (ethertype == 16'(ET_PTP))     ||
          (ethertype == 16'(ET_FCOE))     || (ethertype == 16'(ET_ROCE)));

    is_unknown = type_valid && !is_length_field && !is_ip && !is_known_non_ip;

    consumer_id = is_ip           ? 4'd1
                : is_known_non_ip ? 4'd2
                : is_length_field ? 4'd3     // 802.3 LLC path
                                  : 4'd0;    // nobody -- discard

    type_space_size = 32'(TYPE_SPACE_SIZE);

    ip_share_ppm = (c_frames == 0) ? 32'd0
                 : 32'((c_ip_frames * 48'd1_000_000) / c_frames);

    ip_octet_share_ppm =
      ((c_ip_octets + c_non_ip_octets) == 0) ? 32'd0
      : 32'((c_ip_octets * 48'd1_000_000)
            / (c_ip_octets + c_non_ip_octets));
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || clear) begin
      c_frames <= '0; c_ip_frames <= '0; c_non_ip_frames <= '0;
      c_unknown_frames <= '0; c_ip_octets <= '0; c_non_ip_octets <= '0;
    end else if (type_valid) begin
      c_frames <= c_frames + 48'd1;
      if (is_ip) begin
        c_ip_frames <= c_ip_frames + 48'd1;
        c_ip_octets <= c_ip_octets + 48'(frame_octets);
      end else begin
        c_non_ip_frames <= c_non_ip_frames + 48'd1;
        c_non_ip_octets <= c_non_ip_octets + 48'(frame_octets);
      end
      if (is_unknown) c_unknown_frames <= c_unknown_frames + 48'd1;
    end
  end
endmodule

Classification: a sixteen-way comparison and six counters, of which the counters are the point.

What it teaches: that the myth is falsifiable in an hour on any real port, and that ip_share_ppm and ip_octet_share_ppm will differ substantially. Non-IP frames are almost all small — LLDP, LACP, PTP and ARP are tens of octets — so a port carrying 8% non-IP frames may carry 0.3% non-IP octets, and which number a design should care about depends entirely on whether the cost is per frame or per octet.

And it teaches that consumer_id of zero is a real outcome that a design must handle. Chapter 2.4 §3's demultiplexer establishes that an EtherType with no registered consumer is discarded at the boundary, and c_unknown_frames counting is either a misconfiguration, a protocol the design does not implement, or traffic somebody added to the network without telling the hardware team.

Deliberately simplified: the comparison is flat where a real design uses a small CAM or a hash, because thirteen sixteen-bit comparators at line rate is a wide, slow structure at a 1 024-bit datapath. The VLAN case is not unwrapped — a frame with EtherType 0x8100 has its real type four octets later, and a design that counts 0x8100 as a protocol has counted a shim. And double tagging is not handled at all, which is Chapter 13.2 §6's problem and the reason a fixed-offset type field is a fiction.

Production implication: the VLAN simplification is the one that produces a wrong measurement rather than a wrong design. A port carrying tagged traffic reports 100% EtherType 0x8100 from this module, so ip_share_ppm reads zero and the myth appears refuted for entirely the wrong reason. The fix is to unwrap the tag before classifying — which means the classifier's input offset is no longer fixed, which is Chapter 19.2 §6's argument that a parser's cost is set by how far into the frame a field's position depends on the frame's own content. One shim costs one conditional; two costs a loop, and a loop at 595 Mpps is a pipeline.


6. What a MAC Cannot See, and What Follows

Section 2 said a MAC reads eighteen octets. This section says what that makes impossible, because the impossibilities are where the myth does damage.

A MAC cannot tellBecauseWhat needs it
a retransmission from an originalsequence numbers are in TCP, at octet 38any duplicate suppression
a connection from a flowports are at octets 34 and 36Chapter 23.4's context cache
which frames belong togetherfragmentation state is in the IP headerreassembly
whether delivery succeededacknowledgements are end to endChapter 24.1 §12's completion notion
who the sender is, end to endthe source MAC is this hop's, not the origin'sany end-to-end policy
how much to sendcongestion windows are a transport conceptrate control

Row five is the one that produces an operational surprise and it is worth a line of arithmetic. A frame arriving at a host from a station four routers away carries the source MAC of the last router, which means a MAC-address-based access control list on the receiving switch sees one address for every remote station in the world. Chapter 12.2's table on such a port holds one entry for the entire internet.

Row two is the expensive one and Section 10 prices it.


7. RTL 3 — The Opacity Ledger

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// opacity_ledger -- what a design knows about a frame, recorded by
// which layer the fact came from.
//
// Chapter 2.4 Section 7 made opacity structural by removing the ports
// through which a payload decision could leave. This does the
// complementary thing: it makes every fact carry its provenance, so a
// boundary violation is visible in a report rather than in a review.
// ---------------------------------------------------------------------
module opacity_ledger
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        clear,

  input  logic        frame_valid,
  input  logic [15:0] frame_octets,

  // facts a MAC may produce: all properties of the CONTAINER
  input  logic        fact_length,
  input  logic        fact_fcs_ok,
  input  logic        fact_da_matched,
  input  logic        fact_type,

  // facts that require reading the CONTENTS
  input  logic        fact_l3_protocol,
  input  logic        fact_l4_ports,
  input  logic        fact_tcp_flags,
  input  logic        fact_payload_bytes,

  output logic  [3:0] container_facts,
  output logic  [3:0] content_facts,
  output logic        boundary_crossed,
  output logic [15:0] deepest_layer,
  output logic [47:0] c_container_only,
  output logic [47:0] c_content_read,
  output logic [31:0] crossing_ppm,
  output logic        ledger_is_complete
);
  always_comb begin
    container_facts = { fact_type, fact_da_matched, fact_fcs_ok, fact_length };
    content_facts   = { fact_payload_bytes, fact_tcp_flags,
                        fact_l4_ports, fact_l3_protocol };

    // One bit, and it is the whole of Chapter 2.4's contract.
    boundary_crossed = (content_facts != 4'b0000);

    deepest_layer = fact_payload_bytes ? 16'd7
                  : fact_tcp_flags     ? 16'd4
                  : fact_l4_ports      ? 16'd4
                  : fact_l3_protocol   ? 16'd3
                                       : 16'd2;

    crossing_ppm = ((c_container_only + c_content_read) == 0) ? 32'd0
                 : 32'((c_content_read * 48'd1_000_000)
                       / (c_container_only + c_content_read));

    // Every fact is accounted to a layer. A design that produces a
    // fact this module has no input for has escaped the ledger.
    ledger_is_complete = 1'b1;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || clear) begin
      c_container_only <= '0; c_content_read <= '0;
    end else if (frame_valid) begin
      if (boundary_crossed) c_content_read   <= c_content_read + 48'd1;
      else                  c_container_only <= c_container_only + 48'd1;
    end
  end
endmodule

Classification: a provenance ledger, in which the design's own facts are classified by the layer they came from.

What it teaches: that deepest_layer is a reportable quantity and almost no design reports it. Chapter 2.4 §7 made opacity structural by removing the ports through which a payload decision could leave; that is the strongest form and it is only available when the block is designed that way. This is the retrofit: a design that has already crossed the boundary can at least say how far, and crossing_ppm says on what fraction of its traffic.

And it teaches that boundary_crossed is a single bit with a large consequence for verification. A testbench that asserts a property over content_facts has itself crossed the boundary, and Section 20's rejected property is about exactly that: the property can only be checked by an observer the product is forbidden to contain.

Deliberately simplified: the fact inputs are a fixed set, so a design producing a fact the ledger has no input for escapes it silentlyledger_is_complete is hard-wired true and is a claim rather than a check, which is the module's own instance of the problem it exists to catch. deepest_layer uses a priority encoder where a design reading both layer-3 and layer-7 facts should report both. And the counters do not distinguish reading a field from acting on it, which matters: a design that extracts a five-tuple for a hash has read it, and a design that drops on a TCP flag has acted on it, and only the second is a policy decision at the wrong layer.

Production implication: the distinction the counters miss is the one a security review cares about. Reading an IP field to compute Chapter 15.2's hash is a performance optimisation with no correctness consequence — a bad hash costs balance, not behaviour. Dropping a frame because of a TCP flag is a firewall, and a firewall in a MAC is a policy engine that the operating system does not know exists, cannot configure and cannot audit. The two are indistinguishable in this module and trivially distinguished by adding a fact_acted_on bit beside each fact_* input — four extra bits, and it turns a provenance ledger into an audit trail.


8. The One Place They Touch — the MTU

Six impossibilities and sixty-four thousand EtherTypes say the layers are separate. One number says they are coupled, and it is the only one.

1 500 is an Ethernet number. Chapter 5.7 §2 established where it came from — a bound on how long a station may hold the medium, chosen when the medium was shared — and it has no significance above layer 2 whatsoever.

And it is the number every IP stack in the world defaults to.

Why 1 500 is there
in Etherneta transmission-time bound on a shared medium, decided in 1980
in IPbecause the link below it is usually Ethernet
in TCPbecause the maximum segment size is the IP MTU less 40

So the transport's segment size is an Ethernet decision, three layers down, made for a medium that no longer exists.

Quantify the coupling by breaking it. An IP datagram larger than the link's MTU must be fragmented:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a 9 000-octet IP datagram -- 20 octets of header and 8 980 of payload
over a 1 500-octet MTU, each fragment carries 1 480 octets of payload
fragments = ceil(8 980 / 1 480) = 7
Unfragmented, 9 018 MTUFragmented, 1 500 MTU
frames on the wire17
IP headers20 octets140 octets
Ethernet framing38 octets266 octets
total wire octets9 0389 386
overhead penalty3.85%
loss amplification7× — one lost fragment discards the datagram

Row six is the cost and row five is the one people quote. A 3.85% efficiency penalty is unremarkable; a seven-fold loss amplification is not, and it is the same arithmetic Chapter 24.2 §10 found in RoCE's go-back-N for the same structural reason: a fixed quantity of data split into N pieces, all of which must arrive.

The layers are independent in every respect except one, and the exception costs a seven-fold loss amplification when it is got wrong.


9. RTL 4 — The MTU Coupling Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// mtu_coupling_model -- the one genuine dependency between the layers,
// and what breaking it costs.
//
// The output that matters is loss_amplification: fragmentation turns
// one datagram into N frames all of which must arrive, so the
// effective loss rate is multiplied rather than added to.
// ---------------------------------------------------------------------
module mtu_coupling_model
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic [15:0] link_mtu,
  input  logic [15:0] datagram_octets,
  input  logic        dont_fragment,
  input  logic        icmp_reaches_sender,
  input  logic [15:0] drop_rate_ppm,

  output logic [15:0] fragment_payload,
  output logic [15:0] fragments,
  output logic [31:0] wire_octets_frag,
  output logic [31:0] wire_octets_whole,
  output logic [15:0] overhead_penalty_x100,
  output logic [15:0] loss_amplification,
  output logic [31:0] effective_loss_ppm,
  output logic        black_hole,
  output logic        connection_establishes,
  output logic        bulk_transfer_fails,
  output logic [31:0] c_black_holes
);
  always_comb begin
    fragment_payload = (link_mtu > 16'(OCT_IPV4_MIN))
                     ? (link_mtu - 16'(OCT_IPV4_MIN)) : 16'd1;

    fragments = (datagram_octets <= fragment_payload)
              ? 16'd1
              : ((datagram_octets + fragment_payload - 16'd1)
                 / fragment_payload);

    wire_octets_whole = 32'(datagram_octets) + 32'(OCT_IPV4_MIN) + 32'd38;
    wire_octets_frag  = 32'(datagram_octets)
                      + (32'(fragments) * (32'(OCT_IPV4_MIN) + 32'd38));

    overhead_penalty_x100 =
      (wire_octets_whole == 0) ? 16'd0
      : 16'(((wire_octets_frag - wire_octets_whole) * 32'd10_000)
            / wire_octets_whole);

    // THE number. Every fragment must arrive or the datagram is lost.
    loss_amplification = fragments;
    effective_loss_ppm = 32'(drop_rate_ppm) * 32'(fragments);

    // The failure that establishes and then hangs.
    black_hole = dont_fragment && (datagram_octets > fragment_payload)
              && !icmp_reaches_sender;

    connection_establishes = 1'b1;              // the handshake is small
    bulk_transfer_fails    = black_hole;
  end

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

Classification: a division and a multiplication, of which the multiplication is the one nobody does.

What it teaches: that loss_amplification equals the fragment count and that this is a multiplication rather than an addition. A path with a 10⁻³ drop rate carrying 9 000-octet datagrams over a 1 500-octet MTU has an effective datagram loss rate of 7 × 10⁻³, because all seven fragments must arrive. The link's counters report 0.1% and the application experiences 0.7%, and neither number is wrong.

And it teaches that connection_establishes is hard-wired high, which is why the black hole is expensive. The handshake is small and always works; only bulk transfer fails, so the fault presents as an application problem on a path that passes every connectivity test — and c_black_holes is a counter no device keeps.

Deliberately simplified: the fragment arithmetic ignores that IPv4 fragment offsets are in units of eight octets, so real fragment payloads are rounded down to a multiple of eight — 1 480 happens to be one, which is why the example is clean and a 1 492-octet MTU on a PPPoE link is not. The model assumes IPv4; IPv6 does not permit routers to fragment at all, so the black hole is the only failure mode there. And drop_rate_ppm is applied independently per fragment, where losses are correlated: a congested queue drops bursts, and a burst is likely to take several fragments of one datagram together, which makes the real amplification lower than fragments and the variance higher.

Production implication: the correlation cuts the other way for the diagnosis. Independent losses would make fragmented traffic uniformly a little worse; correlated losses make it occasionally much worse, so the symptom is not a slightly elevated error rate — it is a transfer that is fine for minutes and then stalls. The defence in hardware is to make the coupling visible: a receive path that counts fragments separately from whole datagrams costs one comparison of the IP fragment-offset and more-fragments fields — which is a boundary crossing, and Section 7's ledger should record it as one. It is a legitimate crossing with a real diagnostic payoff, and the discipline is to declare it rather than to let it happen in a classifier nobody documented.


10. What Believing the Myth Costs in Silicon

What crossing the layer boundary costs in one real part. A pure media access controller holds a parse window of twenty two octets when virtual local area network tagged, which is three thousand five hundred and twenty bitcell equivalents, or zero point zero one two of a receive datapath. A design that offloads the transmission control protocol checksum over Internet Protocol version four with options holds one hundred and thirty four octets, twenty one thousand four hundred and forty bitcell equivalents, six point one times as much. Adding one level of tunnelling takes it to two hundred and sixteen octets and thirty four thousand five hundred and sixty bitcell equivalents, nine point eight times. And the connection context cache that a transmission control protocol offload requires is three point three five six times ten to the seventh bitcell equivalents, which is one hundred and eighteen point five receive datapaths and sixty six point two per cent of chapter twenty three point four's whole network interface card. The total against the pure media access controller is a ratio of nine thousand five hundred and forty five.Pure MAC22 oct — 3 520 BCE+ IPv4 and TCP134 oct — 6.1×+ one tunnel216 oct — 9.8×Context cache3.356e7 BCE66.2% of a NIC118.5 datapathsWhy it is therethe 39.4% miss cliff9 545×all above octet 21Octet 21the boundary12
Figure 3 — 9 545 times, and every bit of the difference is above octet 21.

The chapter's strongest argument is not that the layers are separate. It is that crossing the boundary is expensive, and that a real part exists whose cost is two thirds boundary crossing.

Chapter 23.4 §9 derived a high-rate NIC's area.

StructureBCEShareWhich layer?
connection context cache, 16 384 entries3.356 × 10⁷66.2%4 — TCP connections
everything else — MAC, DMA, descriptors, counters1.714 × 10⁷33.8%2 and the host interface
total5.070 × 10⁷100%

Two thirds of a modern network interface card is state about transport connections, and a device that stopped at the Ethernet boundary would hold none of it.

And the reason it is there is worth stating precisely, because it is not a mistake. Chapter 23.4 §10 established the cliff: above a 39.4% context-cache miss rate the NIC's throughput collapses, and Chapter 23.4 §12 derived that Chapter 23.2's striping ×32 pushes the connection count to 32 768 against a 16 384-entry cache — a 50% miss rate and 35.1 Gb/s of a 100 Gb/s port.

So the cache is load-bearing. A NIC that offloads TCP must hold connection state, and holding it is 66.2% of the part.

Now price the parser, which is the other half of crossing.

What the design parsesWindowHeld in registers× the datapathGrowth
Ethernet only, VLAN-tagged22 octets3 520 BCE0.0121.0×
+ IPv4 and TCP, with options134 octets21 440 BCE0.0766.1×
+ one level of tunnel216 octets34 560 BCE0.1229.8×

The absolute numbers are small and the ratio is not, and the ratio is what shows up as timing pressure: a 216-octet extraction window at Chapter 23.6 §4's 1 024-bit datapath is two beats of field extraction with content-dependent offsets, which is a much harder timing problem than a 22-octet window at a fixed offset.

Put the two together and the myth's silicon bill is complete.

BCE× the datapath
connection context, 16 384 entries3.356 × 10⁷118.5
a tunnel-capable parser window34 5600.122
total, one NIC3.359 × 10⁷118.6
a pure MAC's equivalent3 5200.012
ratio9 545×

Nine and a half thousand times, and every bit of the difference is above octet 21.


11. RTL 5 — The Offload Parser

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// offload_parser -- what a design must build when it decides to cross
// the boundary, and the one output that keeps the crossing honest.
//
// parse_incomplete is the whole point. A fixed window meets an
// unbounded header chain eventually, and the only safe behaviour is
// to say so rather than to compute a checksum over the wrong bytes.
// ---------------------------------------------------------------------
module offload_parser
  import layermyth_pkg::*;
#(
  parameter int unsigned WINDOW_OCTETS = 134
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_start,
  input  logic        octet_valid,
  input  logic  [7:0] octet,
  input  logic [15:0] octet_index,

  output logic [15:0] l3_offset,
  output logic [15:0] l4_offset,
  output logic        l3_offset_valid,
  output logic        l4_offset_valid,
  output logic        is_ipv4,
  output logic        is_tcp,
  output logic        parse_incomplete,
  output logic        checksum_may_be_computed,
  output logic [15:0] deepest_octet_read,
  output logic [31:0] window_bce_o,
  output logic [31:0] c_incomplete,
  output logic [31:0] c_parsed_ok
);
  logic [15:0] type_hi, type_lo;
  logic [15:0] eth_type;
  logic  [3:0] ihl;
  logic        vlan_present;

  always_comb begin
    window_bce_o = 32'(window_bce(WINDOW_OCTETS));

    eth_type = { type_hi[7:0], type_lo[7:0] };
    vlan_present = (eth_type == 16'(ET_VLAN));

    // Chapter 19.2 Section 6's problem: the offset depends on content.
    l3_offset       = vlan_present ? 16'd18 : 16'd14;
    l3_offset_valid = (octet_index >= l3_offset);

    is_ipv4 = l3_offset_valid && (eth_type == 16'(ET_IPV4));

    // And again one layer up: the IPv4 header's length is IN the header.
    l4_offset       = l3_offset + (16'(ihl) * 16'd4);
    l4_offset_valid = is_ipv4 && (octet_index >= l4_offset);

    is_tcp = l4_offset_valid;

    deepest_octet_read = octet_index;

    // THE output. A window is finite and a header chain is not.
    parse_incomplete = (l4_offset + 16'd20) > 16'(WINDOW_OCTETS);

    // Never compute a checksum you could not locate the bounds of.
    checksum_may_be_computed = l4_offset_valid && !parse_incomplete;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      type_hi <= '0; type_lo <= '0; ihl <= '0;
      c_incomplete <= '0; c_parsed_ok <= '0;
    end else begin
      if (frame_start) begin
        type_hi <= '0; type_lo <= '0; ihl <= '0;
      end
      if (octet_valid) begin
        if (octet_index == 16'd12) type_hi <= {8'd0, octet};
        if (octet_index == 16'd13) type_lo <= {8'd0, octet};
        if (octet_index == l3_offset) ihl <= octet[3:0];
        if (octet_index == 16'(WINDOW_OCTETS) - 16'd1) begin
          if (parse_incomplete) c_incomplete <= c_incomplete + 32'd1;
          else                  c_parsed_ok  <= c_parsed_ok + 32'd1;
        end
      end
    end
  end
endmodule

Classification: a parser with two content-dependent offsets and one honest failure output.

What it teaches: that the offsets depend on the content twice, at two different layers, and each dependency costs the same thing Chapter 19.2 §6 identified: a field whose position the frame's own content decides cannot be extracted by a fixed mux. The VLAN tag moves l3_offset by four; the IPv4 header's own ihl field moves l4_offset by up to forty — and ihl is inside the header whose length it describes.

And it teaches that parse_incomplete is the output a design must have and usually does not. A fixed window meets an unbounded chain — IPv4 options, IPv6 extension headers, a second tunnel — and the three possible behaviours are: drop, forward without offload, or compute a checksum over whatever happened to be in the window. The third is a silent corruption and it is the common one, because the parser does not know it failed.

Deliberately simplified: the module handles one VLAN tag, not Chapter 13.2 §6's stacked tags, and it handles IPv4 but not IPv6's extension-header chain, which is the case with no bound at all. ihl is captured in one cycle at a computed index, where a real design at a 1 024-bit datapath extracts it with a barrel shifter — Chapter 23.6 §4's aligner, which at 1 024 bits was 41 984 LUT6. And the checksum itself is not built: it is a 16-bit ones-complement adder tree over the datapath, 64 adders at 1 024 bits, whose cost is logic rather than state and therefore outside BCE.

Production implication: the three behaviours on window exhaustion should be a configuration field rather than a design decision, and almost no part exposes one. A switch wants "forward without offload"; a NIC wants "forward and set parse_incomplete in the descriptor"; a security appliance wants "drop" — and all three are correct for their role. A part that hard-codes one of them has made a policy decision for its integrator, and the integrator finds out when a traffic class with IPv6 extension headers either disappears or arrives corrupted. One two-bit configuration register, and one bit in the receive descriptor, is the whole cost of getting it right.


12. Where the Boundary Actually Fails

Sections 10 and 11 priced a deliberate crossing. This section is about the crossings nobody decided to make, and they are all the same shape: a field assumed present at an offset in traffic that has neither.

Five failures, each with a number.

FailureThe assumptionWhat it costs
a hash that finds no IP headerevery frame has a five-tupleall non-IP traffic on one Chapter 15.2 member
a filter keyed on a TCP portthe port is at octet 34PTP, LLDP, FCoE and RoCEv1 misclassified
a checksum at a fixed offsetthe IPv4 header is 20 octetscorrupted data whenever an option is present
a testbench that only makes IPthe traffic is IPthe non-IP path is never exercised
an assertion naming "the source address"there is oneSection 2's ambiguity; the property means two things

Row one has the largest number and it is worth deriving. Chapter 15.2 §5 established that a five-tuple hash distributes across an aggregation group's members. A frame with no IP header has no five-tuple, so a hash implemented as extract the tuple, hash it returns a constant on all such frames — and they all take member zero.

FramesWhere they land
IP traffic on an 8-member LAG92%spread across 8
non-IP traffic8%all on member 0
member 0's share11.5% + 8% = 19.5%
the other seven11.5% each

A 70% imbalance on one member, and Chapter 23.2 §7 established that a collective runs at its slowest member, so the fabric's delivered bandwidth is set by the imbalanced link rather than by the mean.

And on a fabric carrying FCoE or RoCEv1 the arithmetic is much worse, because those are bulk protocols: a storage fabric where 40% of the octets are FCoE puts 40% of the load on one member of an eight-member group.

The fix is one line and it is in every good hash implementation: hash over whatever fields are actually present, falling back to the MAC addresses and the EtherType when there is no IP header. It costs a multiplexer and it converts a pathological case into a merely imperfect one.


13. RTL 6 — The Tunnel Depth Limiter

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// tunnel_depth_limiter -- the structure that makes Section 11's
// unbounded case bounded, and reports when it did.
//
// The output that matters is depth_exceeded, which is how a finite
// design meets an infinite specification honestly.
// ---------------------------------------------------------------------
module tunnel_depth_limiter
  import layermyth_pkg::*;
#(
  parameter int unsigned MAX_DEPTH  = 2,
  parameter int unsigned MAX_OCTETS = 216
)(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        frame_start,
  input  logic        header_seen,
  input  logic [15:0] header_octets,
  input  logic        header_is_encapsulation,

  output logic [15:0] depth,
  output logic [15:0] octets_consumed,
  output logic        depth_exceeded,
  output logic        octets_exceeded,
  output logic        stop_parsing,
  output logic  [1:0] action_on_limit,      // configured, not hard-coded
  output logic        may_claim_verified,
  output logic [31:0] c_depth_exceeded,
  output logic [31:0] c_octets_exceeded
);
  // 0 = forward without offload, 1 = forward and flag, 2 = drop.
  // Section 11's production note: this is the integrator's choice.
  localparam logic [1:0] ACT_NO_OFFLOAD = 2'd0;

  always_comb begin
    depth_exceeded  = (depth > 16'(MAX_DEPTH));
    octets_exceeded = (octets_consumed > 16'(MAX_OCTETS));
    stop_parsing    = depth_exceeded || octets_exceeded;

    action_on_limit = ACT_NO_OFFLOAD;

    // THE rule. A design that could not locate the bounds must not
    // claim to have checked what is inside them.
    may_claim_verified = !stop_parsing;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      depth <= '0; octets_consumed <= '0;
      c_depth_exceeded <= '0; c_octets_exceeded <= '0;
    end else begin
      if (frame_start) begin
        depth <= '0; octets_consumed <= '0;
      end else if (header_seen) begin
        octets_consumed <= octets_consumed + header_octets;
        if (header_is_encapsulation) depth <= depth + 16'd1;
        if ((depth + 16'd1) > 16'(MAX_DEPTH))
          c_depth_exceeded <= c_depth_exceeded + 32'd1;
        if ((octets_consumed + header_octets) > 16'(MAX_OCTETS))
          c_octets_exceeded <= c_octets_exceeded + 32'd1;
      end
    end
  end
endmodule

Classification: two counters and a prohibition, which is how a finite parser meets an unbounded format.

What it teaches: that may_claim_verified is the one signal that makes the limiter safe rather than merely present. A parser that stops has not failed; a parser that stops and still reports a verified checksum has. The distinction is one AND gate and it separates a graceful degradation from a silent data corruption.

And it teaches that two limits are needed rather than one. MAX_DEPTH bounds encapsulation levels and MAX_OCTETS bounds total header bytes, and neither implies the other: a single IPv6 packet with a long extension-header chain has depth 1 and can exhaust the octet budget, while three trivially small tunnels exhaust the depth budget at a fraction of it. A design with only a depth limit is unbounded in octets, which is the case that actually ships.

Deliberately simplified: the limiter consumes headers one at a time where a real parser at a 1 024-bit datapath sees several per beat and must evaluate the limits combinationally across all of them. action_on_limit is a localparam rather than the configuration register Section 11's production note argued for, which is the simplification the note itself identifies as wrong. And header_is_encapsulation is supplied from outside, so the module does not decide what counts as a tunnel — which is the interesting question, since a VLAN tag, a PPPoE header and a VXLAN header are all encapsulations at different layers.

Production implication: the question the module does not answer is the one that decides whether a limit is a security control. A design counting only IP-in-IP tunnels toward MAX_DEPTH can be driven to arbitrary parse depth with stacked VLAN tags, each of which is four octets and none of which increments the counter. The octet budget catches it and the depth budget does not, which is the argument for having both: an attacker chooses the encapsulation the counter does not count, and only a limit on total work is immune to that choice. The general rule is worth carrying: bound the resource, not the construct — a limit on levels bounds a category and a limit on octets bounds the work, and the work is what runs out.


14. What a MAC Must Never Do

Five prohibitions, and each has a failure in this chapter behind it.

#NeverBecause
1branch on an octet above 13 — or 21 with a tag — without declaring itSection 3: that offset is the operational definition of the boundary
2claim a checksum was verified over a range you could not locateSection 12: on receive it is a silent corruption with no counter
3hash a five-tuple that is not thereSection 12: 70% imbalance on one Chapter 15.2 member
4bound a parser by encapsulation levels aloneSection 13: stacked VLAN tags increment no tunnel counter
5write a property naming "the source address"Section 2: there are two, they differ after a router, and only one is checkable here

Row one is the one to enforce structurally rather than by review, and Chapter 2.4 §7 showed how: remove the ports through which a payload decision could leave. A block whose port list cannot express a content-derived output cannot violate the boundary, and no review is required.

Row five is the one that reaches sign-off, and it is Section 20's refused property.


15. RTL 7 — Layer Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// layer_telemetry -- the counters that make the myth falsifiable on a
// live port, and the boundary auditable on a live design.
//
// Design rule: every counter here answers a question whose answer the
// myth predicts wrongly.
// ---------------------------------------------------------------------
module layer_telemetry
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
  input  logic        clear,

  input  logic        frame_valid,
  input  logic [15:0] frame_octets,
  input  logic        is_ip_i,
  input  logic        boundary_crossed_i,
  input  logic        parse_incomplete_i,
  input  logic        verified_claimed_i,
  input  logic        hash_had_tuple_i,
  input  logic [15:0] deepest_offset_i,
  input  logic        fragment_seen_i,

  output logic [47:0] c_frames,
  output logic [47:0] c_ip,
  output logic [47:0] c_non_ip,
  output logic [47:0] c_crossings,
  output logic [47:0] c_parse_incomplete,
  output logic [47:0] c_unverifiable_claims,
  output logic [47:0] c_hash_no_tuple,
  output logic [47:0] c_fragments,
  output logic [15:0] deepest_offset_seen,
  output logic [31:0] non_ip_ppm,
  output logic [31:0] crossing_ppm_o,
  output logic        design_is_layer2_only
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n || clear) begin
      c_frames <= '0; c_ip <= '0; c_non_ip <= '0; c_crossings <= '0;
      c_parse_incomplete <= '0; c_unverifiable_claims <= '0;
      c_hash_no_tuple <= '0; c_fragments <= '0; deepest_offset_seen <= '0;
    end else if (frame_valid) begin
      c_frames <= c_frames + 48'd1;
      if (is_ip_i) c_ip     <= c_ip + 48'd1;
      else         c_non_ip <= c_non_ip + 48'd1;
      if (boundary_crossed_i) c_crossings <= c_crossings + 48'd1;
      if (parse_incomplete_i) c_parse_incomplete <= c_parse_incomplete + 48'd1;

      // THE counter that finds Section 12's silent corruption.
      if (parse_incomplete_i && verified_claimed_i)
        c_unverifiable_claims <= c_unverifiable_claims + 48'd1;

      if (!hash_had_tuple_i) c_hash_no_tuple <= c_hash_no_tuple + 48'd1;
      if (fragment_seen_i)   c_fragments     <= c_fragments + 48'd1;
      if (deepest_offset_i > deepest_offset_seen)
        deepest_offset_seen <= deepest_offset_i;
    end
  end

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

    crossing_ppm_o = (c_frames == 0) ? 32'd0
                   : 32'((c_crossings * 48'd1_000_000) / c_frames);

    // A design that has never branched above octet 21 is layer 2, and
    // it can say so with evidence rather than with documentation.
    design_is_layer2_only = (deepest_offset_seen <= 16'd21);
  end
endmodule

Classification: a counter bank in which one counter — c_unverifiable_claims — should always read zero and is the only one worth an alarm.

What it teaches: that c_unverifiable_claims is the hardware form of Section 12's worst failure and it costs one AND gate. parse_incomplete AND verified_claimed is a design reporting a checksum it could not have computed, and it is the silent receive corruption that no other counter in any part reports. Any nonzero value is a bug, not a workload characteristic.

And it teaches that design_is_layer2_only converts a documentation claim into a measurement. A part whose datasheet says it is a layer-2 device and whose deepest_offset_seen reads 134 has crossed the boundary in the field, whatever its architecture document says, and the evidence is one 16-bit high-water register.

Deliberately simplified: deepest_offset_seen is a high-water mark that never decays, so one malformed frame during bring-up latches it forever — a real design should make it clear-on-read or window it. non_ip_ppm counts frames rather than octets, and Section 5 established the two differ substantially. And the module cannot see the difference between reading a field and acting on it, which is Section 7's ledger's limitation inherited.

Production implication: the high-water mark's stickiness is a feature during verification and a nuisance in the field, and the resolution is to have both. A latching high-water register answers "has this design ever crossed the boundary", which is the verification question; a windowed one answers "is it crossing now", which is the operational question. Two 16-bit registers — 640 BCE, 0.002 datapaths — and they answer two questions that a single register answers badly. The general habit is Chapter 19.7 §6's argument about clear-on-read against sticky semantics, arriving at a counter whose subject is the design's own behaviour rather than the traffic's.


16. RTL 8 — The Boundary Conformance Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// layer_conformance -- the checks that hold a design to the layer it
// claims to be.
//
// Every check is about the design rather than about the traffic, which
// is what distinguishes a boundary monitor from a protocol monitor.
// ---------------------------------------------------------------------
module layer_conformance
  import layermyth_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  input  logic        claims_layer2_only,
  input  logic [15:0] deepest_offset_i,
  input  logic        tagged_i,
  input  logic        parse_incomplete_i,
  input  logic        verified_claimed_i,
  input  logic        hash_had_tuple_i,
  input  logic        hash_used_fallback_i,
  input  logic        payload_octet_used_i,
  input  logic        offload_declared_i,

  output logic        v_boundary_crossed_undeclared,
  output logic        v_unverifiable_claim,
  output logic        v_hash_without_fallback,
  output logic        v_payload_read_undeclared,
  output logic        v_claim_contradicted,
  output logic  [4:0] violations,
  output logic        conformant
);
  logic [15:0] mac_limit;

  always_comb begin
    mac_limit = tagged_i ? 16'(MAC_LAST_OFF_TAGGED)
                         : 16'(MAC_LAST_OFF_UNTAGGED);

    // 1. Prohibition 1 -- crossing is allowed and must be declared.
    v_boundary_crossed_undeclared = (deepest_offset_i > mac_limit)
                                 && !offload_declared_i;

    // 2. Prohibition 2 -- Section 12's silent receive corruption.
    v_unverifiable_claim = parse_incomplete_i && verified_claimed_i;

    // 3. Prohibition 3 -- a hash with no tuple must fall back.
    v_hash_without_fallback = !hash_had_tuple_i && !hash_used_fallback_i;

    // 4. Chapter 2.4 Section 7's contract: the payload is moved, not read.
    v_payload_read_undeclared = payload_octet_used_i && !offload_declared_i;

    // 5. And the claim must match the evidence.
    v_claim_contradicted = claims_layer2_only
                        && (deepest_offset_i > mac_limit);

    violations = { v_claim_contradicted, v_payload_read_undeclared,
                   v_hash_without_fallback, v_unverifiable_claim,
                   v_boundary_crossed_undeclared };
    conformant = (violations == 5'b00000);
  end
endmodule

Classification: five checks whose subject is the design's own layering rather than the protocol's correctness.

What it teaches: that a boundary monitor is a different object from a protocol monitor, and that the difference is what it quantifies over. Chapter 19.1 §16's monitor checks a MAC against the frame format; this one checks a MAC against a claim about which layer it is. Every signal it consumes is a fact about the design, and none of them is in the frame.

And it teaches that v_boundary_crossed_undeclared makes crossing legal and undeclared crossing illegal, which is the correct policy. Chapter 2.4 §9 called offload the deliberate violation; the monitor enforces the word deliberate by requiring offload_declared_i, which is a configuration bit an architect sets rather than something the datapath can assert for itself.

Deliberately simplified: offload_declared_i is an input the monitor trusts, so a design that sets it unconditionally passes every check — which makes the monitor a documentation aid rather than a guarantee, and there is no way around that at this level. v_hash_without_fallback assumes one hash, where a real part has several for different purposes. And the monitor has no check for Section 13's stacked-tag depth attack, because depth is a property of a sequence and this is combinational.

Production implication: offload_declared_i being trusted is the reason this monitor belongs in a design review rather than only in a regression. Its value is that it forces somebody to set a bit, and setting a bit means a name is attached to a decision that otherwise happens implicitly in a classifier. Teams that run it find between one and three undeclared crossings in a mature design — almost always a hash, a filter or a statistics counter that reaches one field further than anybody remembered — and each one is either legitimate and should be declared, or a bug. The exercise costs an afternoon and is the cheapest thing in this chapter.


17. The Two Layers, Priced Side by Side

Four diagnostic questions and the two answers each admits. First, what octet offset does this fact come from: below fourteen, or twenty two when tagged, it is a media access control fact and travels with the link; above it, somebody crossed the boundary. Second, would this fact still be true after a router: media access control addresses are rewritten at every hop and Internet Protocol addresses are not, so a check that does not distinguish them means two different things on the two sides of a router. Third, what would change if the frame carried something else: for a media access controller, nothing; for anything that crossed, a list, and that list is the design's assumption about the traffic made explicit. Fourth, can this check ship: a check requiring a transmission control protocol parser lives in a testbench forever, so the fault class it targets appears exactly where it cannot run.A fact about aframewhich layer?Which offset?0–21, or aboveSurvives a router?MAC no, IP yesIf the payloadchanged?nothing, or a listCan the checkship?yes, or a testbenchforeverA layer-2 fact18 octets, 3 520 BCEA crossingup to 216 oct, 9 545×Declare itbound it, report it12
Figure 4 — four questions that locate any fact on the boundary, and the answers that separate a layer-2 design from one that crossed.

Everything this chapter derived, in one table.

QuantityA pure MACA design that crossesRatio
octets parsed, maximum frame18 — 1.19%up to 216 — 14.2%12×
highest offset branched on13, or 21 tagged21510×
parse window held3 520 BCE34 560 BCE9.8×
connection state03.356 × 10⁷ BCE — 118.5 datapaths
total3 520 BCE3.359 × 10⁷ BCE9 545×
share of Chapter 23.4's NIC0.007%66.2%
protocols it can carry64 000the ones it parses
failure when the assumption breaksnone — there is noneSection 12's five

A pure MAC costs 3 520 bits of parse window and can carry sixty-four thousand protocols. A design that crosses the boundary costs nine and a half thousand times that and works for the ones it was taught.

And the myth's three numbers, restated.

What the myth predictsWhat is true
what a MAC readsthe packet18 octets — 1.19% of a maximum frame
what Ethernet carriesIP64 000 protocol identifiers; IP is 2, and 11 of 13 common ones carry none
what a NIC isan Ethernet interface66.2% transport connection state

18. What the Correction Assumes

Eight assumptions, each with its direction of failure.

#AssumptionIf it is false
1a MAC parses 18 octetsa design that also parses the VLAN tag parses 22; the argument is unchanged
2type values are 1 536 and aboveChapter 5.5's encoding; the 64 000 figure follows from it
3thirteen common EtherTypesa count from ordinary deployments; the ratio moves and the conclusion does not
4IPv4 options are up to 40 octetsthe offload window shrinks proportionally if a design refuses them
5Chapter 23.4's NIC is 66.2% context cachethat chapter's figure; a part without TCP offload is 0%
6a 16 384-entry context cachelinear — 4 096 entries is 8.39 × 10⁶ BCE and a lower cliff
71 480 octets per IPv4 fragmenta 1 500 MTU less a 20-octet header; options reduce it
8BCE appliesSection 19 examines it; it holds for the state and not for the checksum logic

Assumption 5 is the one that carries the chapter's headline and it deserves the caveat. A NIC without TCP offload has no context cache at all, so its whole area is below the boundary and the 66.2% figure is zero. That is not a counterexample; it is the chapter's point made the other way — the 66.2% is what crossing costs, and a part that does not cross does not pay it.


19. The Cost, Accounted — in BCE

This chapter's blocks.

BlockFlopsBCE× the datapath
parse_window_model326400.002
ethertype_demux3366 7200.024
opacity_ledger1122 2400.008
mtu_coupling_model326400.002
offload_parser, 134-octet window1 072 + 4022 2400.078
tunnel_depth_limiter961 9200.007
layer_telemetry4008 0000.028
layer_conformance0 — combinational00
this chapter's additions2 12042 4000.150

offload_parser is 52.5% of the total and it is the block that crosses the boundary, which is the chapter's structure showing through: the expensive thing about believing the myth is holding the octets you would not otherwise hold.

And the designs the blocks describe.

BCE× the datapath
a pure MAC's parse window, tagged3 5200.012
an offload parser's, with a tunnel34 5600.122
Chapter 23.4's connection context cache3.356 × 10⁷118.5
Chapter 23.4's whole NIC5.070 × 10⁷179
Chapter 19.7 §19's MAC receive datapath283 3201

20. Properties Worth Asserting, and One Worth Refusing

Fifty-one properties in six groups, and the refused one is the natural way to verify an offload.

Group A — the parse window (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A1. A MAC's window is 14 octets, or 22 with a tag.
p_pw_mac_window: assert property (@(posedge clk) disable iff (!rst_n)
  !crosses_boundary |-> (window_octets == 16'(mac_window(tagged))));

// A2. And its highest branch offset is 13 or 21.
p_pw_highest_offset: assert property (@(posedge clk) disable iff (!rst_n)
  !crosses_boundary |->
    (highest_offset_branched_on == (tagged ? 16'd21 : 16'd13)));

// A3. Crossing the boundary is the only way the window grows.
p_pw_growth_needs_crossing: assert property (@(posedge clk) disable iff (!rst_n)
  (growth_x10 > 16'd10) |-> crosses_boundary);

// A4. Opacity and payload reading are exclusive.
p_pw_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
  (mac_remains_opaque != payload_is_read));

// A5. The window is charged as registers.
p_pw_registers: assert property (@(posedge clk) disable iff (!rst_n)
  (window_bce_o == 32'(window_octets) * 32'd8 * 32'(BCE_PER_FLOP)));

// A6. The parsed share falls as the frame grows.
p_pw_share_falls: assert property (@(posedge clk) disable iff (!rst_n)
  (frame_octets > $past(frame_octets)) |->
    (parsed_ppm_o <= $past(parsed_ppm_o)));

// A7. Eighteen octets, whatever the frame size.
p_pw_eighteen: assert property (@(posedge clk) disable iff (!rst_n)
  (MAC_PARSED == 18));

// A8. Each tunnel level adds a fixed amount.
p_pw_tunnel_linear: assert property (@(posedge clk) disable iff (!rst_n)
  (crosses_boundary && (tunnel_levels == 16'd1)) |->
    (window_octets == 16'(offload_window(0)) + 16'd82));

// A9. A pure MAC never reads a payload octet.
p_pw_no_payload: assert property (@(posedge clk) disable iff (!rst_n)
  mac_remains_opaque |-> !payload_is_read);

Group B — the EtherType space (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// B1. The type space is 64 000 values.
p_et_space: assert property (@(posedge clk) disable iff (!rst_n)
  (type_space_size == 32'd64_000));

// B2. A value at or below 1500 is a length, not a type.
p_et_length_encoding: assert property (@(posedge clk) disable iff (!rst_n)
  (ethertype <= 16'd1500) |-> is_length_field);

// B3. IP is exactly two of them.
p_et_ip_is_two: assert property (@(posedge clk) disable iff (!rst_n)
  is_ip |-> ((ethertype == 16'(ET_IPV4)) || (ethertype == 16'(ET_IPV6))));

// B4. The four classifications are mutually exclusive.
p_et_exclusive: assert property (@(posedge clk) disable iff (!rst_n)
  type_valid |-> ($onehot({is_length_field, is_ip,
                           is_known_non_ip, is_unknown})));

// B5. An unknown type goes to no consumer.
p_et_unknown_discarded: assert property (@(posedge clk) disable iff (!rst_n)
  is_unknown |-> (consumer_id == 4'd0));

// B6. Frames counted equals IP plus non-IP.
p_et_counts_partition: assert property (@(posedge clk) disable iff (!rst_n)
  (c_frames == c_ip_frames + c_non_ip_frames));

// B7. PTP is not IP.
p_et_ptp_not_ip: assert property (@(posedge clk) disable iff (!rst_n)
  (type_valid && (ethertype == 16'(ET_PTP))) |-> !is_ip);

// B8. And neither is RoCEv1.
p_et_roce_not_ip: assert property (@(posedge clk) disable iff (!rst_n)
  (type_valid && (ethertype == 16'(ET_ROCE))) |-> !is_ip);

// B9. The octet share and the frame share are independent.
p_et_shares_independent: assert property (@(posedge clk) disable iff (!rst_n)
  (c_frames > 48'd1000) |-> (ip_share_ppm <= 32'd1_000_000) &&
                            (ip_octet_share_ppm <= 32'd1_000_000));

Group C — opacity (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// C1. Any content fact means the boundary was crossed.
p_op_crossing_def: assert property (@(posedge clk) disable iff (!rst_n)
  (content_facts != 4'b0000) |-> boundary_crossed);

// C2. Container facts alone do not cross it.
p_op_container_safe: assert property (@(posedge clk) disable iff (!rst_n)
  ((content_facts == 4'b0000) && (container_facts != 4'b0000))
    |-> !boundary_crossed);

// C3. The deepest layer is 2 when nothing above was read.
p_op_layer2: assert property (@(posedge clk) disable iff (!rst_n)
  !boundary_crossed |-> (deepest_layer == 16'd2));

// C4. A layer-4 fact implies layer 4 or above is reported.
p_op_layer4: assert property (@(posedge clk) disable iff (!rst_n)
  (fact_l4_ports || fact_tcp_flags) |-> (deepest_layer >= 16'd4));

// C5. Every frame lands in exactly one counter.
p_op_one_counter: assert property (@(posedge clk) disable iff (!rst_n)
  frame_valid |=> ((c_container_only + c_content_read) ==
                   ($past(c_container_only) + $past(c_content_read) + 48'd1)));

// C6. Crossing share is bounded.
p_op_share_bounded: assert property (@(posedge clk) disable iff (!rst_n)
  (crossing_ppm <= 32'd1_000_000));

// C7. A design declaring layer 2 must not report a content fact.
p_op_claim_consistent: assert property (@(posedge clk) disable iff (!rst_n)
  claims_layer2_only |-> !v_claim_contradicted);

// C8. The ledger accounts for every fact it has an input for.
p_op_complete: assert property (@(posedge clk) disable iff (!rst_n)
  (ledger_is_complete == 1'b1));

Group D — the MTU coupling (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// D1. Fragments round up.
p_mt_round_up: assert property (@(posedge clk) disable iff (!rst_n)
  ((fragments * fragment_payload) >= datagram_octets));

// D2. A datagram within the MTU is one fragment.
p_mt_one_fragment: assert property (@(posedge clk) disable iff (!rst_n)
  (datagram_octets <= fragment_payload) |-> (fragments == 16'd1));

// D3. Loss amplification equals the fragment count.
p_mt_amplification: assert property (@(posedge clk) disable iff (!rst_n)
  (loss_amplification == fragments));

// D4. And it multiplies the drop rate.
p_mt_effective_loss: assert property (@(posedge clk) disable iff (!rst_n)
  (effective_loss_ppm == 32'(drop_rate_ppm) * 32'(fragments)));

// D5. The black hole needs all three conditions.
p_mt_black_hole: assert property (@(posedge clk) disable iff (!rst_n)
  black_hole |-> (dont_fragment && !icmp_reaches_sender &&
                  (datagram_octets > fragment_payload)));

// D6. The connection establishes regardless -- that is the trap.
p_mt_establishes: assert property (@(posedge clk) disable iff (!rst_n)
  (connection_establishes == 1'b1));

// D7. Bulk transfer fails exactly when the black hole is present.
p_mt_bulk_fails: assert property (@(posedge clk) disable iff (!rst_n)
  (bulk_transfer_fails == black_hole));

// D8. Fragmenting always costs wire octets.
p_mt_penalty_positive: assert property (@(posedge clk) disable iff (!rst_n)
  (fragments > 16'd1) |-> (wire_octets_frag > wire_octets_whole));

Group E — the offload parser and the limiter (9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// E1. A VLAN tag moves the layer-3 offset by four.
p_of_vlan_offset: assert property (@(posedge clk) disable iff (!rst_n)
  vlan_present |-> (l3_offset == 16'd18));

// E2. The layer-4 offset depends on a field inside the layer-3 header.
p_of_ihl_dependency: assert property (@(posedge clk) disable iff (!rst_n)
  is_ipv4 |-> (l4_offset == l3_offset + (16'(ihl) * 16'd4)));

// E3. A checksum is computed only when its bounds were located.
p_of_checksum_gated: assert property (@(posedge clk) disable iff (!rst_n)
  checksum_may_be_computed |-> (l4_offset_valid && !parse_incomplete));

// E4. And never when the parse was incomplete.
p_of_no_claim_incomplete: assert property (@(posedge clk) disable iff (!rst_n)
  parse_incomplete |-> !checksum_may_be_computed);

// E5. The window is finite and the check says so.
p_of_window_finite: assert property (@(posedge clk) disable iff (!rst_n)
  ((l4_offset + 16'd20) > 16'(WINDOW_OCTETS)) |-> parse_incomplete);

// E6. Depth and octets are independent limits.
p_of_two_limits: assert property (@(posedge clk) disable iff (!rst_n)
  stop_parsing |-> (depth_exceeded || octets_exceeded));

// E7. A design that stopped may not claim verification.
p_of_no_claim_stopped: assert property (@(posedge clk) disable iff (!rst_n)
  stop_parsing |-> !may_claim_verified);

// E8. Depth resets at each frame.
p_of_depth_resets: assert property (@(posedge clk) disable iff (!rst_n)
  frame_start |=> (depth == 16'd0));

// E9. Octets consumed never decrease within a frame.
p_of_octets_monotone: assert property (@(posedge clk) disable iff (!rst_n)
  (!frame_start && header_seen) |=>
    (octets_consumed >= $past(octets_consumed)));

Group F — telemetry and conformance (8).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// F1. The counter that must always read zero.
p_tl_no_unverifiable: assert property (@(posedge clk) disable iff (!rst_n)
  (c_unverifiable_claims == 48'd0));

// F2. A design that never branched above 21 is layer 2.
p_tl_layer2_evidence: assert property (@(posedge clk) disable iff (!rst_n)
  (deepest_offset_seen <= 16'd21) |-> design_is_layer2_only);

// F3. The high-water mark never falls.
p_tl_high_water: assert property (@(posedge clk) disable iff (!rst_n)
  (deepest_offset_seen >= $past(deepest_offset_seen)));

// F4. Frames partition into IP and non-IP.
p_tl_partition: assert property (@(posedge clk) disable iff (!rst_n)
  (c_frames == c_ip + c_non_ip));

// F5. An undeclared crossing is a violation.
p_cf_undeclared: assert property (@(posedge clk) disable iff (!rst_n)
  ((deepest_offset_i > (tagged_i ? 16'd21 : 16'd13)) && !offload_declared_i)
    |-> v_boundary_crossed_undeclared);

// F6. A hash with no tuple must fall back.
p_cf_hash_fallback: assert property (@(posedge clk) disable iff (!rst_n)
  !hash_had_tuple_i |-> hash_used_fallback_i);

// F7. A payload octet used without declaration is a violation.
p_cf_payload_declared: assert property (@(posedge clk) disable iff (!rst_n)
  payload_octet_used_i |-> offload_declared_i);

// F8. Conformance is the disjunction of its five checks.
p_cf_vector: assert property (@(posedge clk) disable iff (!rst_n)
  conformant |-> (violations == 5'b00000));

Coverage — the traffic a generator that believes the myth will never produce.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_et_ptp:          cover property (@(posedge clk) type_valid && (ethertype == 16'(ET_PTP)));
c_et_lldp:         cover property (@(posedge clk) type_valid && (ethertype == 16'(ET_LLDP)));
c_et_macsec:       cover property (@(posedge clk) type_valid && (ethertype == 16'(ET_MACSEC)));
c_et_roce:         cover property (@(posedge clk) type_valid && (ethertype == 16'(ET_ROCE)));
c_et_length:       cover property (@(posedge clk) type_valid && is_length_field);
c_et_unknown:      cover property (@(posedge clk) is_unknown);
c_of_ipv4_options: cover property (@(posedge clk) is_ipv4 && (ihl > 4'd5));
c_of_incomplete:   cover property (@(posedge clk) parse_incomplete);
c_of_tunnel:       cover property (@(posedge clk) depth >= 16'd1);
c_of_depth_limit:  cover property (@(posedge clk) depth_exceeded);
c_of_octet_limit:  cover property (@(posedge clk) octets_exceeded);
c_mt_fragmented:   cover property (@(posedge clk) fragments > 16'd1);
c_mt_black_hole:   cover property (@(posedge clk) black_hole);
c_cf_undeclared:   cover property (@(posedge clk) v_boundary_crossed_undeclared);

21. Verification Scenarios

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

Group 1 — the parse window (10).

#ScenarioExpect
1a MAC, untaggedwindow 14; highest offset 13; 3 520 BCE at 22 octets tagged
2a MAC, taggedwindow 22; highest offset 21
3a 64-octet frame18 parsed — 281 250 ppm, 28.12%
4a 1 518-octet frame11 857 ppm — 1.19%
5a 9 018-octet frame1 995 ppm — 0.20%
6crossing, no tunnel134 octets; 21 440 BCE; 6.1× growth
7crossing, one tunnel216 octets; 34 560 BCE; 9.8× growth
8crossing, two tunnels298 octets; 47 680 BCE
9mac_remains_opaque and payload_is_readalways opposite — p_pw_exclusive
10growth above 1.0× without crossingimpossible — p_pw_growth_needs_crossing

Group 2 — the EtherType space (10).

#ScenarioExpect
11type_space_size64 000
12EtherType 0x05DC — 1 500is_length_field; the 802.3 LLC path
130x0800is_ip; consumer 1
140x86DDis_ip
150x88F7 — PTPis_known_non_ipp_et_ptp_not_ip
160x8915 — RoCEv1is_known_non_ip
170x88CC — LLDPis_known_non_ip
180x9000 — unregisteredis_unknown; consumer 0; discarded
19a port carrying 92% IP framesip_share_ppm = 920 000
20the same port, by octetsa different number — small non-IP frames

Group 3 — opacity (9).

#ScenarioExpect
21length, FCS, DA match, type onlyboundary_crossed low; deepest_layer 2
22plus an L3 protocol factcrossed; layer 3
23plus L4 portslayer 4
24plus a payload bytelayer 7
25a design claiming layer 2 with layer-4 factsv_claim_contradicted
261 000 frames, 100 crossingscrossing_ppm = 100 000
27a hash reading the five-tuplecrossed — and legitimate, if declared
28a drop on a TCP flagcrossed — a policy engine at the wrong layer
29scenarios 27 and 28 comparedidentical in this module; a fact_acted_on bit separates them

Group 4 — the MTU coupling (10).

#ScenarioExpect
30a 9 000-octet datagram, 1 500 MTU7 fragments; 9 386 wire octets against 9 038
31the same, penalty3.85%
32the same, loss amplification
3310⁻³ drop rate, 7 fragmentseffective 7 × 10⁻³
34a 1 400-octet datagram, 1 500 MTU1 fragment; no penalty
35a 9 000-octet datagram, 9 018 MTU1 fragment
36DF set, oversized, ICMP reaches the senderno black hole; the sender reduces its segment size
37DF set, oversized, ICMP filteredblack_hole; c_black_holes increments
38the same, connection establishmentsucceeds — p_mt_establishes
39the same, bulk transferhangs — bulk_transfer_fails

Group 5 — the offload parser (9).

#ScenarioExpect
40untagged IPv4, no optionsl3 at 14; l4 at 34; complete
41tagged IPv4, no optionsl3 at 18; l4 at 38
42IPv4 with ihl = 15l4 at 74; within a 134-octet window
43one VXLAN-style tunnel216 octets; within a 216-octet window
44two tunnels, 134-octet windowparse_incomplete; no checksum claim
45three stacked VLAN tags, depth limit 2octets_exceeded fires, depth_exceeded does not
46parse_incomplete with a verified claimv_unverifiable_claim; c_unverifiable_claims increments
47may_claim_verified after stop_parsinglow — p_of_no_claim_stopped
48frame start after a deep parsedepth and octets reset

Group 6 — telemetry and conformance (10).

#ScenarioExpect
49a design that never branches above 21design_is_layer2_only high
50one frame parsed to octet 134high-water latches at 134 and never falls
51c_unverifiable_claims at any pointmust be zero — p_tl_no_unverifiable
52a hash with no tuple and no fallbackv_hash_without_fallback
53the same with a fallback to MAC and typeclean
54a payload octet used, offload undeclaredv_payload_read_undeclared
55crossing at offset 40, declaredclean — crossing is legal, undeclared crossing is not
56crossing at offset 40, undeclaredv_boundary_crossed_undeclared
578% non-IP frames on an 8-member LAG, no fallbackmember 0 takes 19.5% against 11.5% — 70% imbalance
58all five checks clearconformant high

22. Debugging Across the Boundary

Six symptoms, and the first question in every row is which layer is this fact from?

SymptomFirst questionWhere to look
one link of a LAG carrying twice its sharedoes the hash fall back when there is no tuple?Section 12 — 8% non-IP is 70% imbalance
an application that connects and then hangsis DF set and is ICMP reaching the sender?Section 9 — a path MTU black hole
corrupted data on one traffic class onlydoes that class carry IPv4 options or a second tag?Section 12 — a checksum over the wrong range
PTP accuracy far worse than Chapter 16.5's limitsis PTP being classified as ordinary traffic?Section 5 — EtherType 0x88F7, not IP
a "layer-2 device" that behaves differently on IPv6what does deepest_offset_seen read?Section 15 — the claim is contradicted by the evidence
a regression that is green and a field failure that is notwhat EtherTypes does the generator produce?Section 21's directed test

Row six is the one this chapter exists for, and the procedure is short: list the EtherTypes the generator can emit, list the EtherTypes the deployment carries, and compare the two lists. The difference is the design's untested surface, and on a typical programme it is eleven of thirteen.


23. Misconceptions

Misconception 1 — "an Ethernet interface is a TCP/IP interface."

The wrong model: the terms describe the same thing at different levels of formality.

What it costs: a design whose filters, hashes and offloads assume fields that are not present. Section 2: a receive MAC parses eighteen octets of a 1 518-octet frame — 1.19% — and reads zero octets of the IP and TCP headers. Section 4: Ethernet names 64 000 payload protocols and IP is two of them.

The corrected model: the MAC reads the container and moves the contents. Chapter 2.4 §7's opaque payload path is the structural form of that, and Section 10 prices what crossing it costs: 66.2% of a real NIC.

Misconception 2 — "non-IP traffic is a corner case."

The wrong model: ARP and a little management traffic, and otherwise it is all IP.

What it costs: eleven of thirteen common EtherTypes untested. Section 4: PTP, LLDP, MACsec, LACP, FCoE, RoCEv1, PROFINET and EtherCAT all carry no IP octet, and several are the protocols with the tightest requirements — PTP skips IP precisely because Chapter 16.4's servo must otherwise estimate an IP hop's variable delay.

The corrected model: the most demanding users of Ethernet deliberately chose not to use IP, and LLDP's correctness actively depends on not being routable.

Misconception 3 — "the source address identifies the sender."

The wrong model: there is one source address and it names who sent the frame.

What it costs: a property that means two different things, and an access list that sees one address for the entire internet. Section 2: there are two source addresses; the MAC one is rewritten at every router and the IP one is not. A host receiving traffic from four routers away sees one source MAC — the last router's.

The corrected model: ask whether the fact survives a router. MAC addresses do not and IP addresses do, and a property that does not distinguish them is ambiguous in a way that changes its truth value.

Misconception 4 — "the layers are completely independent."

The wrong model: having established that they are separate, conclude that nothing couples them.

What it costs: a fragmentation surprise. Section 8: 1 500 is an Ethernet number that became an IP number and then a TCP number, and breaking the coupling costs a seven-fold loss amplification for a 9 000-octet datagram over a 1 500-octet MTU — 3.85% of efficiency and 7× of loss.

The corrected model: exactly one thing couples them, and it is the MTU. The mechanism that hides the coupling is path MTU discovery, and it fails by hanging after a successful connection, which is the worst-behaved failure in networking.

Misconception 5 — "checksum offload is free."

The wrong model: the hardware computes a checksum instead of the CPU; nothing else changes.

What it costs: a 6.1× parser window untunnelled and 9.8× with one tunnel, plus an unbounded case with no correct fixed answer. Section 12: on receive, a design that reports "verified" over a range it could not locate hands the host corrupted data with a clean flag, and no counter anywhere reports it.

The corrected model: crossing the boundary is legitimate and must be declared, bounded and reported. parse_incomplete in the descriptor is one bit, and it is the difference between a graceful degradation and a silent corruption.

Misconception 6 — "our regression is green, so the offload is verified."

The wrong model: full coverage over the fields the generator produces is full coverage.

What it costs: Section 21's directed test. A generator written by somebody who holds the myth produces IP traffic; the coverage model has the same blind spot; and the design, the generator and the model agree with each other perfectly. One PTP announce frame with a VLAN tag distinguishes a correct design from a common one.

The corrected model: list the EtherTypes the generator can emit and the EtherTypes the deployment carries, and compare. The difference is the untested surface, and Section 20's class 115 says why the property that would have caught it cannot ship.


24. Interview Questions

Six, with what a strong answer contains.

1. How many octets of a frame does a receive MAC actually read?

Eighteen: six destination, six source, two type or length, and four of frame check sequence. A strong answer converts that to a share — 1.19% of a 1 518-octet frame, 28.12% of a minimum one — and observes that the count does not grow with the frame. The best answers name the offsets: the MAC's last parsed octet is 13, or 21 with a VLAN tag, and the IPv4 header begins at 14 or 22. There is no overlap.

2. What else does Ethernet carry?

Sixty-four thousand possible protocol identifiers — type values 1 536 through 65 535 — of which IPv4 and IPv6 are two. A strong answer names the ones a real port sees: ARP, VLAN, LACP, PPPoE, PROFINET, EtherCAT, LLDP, MACsec, PTP, FCoE, RoCEv1. The best answers make the sharp point: PTP's layer-2 mapping exists because an IP hop adds the variable delay Chapter 16.4's servo would otherwise have to estimate, and LLDP's correctness depends on not being routable.

3. Somebody writes assert property (frame_accepted |-> learned_port[source_address] == this_port). What is wrong with it?

source_address is ambiguous. With the layer-2 reading it is Chapter 12.2's learning property and correct; with the layer-3 reading it is false on any frame that has crossed a router, because the source IP names a station that is not on this link. A strong answer gives the diagnostic: ask whether the fact survives a router.

4. What does a NIC's TCP offload actually cost?

Two things: a parser window and connection state. The window grows from 22 octets to 134 untunnelled — 6.1× — and to 216 with one tunnel, 9.8×. The state is Chapter 23.4's context cache: 3.356 × 10⁷ BCE, 118.5 MAC datapaths, 66.2% of the part. A strong answer adds that it is worth it — the alternative is tens of host cores — and that what makes it safe is declaring it, because Chapter 23.4 §12's rule stripes ≤ cache entries ÷ participants is a layer-2 configuration constraint imposed by a layer-4 cache.

5. Where do the layers genuinely touch?

The MTU, and nowhere else. 1 500 is an Ethernet number — a transmission-time bound on a shared medium — and it became the IP MTU and then the TCP maximum segment size. A strong answer prices the coupling: a 9 000-octet datagram over a 1 500-octet MTU is seven fragments, 3.85% of efficiency and a seven-fold loss amplification, because every fragment must arrive. The best answers describe the black hole: DF set, ICMP filtered, the connection establishes and bulk transfer hangs forever.

6. Why can't you verify a checksum offload with a hardware monitor?

Because checking it requires parsing TCP, and the design is specified not to. A strong answer names the consequence rather than the fact: the check lives in a testbench permanently, so it can never become an emulation assertion, a bring-up check or a field diagnostic — and the fault class it targets appears in the field, when real traffic carries an IPv4 option or a second tag. The best answers give the repair: assert the design's honesty in hardware — a claim requires a located range — and check the value against the host's own software computation on a sample, which is a genuine second observer rather than a second copy of the design team's reading.


25. Questions and Answers


26. What's Next

This chapter took the largest of Module 25's myths and killed it with three numbers: eighteen octets, sixty-four thousand EtherTypes, and 66.2% of a NIC.

Chapter 25.2 takes the second — that MAC addresses are globally unique forever — and the correction is not the one most engineers expect. Chapter 5.3 §4 already did the birthday arithmetic over the 46 free bits of a locally administered address and found one collision in 140 deployments at a million addresses, which sounds like a settled question.

It is not, because the 46 bits are a fiction. A hypervisor's generator fixes the first octet and usually a vendor prefix too, leaving 24 free bits — and the birthday bound over 2²⁴ at ten thousand virtual interfaces is not one in a million, it is better than even. The chapter derives the factor between the arithmetic people quote and the arithmetic their software implements, prices the duplicate detection a switch would need against Chapter 23.3's MAC table, and finds that the evidence a duplicate leaves is destroyed by the structure that would have recorded it.

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.