Skip to content
VLSI Mentor

Ethernet · Module 5

Payload, Padding and Minimum Frame Size

The 64-octet minimum is a round-trip propagation time across a shared segment, converted to bits — a constraint whose mechanism full duplex removed and whose cost every frame still pays, in padding that only the length field distinguishes from data.

Chapter 5.5 §7 reconciled a declared length against a received octet count and found the two are not expected to be equal. The excess is padding, and the check treated padding as explicable only up to a floor — a floor it took as a parameter and did not justify.

This chapter justifies it, and the justification comes from outside the frame format entirely.

The number is 64 octets, and nothing about frame structure produces it. It is not derived from address size, or from a useful payload minimum, or from any property of the data being carried. It is a distance, converted into time, converted into bits.

A station on a shared medium had to still be transmitting when the far end's collision signal came back, or it would finish, believe it had succeeded, and never learn otherwise. That round trip across a maximum-length segment — Chapter 1.2's slot time — sets a minimum transmission duration, and a minimum duration at a fixed rate is a minimum size.

That mechanism is gone. Chapter 1.5 removed collisions from the modern link entirely. The floor stayed.

1. Scope — What This Chapter Owns

Chapter 1.2 owns the derivation of slot time — why a station must transmit for a round trip, how segment length and repeater count enter, and why the answer came out at 512 bit times. This chapter takes that number as given and follows what it did to the frame.

Chapter 5.5 owns the length/type field and built the check that compares a declared length against a received count. It named the floor and deferred it.

This chapter owns the floor itself and everything downstream of it: where 64 comes from, why full-duplex links still honour it, how padding is generated and removed, what a receiver can and cannot conclude from a short frame, how a tag changes the floor, and what the pad costs measured as a fraction of a link's capacity.

It does not own: the maximum frame size, which is a separate argument with separate consequences — Chapter 5.7 takes it. Nor the FCS that follows the pad, which Chapter 5.8 takes. Nor the interframe gap, which is the other inherited quantity and belongs to Chapter 5.9.

The question this chapter answers that its neighbours do not: why does a frame carrying four octets of data occupy sixty-four, and what must a receiver do so that the other sixty are not mistaken for content?

2. Where 64 Comes From, and Why It Is Not a Frame-Format Number

Work the derivation in the order it actually happened, because the order is what shows that the frame format was the output.

Start with a distance. A shared segment has a maximum end-to-end extent, fixed by signal attenuation and by the number of repeaters permitted between any two stations.

Convert it to a time. A signal takes some propagation delay to cross that distance, and a collision fragment takes the same time to come back. The worst case is a station at one end and a station at the other, so the quantity that matters is the round trip, plus the delay each repeater adds in both directions.

Convert the time to bits. At a fixed signalling rate, a duration is a bit count. Chapter 1.2 shows this landing at 512 bit times with margin, and 512 bits is 64 octets.

Now, and only now, does the frame format enter. A transmitting station must still be sending when the fragment arrives, so its frame must be at least 64 octets long. The frame already carried 18 octets of unavoidable structure — two addresses at six octets each, the length/type field at two, and the frame check sequence at four. Subtract, and the client-data region has a floor of 46 octets.

3. Full Duplex Removed the Reason and Kept the Floor

Chapter 1.5 is unambiguous: on a full-duplex link there are no collisions, no deferral, no backoff, and no slot time. The mechanism that produced the floor does not exist.

The floor is still enforced, by both ends, on every frame. A transmitter pads short frames; a receiver treats a frame below 64 octets as invalid. Neither is defending against anything.

Three reasons it stayed, and only the third is a good one.

Interoperability of the frame, not the link. A frame does not stay on one link. It is received by a switch and forwarded onto another, and Chapter 2.7 established that forwarding does not rewrite the frame's contents. If any segment anywhere in the path might be half-duplex, a frame shorter than 64 octets can be created on a full-duplex link and become undetectable on a shared one. The floor is a property of the frame format, and the frame format is shared by links that do not share the property that motivated it.

Every receiver already implements it. Removing a minimum is not backward-compatible in the direction that matters: a new transmitter emitting 40-octet frames is talking to receivers that discard them as runts. The change would have to happen at every receiver first, which is the update-the-installed-base problem Chapter 5.5 §2 showed the length/type resolution deliberately avoiding.

And the good reason: shortness now means something else. A frame below the floor is no longer a collision fragment — but it is still, reliably, damage. Truncation in a buffer, a link that dropped mid-frame, a transmitter that terminated early. The floor stopped being a defence and became a detector, and Section 8's classifier is built on exactly that repurposing.

So the honest summary is that the constraint outlived its cause and acquired a new one. That is not unusual in long-lived standards, and it is worth recognising as a distinct situation from a constraint that is simply obsolete — because the second can be removed and the first cannot.

4. Padding Is Not Data, and One Field Separates Them

A minimum-size frame is shown as a sequence of regions. Six octets of destination address, six of source address, two of length or type field, then a client data region of forty six octets, then four octets of frame check sequence. Inside the client data region, only the first few octets are the client's data and the remainder is padding inserted by the transmitting media access control layer. The length field is the only thing that marks the boundary between the two.14 octetsaddresses and length/type46 octetsthe client-data region4 octetsframe check sequence64 octetsthe inherited floorClient datahowever much there wasPadinserted by the MAC —never deliveredThe length fieldthe only marker of theboundary12
Figure 1 — the same octets on the wire; only the length field says where the client's data stops.

On the wire the two regions are indistinguishable. There is no delimiter, no length prefix inside the payload, no change of encoding. The pad begins wherever the client's data stopped, and nothing in the octet stream marks the point.

So the boundary is carried out of band, in the length field, and this produces an asymmetry that catches designs out:

  • A length-form frame carries the boundary. The field says how many octets are the client's, the rest of the region is pad, and stripping is arithmetic.
  • A type-form frame does not. Chapter 5.5 showed the field holding a protocol identifier instead, which says nothing about extent. The MAC cannot strip the pad, because it does not know where it starts.

What happens in the second case is the part worth stating plainly: the pad is delivered. The MAC hands up the whole 46-octet region, and something above — the protocol identified by that very type field — has its own length field and truncates to it. IPv4 and IPv6 both carry one; that is how it works in practice.

Which means the layering is doing real work here and can be got wrong in one specific way. A MAC that tries to be helpful and strips a "probably pad" tail from a type-form frame is guessing, and Chapter 2.3's rule says it must not — it does not know the payload's format and therefore cannot know where the payload ends. The correct behaviour on a type-form frame is to deliver the pad and let the client discard it, which looks like sloppiness and is the only decision available to a layer that must not inspect what it carries.

5. RTL 1 — Generating the Pad Without Losing the Boundary

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Appends pad to reach the frame floor, and -- in the same act -- produces
// the length value that tells the far end where the client's data stopped.
//
// The two must come from ONE count. A design that computes the length in
// one place and the pad in another has two representations of the same
// boundary and no mechanism keeping them equal; they diverge on exactly
// the frames where it matters, which are the short ones.
package frame_size_pkg;
 
  // NORMATIVE. 512 bit times -- Chapter 1.2's slot time, expressed in
  // octets. Not a frame-format constant: a propagation measurement.
  localparam int unsigned MIN_FRAME_OCTETS = 64;
 
  // DA(6) + SA(6) + Length/Type(2) + FCS(4). Everything a frame carries
  // whatever its payload.
  localparam int unsigned FIXED_OVERHEAD   = 18;
 
  // 64 - 18. The floor on the client-data region, and the number that
  // makes a one-octet transfer cost forty-six.
  localparam int unsigned MIN_CLIENT       = MIN_FRAME_OCTETS - FIXED_OVERHEAD; // 46
 
endpackage
 
module pad_generator
  import frame_size_pkg::*;
#(
  // Value written into pad octets. A PARAMETER, deliberately -- Section 13
  // explains why a design must not assert this is zero even though almost
  // every implementation makes it zero.
  parameter logic [7:0] PAD_VALUE = 8'h00
) (
  input  logic        clk,
  input  logic        rst_n,
 
  input  logic        frame_start,
  // How many octets the client will supply. Known before transmission
  // because a MAC cannot begin a frame it cannot finish.
  input  logic [10:0] client_octets,
  input  logic        is_length_form,
 
  input  logic        cli_valid,
  input  logic [7:0]  cli_data,
  input  logic        cli_last,
 
  output logic        tx_valid,
  output logic [7:0]  tx_data,
  output logic        tx_last,
  output logic        tx_is_pad,          // for instrumentation only
 
  // The boundary, emitted with the frame. Meaningful only in length form.
  output logic        emit_length_valid,
  output logic [10:0] emit_length,
 
  output logic [10:0] pad_appended
);
 
  typedef enum logic [1:0] { P_IDLE, P_DATA, P_PAD, P_DONE } p_e;
  p_e          state_q;
  logic [10:0] sent_q;
  logic [10:0] target_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q           <= P_IDLE;
      sent_q            <= '0;
      target_q          <= '0;
      tx_valid          <= 1'b0;
      tx_data           <= '0;
      tx_last           <= 1'b0;
      tx_is_pad         <= 1'b0;
      emit_length_valid <= 1'b0;
      emit_length       <= '0;
      pad_appended      <= '0;
    end else begin
      tx_valid          <= 1'b0;
      tx_last           <= 1'b0;
      tx_is_pad         <= 1'b0;
      emit_length_valid <= 1'b0;
 
      case (state_q)
        P_IDLE: if (frame_start) begin
          state_q  <= P_DATA;
          sent_q   <= '0;
          // ONE decision, made once: how big the client-data region will
          // be. The pad count and the length value are both read off it.
          target_q <= (client_octets < 11'(MIN_CLIENT))
                        ? 11'(MIN_CLIENT) : client_octets;
          // The length is the CLIENT's count, never the target. Emitting
          // the target here is the single most damaging bug in this
          // module: the frame becomes self-consistent and the receiver
          // delivers 46 octets for a 3-octet transfer, with no error
          // anywhere.
          emit_length       <= client_octets;
          emit_length_valid <= is_length_form;
          pad_appended      <= (client_octets < 11'(MIN_CLIENT))
                                 ? (11'(MIN_CLIENT) - client_octets) : 11'd0;
        end
 
        P_DATA: if (cli_valid) begin
          tx_valid <= 1'b1;
          tx_data  <= cli_data;
          sent_q   <= sent_q + 1'b1;
          if (cli_last) begin
            if ((sent_q + 1'b1) >= target_q) begin
              tx_last <= 1'b1;
              state_q <= P_DONE;
            end else begin
              state_q <= P_PAD;
            end
          end
        end
 
        P_PAD: begin
          tx_valid  <= 1'b1;
          tx_data   <= PAD_VALUE;
          tx_is_pad <= 1'b1;
          sent_q    <= sent_q + 1'b1;
          if ((sent_q + 1'b1) >= target_q) begin
            tx_last <= 1'b1;
            state_q <= P_DONE;
          end
        end
 
        P_DONE: state_q <= P_IDLE;
        default: state_q <= P_IDLE;
      endcase
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that the length value and the pad count are two readings of one decision, and the decision is made once, at frame start, from client_octets. The failure this prevents is subtle because the resulting frame is internally consistent: emit target_q as the length instead of client_octets and the frame declares 46, carries 46, passes Chapter 5.5's extent check with zero pad, and delivers 43 octets of garbage to a peer that has no way to know.

Deliberately simplified: client_octets is known up front. A store-and-forward transmit path has it; a cut-through one does not and must either buffer to the floor before starting or defer the length until the count is known — which is one of several reasons cut-through transmit paths are harder than they look.

Production implication: tx_is_pad exists for Section 12's monitor and for nothing else — it is not a delivery qualifier. A downstream block that used it to suppress octets would be re-deriving the boundary from a signal instead of from the length field, which reintroduces the two-representations problem this module exists to avoid.

6. RTL 2 — Removing the Pad, and Refusing To Guess

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Strips pad on the receive side, and the module's real content is the
// case where it MUST NOT.
//
//   length form -> the boundary is carried. Strip arithmetically.
//   type form   -> the boundary is NOT carried. Deliver everything and
//                  say so, because guessing is worse than delivering.
//
// The temptation is to trim a run of trailing zeros in the type-form case.
// It is wrong twice: the pad is not required to be zero (Section 13), and
// a payload is entitled to END in zeros. A MAC that trims has silently
// truncated a client's data with no error and no counter.
module pad_stripper
  import frame_size_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,
 
  input  logic        frame_start,
  input  logic        is_length_form,
  input  logic [10:0] declared_length,
 
  input  logic        rx_valid,
  input  logic [7:0]  rx_data,
  input  logic        rx_last,
 
  output logic        cli_valid,
  output logic [7:0]  cli_data,
  output logic        cli_last,
 
  // High for the whole frame when the pad could not be removed here. The
  // client is being handed the full region and is expected to bound the
  // payload from its own header.
  output logic        pad_not_stripped,
  output logic [10:0] stripped_octets,
  // The frame declared more than it delivered: Chapter 5.5's short frame,
  // seen from the stripper's side.
  output logic        truncated
);
 
  logic [10:0] seen_q;
  logic        armed_q;
 
  // The one comparison that does the stripping. Note it is against the
  // DECLARED length, never against a pattern in the data.
  wire in_client_data = (seen_q < declared_length);
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      seen_q           <= '0;
      armed_q          <= 1'b0;
      cli_valid        <= 1'b0;
      cli_data         <= '0;
      cli_last         <= 1'b0;
      pad_not_stripped <= 1'b0;
      stripped_octets  <= '0;
      truncated        <= 1'b0;
    end else begin
      cli_valid <= 1'b0;
      cli_last  <= 1'b0;
      truncated <= 1'b0;
 
      if (frame_start) begin
        seen_q           <= '0;
        armed_q          <= is_length_form;
        pad_not_stripped <= !is_length_form;
        stripped_octets  <= '0;
      end else if (rx_valid) begin
        seen_q <= seen_q + 1'b1;
 
        if (!armed_q) begin
          // Type form: everything goes up, pad included, and the flag says
          // so. This is not a degraded mode -- it is the correct behaviour
          // for a layer that must not parse what it carries.
          cli_valid <= 1'b1;
          cli_data  <= rx_data;
          cli_last  <= rx_last;
        end else if (in_client_data) begin
          cli_valid <= 1'b1;
          cli_data  <= rx_data;
          // The client's last octet is the one at declared_length-1, which
          // is NOT the frame's last octet on a padded frame.
          cli_last  <= ((seen_q + 1'b1) == declared_length);
        end else begin
          stripped_octets <= stripped_octets + 1'b1;
        end
 
        if (rx_last && armed_q && ((seen_q + 1'b1) < declared_length))
          truncated <= 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that cli_last and rx_last are different events on a padded frame, and conflating them is the standard bug. The client's data ends at declared_length; the frame ends at the floor. A stripper that raises cli_last on rx_last delivers the pad with the data and marks the pad's final octet as the client's — so the client sees the right start, the wrong length, and no indication that anything went wrong.

Deliberately simplified: one octet per cycle. A wide datapath must handle a boundary that falls mid-word, which is a byte-enable problem rather than a new decision.

Production implication: pad_not_stripped is asserted for the entire frame rather than pulsed, because it describes a property of this frame's delivery contract, not an event within it. A client receiving a type-form frame needs to know before it processes the first octet that the tail is unreliable — and a pulse at frame end arrives after the client has already decided how much to keep.

7. What a Receiver Cannot Conclude From a Frame's Extent

It is worth stating the negative results directly, because each one is a conclusion designs draw anyway.

A 64-octet frame does not mean 46 octets of data. It means the client-data region was at least at the floor. Anywhere from 0 to 46 of those octets are the client's, and only the length field distinguishes them.

A frame larger than 64 octets does not mean it is unpadded. Padding lifts a frame to the floor, so any frame above it was already there — but that says nothing about a tagged frame, where Section 9 shows the floor moving.

A run of zeros at the end of a frame does not mean padding. The pad's value is not specified by the MAC standard (Section 13), and a payload may legitimately end in zeros. Both directions of this inference fail: zeros that are not pad, and pad that is not zeros.

A type-form frame's payload extent cannot be determined at this layer at all. Not approximately, not usually — the information is not present. Any answer a MAC produces is a guess about a format it is forbidden to parse.

And the one positive result, which is what Section 8 is built on: a frame below the floor is unambiguously wrong. Nothing conforming produces one. That is a rare thing in a receive path — a conclusion available from extent alone, with no field to consult and no interpretation to get wrong.

8. RTL 3 — Two Different Shortnesses, and the Bit That Separates Them

A frame can be short in two unrelated ways, and a single "runt" counter merges a link problem with a peer conformance problem.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// Three populations, three causes, three responses:
//
//   FRAGMENT      -- below the floor, check sequence BAD.
//                    Truncated in transit, or (on a shared medium) a
//                    collision fragment. A LINK problem.
//
//   UNDERSIZED    -- below the floor, check sequence GOOD.
//                    A peer emitted it that way and computed a correct
//                    check over it. Nothing damaged this frame; the
//                    transmitter did not pad. A PEER CONFORMANCE problem,
//                    and no amount of cable replacement will fix it.
//
//   SHORT-DECLARED-- at or above the floor, but the length field promises
//                    more client octets than arrived. Chapter 5.5's short
//                    frame: the frame is well formed and DISHONEST.
//
// A single counter over all three is the reason "we see runts" is a
// diagnosis nobody can act on.
module runt_classifier
  import frame_size_pkg::*;
#(
  parameter int unsigned CNT_W = 32
) (
  input  logic        clk,
  input  logic        rst_n,
  input  logic        clear,
 
  input  logic        frame_done,
  input  logic [13:0] frame_octets,      // whole frame, FCS included
  input  logic        fcs_ok,
  input  logic        is_length_form,
  input  logic [10:0] declared_length,
  input  logic [10:0] client_octets_rx,  // what the stripper actually saw
 
  output logic        is_fragment,
  output logic        is_undersized,
  output logic        is_short_declared,
 
  output logic [CNT_W-1:0] c_fragment,
  output logic [CNT_W-1:0] c_undersized,
  output logic [CNT_W-1:0] c_short_declared,
 
  // Smallest frame ever seen. Survives `clear`, because the extreme is a
  // property of what this link has carried, not of a measurement window.
  output logic [13:0]      smallest_seen,
  output logic             smallest_valid
);
 
  wire below_floor = (frame_octets < 14'(MIN_FRAME_OCTETS));
 
  function automatic logic [CNT_W-1:0] bump(input logic [CNT_W-1:0] v);
    bump = (&v) ? v : (v + 1'b1);
  endfunction
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      is_fragment       <= 1'b0;
      is_undersized     <= 1'b0;
      is_short_declared <= 1'b0;
      c_fragment        <= '0;
      c_undersized      <= '0;
      c_short_declared  <= '0;
      smallest_seen     <= '1;
      smallest_valid    <= 1'b0;
    end else begin
      is_fragment       <= 1'b0;
      is_undersized     <= 1'b0;
      is_short_declared <= 1'b0;
 
      if (clear) begin
        c_fragment       <= '0;
        c_undersized     <= '0;
        c_short_declared <= '0;
        // smallest_seen deliberately survives.
      end
 
      if (frame_done) begin
        if (!smallest_valid || (frame_octets < smallest_seen)) begin
          smallest_seen  <= frame_octets;
          smallest_valid <= 1'b1;
        end
 
        if (below_floor) begin
          // The check sequence is what splits these, and it is the only
          // thing that can: the two frames are otherwise identical.
          if (fcs_ok) begin
            is_undersized <= 1'b1;
            c_undersized  <= bump(c_undersized);
          end else begin
            is_fragment   <= 1'b1;
            c_fragment    <= bump(c_fragment);
          end
        end else if (is_length_form && (client_octets_rx < declared_length)) begin
          is_short_declared <= 1'b1;
          c_short_declared  <= bump(c_short_declared);
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that a correct check sequence over a malformed frame is diagnostic information, not a contradiction. The instinct is to treat any short frame as damage, and damage is what a bad check sequence means. A good one means the frame arrived exactly as sent — so the fault is at the sender, and every hour spent on cables, connectors and optics is wasted. The same octet count, split by one bit, sends the investigation to two different buildings.

Deliberately simplified: it consumes fcs_ok as an input. Where that signal comes from and precisely what it covers is Chapter 5.8, and the dependency is worth noticing — this classifier's central distinction is only available because a check exists that spans the whole frame.

Production implication: smallest_seen survives clear for the same reason Chapter 5.4's worst broadcast rate does. A counter answers how often; an extreme answers how bad, and the two are cleared on different schedules. A link whose smallest frame ever is 64 has never seen a runt; one whose smallest is 17 has a specific event to explain, and the count of similar events since the last clear says nothing about it.

9. A Tag Moves the Floor, and the Standard Gives Two Answers

Chapter 5.5 §8 showed a tag displacing the length/type field. It also changes the arithmetic in this chapter, in a way that produces a genuine interoperability trap.

The frame floor does not move: it is still 64 octets. But the fixed overhead grew by four, so the client-data floor drops from 46 to 42. A tagged frame with 42 octets of client data needs no padding at all; an untagged one with 42 needs four.

Three cases compared. An untagged frame has eighteen octets of fixed overhead and a client data floor of forty six octets, totalling sixty four. A tagged frame has twenty two octets of fixed overhead and a client data floor of forty two octets, still totalling sixty four. A tagged frame padded to sixty eight octets has forty six octets of client data, so that removing the four tag octets leaves a frame that is still at the sixty four octet floor and needs no repadding.Untagged18 overhead46 client floorpad up to here64 totalthe inherited floorTagged22 overhead42 client floorfour fewer64 totalunchangedTagged to 68the permitted option46 client flooras if untaggedPop the tag: 64no repadding neededSame floordifferent client budgets12
Figure 2 — the frame floor is constant, so adding a tag lowers the client-data floor; the 68-octet option makes tag removal safe.

Now follow a tagged 64-octet frame through a device that removes the tag. Four octets come out, and the frame is 60 — below the floor it must satisfy on the next link. The device has to pad it back up, which means re-running the pad logic on the egress side of a forwarding path, on a frame it did not originate.

The standard permits an alternative, and it is the interesting one. A transmitter emitting a tagged frame may pad it to 68 octets rather than 64 — keeping the client-data region at 46 as though the tag were not there — precisely so that removing the tag yields a 64-octet frame that needs no further work.

Read that as a design pattern rather than a special case. The cost is four octets on frames that are already the least efficient on the link. The purchase is that a downstream transformation becomes size-preserving at the floor, so the device performing it does not need pad logic at all. A small unconditional cost at the source, in exchange for removing a conditional behaviour from every device downstream — and conditional behaviours at a floor are exactly where interoperability failures live.

10. RTL 4 — The Floor as a Computed Quantity

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE.
//
// The client-data floor is not a constant once tags exist, so it must be
// COMPUTED per frame rather than parameterised per design.
//
// Two policies, and the parameter chooses:
//
//   FLOOR_POLICY_MIN  -- pad only to the 64-octet frame floor. Fewest
//                        octets on the wire; a downstream tag removal must
//                        repad.
//   FLOOR_POLICY_POP  -- pad as though the tags were absent (up to 68 for
//                        one tag). Four octets more; downstream tag
//                        removal becomes size-safe.
//
// The parameter is exposed rather than chosen here because the right
// answer depends on what is downstream, which this module cannot know.
module tagged_floor_adjust
  import frame_size_pkg::*;
#(
  parameter bit POP_SAFE = 1'b0,
  // Tags this design will account for. Beyond this the frame is refused
  // rather than sized by a guess -- Chapter 5.5 §9's bounded-parse rule
  // applied to an arithmetic problem instead of a parsing one.
  parameter int unsigned MAX_TAGS = 2,
  parameter int unsigned TAG_OCTETS = 4
) (
  input  logic       clk,
  input  logic       rst_n,
 
  input  logic       req_valid,
  input  logic [2:0] tags_present,
 
  output logic        rsp_valid,
  output logic        rsp_ok,
  // Floor on the CLIENT-DATA region for this frame.
  output logic [10:0] client_floor,
  // Floor on the whole frame, which is what a receiver checks.
  output logic [13:0] frame_floor
);
 
  // Overhead grows with each tag; the client-data floor shrinks by the
  // same amount, because the FRAME floor is what is fixed.
  wire [10:0] tag_octets  = 11'(tags_present) * 11'(TAG_OCTETS);
  wire [10:0] shrunk      = 11'(MIN_CLIENT) - tag_octets;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      rsp_valid    <= 1'b0;
      rsp_ok       <= 1'b0;
      client_floor <= 11'(MIN_CLIENT);
      frame_floor  <= 14'(MIN_FRAME_OCTETS);
    end else begin
      rsp_valid <= req_valid;
 
      if (req_valid) begin
        if (tags_present > 3'(MAX_TAGS)) begin
          // Refuse rather than compute. A negative floor is what an
          // unbounded version produces, and an unsigned subtraction turns
          // that into an enormous positive one -- a frame padded to
          // thousands of octets, from one wrap.
          rsp_ok       <= 1'b0;
          client_floor <= 11'(MIN_CLIENT);
          frame_floor  <= 14'(MIN_FRAME_OCTETS);
        end else begin
          rsp_ok <= 1'b1;
          if (POP_SAFE) begin
            // Size as though the tags were not there. The frame floor
            // rises with each tag; the client floor stays at 46, so
            // removing a tag lands exactly on the 64-octet floor.
            client_floor <= 11'(MIN_CLIENT);
            frame_floor  <= 14'(MIN_FRAME_OCTETS) + 14'(tag_octets);
          end else begin
            client_floor <= shrunk;
            frame_floor  <= 14'(MIN_FRAME_OCTETS);
          end
        end
      end
    end
  end
 
endmodule

Classification: synthesizable.

What it teaches: that a floor computed by subtraction needs its bound checked before the subtraction, not after. MIN_CLIENT - tag_octets is fine for zero, one or two tags and wraps for twelve. Unsigned wrap does not produce a small number or a negative one — it produces a floor near 2048, and the transmitter dutifully pads every frame to it. The symptom is enormous frames on a link that should be carrying small ones, which reads as a payload problem and is an arithmetic one.

Deliberately simplified: a fixed four octets per tag. Real deployments see one tag size in practice, and the parameter exists so the module does not encode the assumption silently.

Production implication: POP_SAFE is exposed rather than decided because the right answer depends on a device this module cannot see. A NIC whose frames terminate at a directly attached switch that pops tags should set it; one on a link where nothing removes tags is paying four octets per short frame for nothing. A parameter is the correct representation of a decision that belongs to the system integrator — and hard-coding either value is a design that is wrong in half of its deployments and silent about which half.

11. What the Floor Costs, Measured Properly

The floor's cost is usually stated as "small frames are inefficient", which is true and not actionable. The measurable version is more useful and more alarming.

Count everything the wire carries for one minimum frame:

RegionOctets
preamble and start delimiter8
addresses and length/type14
client datan
pad46 − n
frame check sequence4
interframe gap12
total wire occupancy84

Illustrative, from that table: a frame carrying one octet of client data occupies 84 octet times. The useful fraction is 1/84 — about 1.2%. Even at the full 46 octets, the useful fraction is 46/84, about 55%, because preamble, header, check sequence and gap are all still there.

Now express the same thing as a rate, which is the form that changes decisions. On a link running at line rate with minimum-size frames back to back, the frame rate is line rate divided by 84 octets — so a 1 Gb/s link carries roughly 1.49 million minimum frames per second, an illustrative figure that follows directly from 84 × 8 = 672 bits per frame slot.

Two consequences, and neither is about bandwidth.

The packet rate, not the bit rate, sizes the receive path. Every frame costs a lookup, a filter decision, an interrupt or descriptor, a buffer. Those costs are per frame, so a link saturated with minimum-size frames imposes roughly twenty times the per-frame work of one saturated with 1500-octet frames at the same bit rate. A receive path sized by bandwidth is undersized by that factor.

And the pad is bandwidth that no accounting layer sees. It is inside the frame, so it is counted as payload by every counter that counts frame octets. A link that is 40% pad reports as fully utilised while carrying 60% of the data it appears to. Section 12 measures it, because nothing else in the system can.

12. RTL 5 — Measuring the Pad, Because Nothing Else Can

Padding octets sit inside the frame, so an octet counter at the interface counts them as payload and reports a utilisation figure that overstates the data actually carried. Separately, because padding exists only on short frames, a link that is heavily padded is also a link with a high frame rate, which drives per frame work in the receive path. Only a monitor that knows which octets were pad can separate the two.Pad octetsinside the frameOctet counterscount them as payloadUtilisationoverstated, silentlyShort framesthe only frames paddedHigh frame rate84 octets per frame slotPer-frame worklookups, buffers,descriptorsPad monitorthe only view thatseparates them12
Figure 3 — pad octets are inside the frame, so every octet counter in the system counts them as payload.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE INSTRUMENTATION.
//
// Answers a question no other counter in the system can:
//
//     of the octets this link carried, how many were the floor's, and
//     how many were the client's?
//
// Every conventional octet counter counts pad as payload, because pad is
// inside the frame. So a link that is heavily padded reports full
// utilisation while delivering a fraction of that in data, and the gap is
// invisible from every direction except this one.
module padding_composition_monitor
  import frame_size_pkg::*;
#(
  parameter int unsigned CNT_W  = 40,
  parameter int unsigned WINDOW = 1_000_000,
  parameter int unsigned WIN_W  = $clog2(WINDOW + 1)
) (
  input  logic clk,
  input  logic rst_n,
  input  logic clear,
 
  input  logic        frame_done,
  input  logic [13:0] frame_octets,
  input  logic [10:0] pad_octets,
  input  logic        was_padded,
 
  output logic [CNT_W-1:0] total_octets,
  output logic [CNT_W-1:0] pad_total,
  output logic [CNT_W-1:0] frames_total,
  output logic [CNT_W-1:0] frames_padded,
 
  // Per-window snapshot. A RATIO is only meaningful over an interval, and
  // a free-running pair of counters cannot express "right now".
  output logic [CNT_W-1:0] win_octets,
  output logic [CNT_W-1:0] win_pad,
  output logic             win_valid,
 
  // Worst window seen. Survives `clear`, like Section 8's smallest frame.
  output logic [CNT_W-1:0] worst_pad_permille
);
 
  logic [WIN_W-1:0] win_q;
  logic [CNT_W-1:0] wo_q, wp_q;
 
  function automatic logic [CNT_W-1:0] add(input logic [CNT_W-1:0] v,
                                           input logic [CNT_W-1:0] d);
    add = (v > ({CNT_W{1'b1}} - d)) ? {CNT_W{1'b1}} : (v + d);
  endfunction
 
  // Per mille rather than per cent, and integer rather than fractional.
  // A design that reports a percentage loses the difference between 0.4%
  // and 0.04% pad -- which is exactly the range in which a link is fine.
  wire [CNT_W-1:0] win_permille =
      (wo_q == '0) ? '0 : ((wp_q * 1000) / wo_q);
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      total_octets       <= '0;
      pad_total          <= '0;
      frames_total       <= '0;
      frames_padded      <= '0;
      win_q              <= '0;
      wo_q               <= '0;
      wp_q               <= '0;
      win_octets         <= '0;
      win_pad            <= '0;
      win_valid          <= 1'b0;
      worst_pad_permille <= '0;
    end else begin
      win_valid <= 1'b0;
 
      if (clear) begin
        total_octets  <= '0;
        pad_total     <= '0;
        frames_total  <= '0;
        frames_padded <= '0;
        // worst_pad_permille deliberately survives.
      end else if (frame_done) begin
        total_octets  <= add(total_octets, CNT_W'(frame_octets));
        pad_total     <= add(pad_total,    CNT_W'(pad_octets));
        frames_total  <= add(frames_total, CNT_W'(1));
        if (was_padded) frames_padded <= add(frames_padded, CNT_W'(1));
        wo_q          <= add(wo_q, CNT_W'(frame_octets));
        wp_q          <= add(wp_q, CNT_W'(pad_octets));
      end
 
      if (win_q == WIN_W'(WINDOW - 1)) begin
        win_octets <= wo_q;
        win_pad    <= wp_q;
        win_valid  <= 1'b1;
        if (win_permille > worst_pad_permille) worst_pad_permille <= win_permille;
        wo_q  <= '0;
        wp_q  <= '0;
        win_q <= '0;
      end else begin
        win_q <= win_q + 1'b1;
      end
    end
  end
 
endmodule

Classification: synthesizable instrumentation.

What it teaches: that frames_padded and pad_total answer different questions and a design needs both. The frame count says how many transfers were below the floor — a property of the traffic's shape. The octet total says how much capacity the floor consumed — a property of the link's economics. A workload of tiny frames scores high on both; a workload with a few one-octet frames among many large ones scores high on the first and negligibly on the second, and treating those as the same finding sends the investigation to the wrong layer.

Deliberately simplified: integer per-mille, computed with a divide at the window boundary. A divider once per window is affordable in a way that a divider per frame is not, and the placement is the design decision rather than the arithmetic.

Production implication: this monitor is the only thing in the system that can say a link is fully utilised and mostly idle in the useful sense. The interface counters, the switch's port statistics, and the driver's byte counts all count pad as data, because from their vantage point it is indistinguishable from data — which is the same reason Section 4 gave for why the boundary has to be carried in a field. Instrumentation inherits the ambiguities of the format it observes, and escaping them requires a signal from the point where the ambiguity was resolved.

13. Assertions — The Boundary, the Floor, and One Property About Content

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// P1 -- THE CENTRAL PROPERTY. The emitted length is the CLIENT's octet
// count, never the padded target. Section 5's most damaging bug produces
// a frame that is internally consistent and silently wrong.
// ---------------------------------------------------------------------
property p_length_is_client_count;
  @(posedge clk) disable iff (!rst_n)
    (emit_length_valid) |-> (emit_length == $past(client_octets));
endproperty
a_length_is_client_count: assert property (p_length_is_client_count)
  else $error("length field carries the padded size, not the client's");
 
// ---------------------------------------------------------------------
// P2 -- Every transmitted frame reaches the floor.
// ---------------------------------------------------------------------
property p_frame_reaches_floor;
  @(posedge clk) disable iff (!rst_n)
    (tx_valid && tx_last) |-> (sent_total >= 11'(MIN_CLIENT));
endproperty
a_frame_reaches_floor: assert property (p_frame_reaches_floor);
 
// ---------------------------------------------------------------------
// P3 -- Pad is only ever appended, never inserted. A pad octet before the
// client's last octet has corrupted the payload rather than extended it.
// ---------------------------------------------------------------------
property p_pad_only_after_data;
  @(posedge clk) disable iff (!rst_n)
    (tx_valid && tx_is_pad) |-> client_data_complete;
endproperty
a_pad_only_after_data: assert property (p_pad_only_after_data);
 
// ---------------------------------------------------------------------
// P4 -- A frame already at or above the floor is not padded at all.
// ---------------------------------------------------------------------
property p_no_pad_when_long_enough;
  @(posedge clk) disable iff (!rst_n)
    (frame_start && client_octets >= 11'(MIN_CLIENT)) |=> (pad_appended == '0);
endproperty
a_no_pad_when_long_enough: assert property (p_no_pad_when_long_enough);
 
// ---------------------------------------------------------------------
// P5 -- Pad count and length are two readings of one decision, so they
// must sum to the region size. This is the ONE relation that keeps them
// from drifting.
// ---------------------------------------------------------------------
property p_pad_plus_length_is_region;
  @(posedge clk) disable iff (!rst_n)
    (emit_length_valid && pad_appended != '0)
      |-> ((emit_length + pad_appended) == 11'(MIN_CLIENT));
endproperty
a_pad_plus_length_is_region: assert property (p_pad_plus_length_is_region);
 
// ---------------------------------------------------------------------
// P6 -- RECEIVE SIDE. The client is delivered exactly the declared count
// on a length-form frame. Not the region, not the frame.
// ---------------------------------------------------------------------
property p_delivers_declared_count;
  @(posedge clk) disable iff (!rst_n)
    (cli_valid && cli_last && !pad_not_stripped)
      |-> (delivered_count == declared_length);
endproperty
a_delivers_declared_count: assert property (p_delivers_declared_count);
 
// ---------------------------------------------------------------------
// P7 -- cli_last and rx_last are DIFFERENT events on a padded frame. The
// standard bug is raising the first on the second.
// ---------------------------------------------------------------------
property p_cli_last_precedes_rx_last;
  @(posedge clk) disable iff (!rst_n)
    (cli_last && stripped_octets_will_be_nonzero) |-> !rx_last;
endproperty
a_cli_last_precedes_rx_last: assert property (p_cli_last_precedes_rx_last);
 
// ---------------------------------------------------------------------
// P8 -- A type-form frame is NEVER stripped. The boundary is not present,
// so any stripping is a guess about a format this layer may not parse.
// ---------------------------------------------------------------------
property p_type_form_never_stripped;
  @(posedge clk) disable iff (!rst_n)
    (frame_start && !is_length_form) |=> (pad_not_stripped && stripped_octets == '0);
endproperty
a_type_form_never_stripped: assert property (p_type_form_never_stripped);
 
// ---------------------------------------------------------------------
// P9 -- The three shortness classes partition. A frame cannot be two of
// them, and a frame that is none of them is not short.
// ---------------------------------------------------------------------
property p_shortness_classes_disjoint;
  @(posedge clk) disable iff (!rst_n)
    frame_done |=> $onehot0({is_fragment, is_undersized, is_short_declared});
endproperty
a_shortness_classes_disjoint: assert property (p_shortness_classes_disjoint);
 
// ---------------------------------------------------------------------
// P10 -- The check sequence is what splits fragment from undersized, so
// each class implies its own value of fcs_ok.
// ---------------------------------------------------------------------
property p_undersized_implies_good_fcs;
  @(posedge clk) disable iff (!rst_n)
    is_undersized |-> $past(fcs_ok);
endproperty
a_undersized_implies_good_fcs: assert property (p_undersized_implies_good_fcs);
 
// ---------------------------------------------------------------------
// P11 -- A short-declared frame is at or above the floor. Below it, the
// frame is a fragment or undersized and the declaration is not the story.
// ---------------------------------------------------------------------
property p_short_declared_is_full_size;
  @(posedge clk) disable iff (!rst_n)
    is_short_declared |-> ($past(frame_octets) >= 14'(MIN_FRAME_OCTETS));
endproperty
a_short_declared_is_full_size: assert property (p_short_declared_is_full_size);
 
// ---------------------------------------------------------------------
// P12 -- The computed floor never wraps. Checked as a RANGE on the
// output, because the bound test protecting it is easy to move.
// ---------------------------------------------------------------------
property p_client_floor_sane;
  @(posedge clk) disable iff (!rst_n)
    (rsp_valid && rsp_ok) |-> (client_floor <= 11'(MIN_CLIENT));
endproperty
a_client_floor_sane: assert property (p_client_floor_sane);
 
// ---------------------------------------------------------------------
// P13 -- The FRAME floor is the invariant; the client floor is derived.
// Under the minimum policy the frame floor never moves.
// ---------------------------------------------------------------------
property p_frame_floor_fixed_when_min;
  @(posedge clk) disable iff (!rst_n)
    (rsp_valid && rsp_ok && !POP_SAFE) |-> (frame_floor == 14'(MIN_FRAME_OCTETS));
endproperty
a_frame_floor_fixed_when_min: assert property (p_frame_floor_fixed_when_min);
 
// ---------------------------------------------------------------------
// P14 -- Under the pop-safe policy, removing the tags lands exactly on
// the floor. This is the property the policy exists to provide.
// ---------------------------------------------------------------------
property p_pop_safe_lands_on_floor;
  @(posedge clk) disable iff (!rst_n)
    (rsp_valid && rsp_ok && POP_SAFE)
      |-> ((frame_floor - 14'($past(tags_present)) * 14'(TAG_OCTETS))
             == 14'(MIN_FRAME_OCTETS));
endproperty
a_pop_safe_lands_on_floor: assert property (p_pop_safe_lands_on_floor);
 
// ---------------------------------------------------------------------
// P15 -- Pad octets never exceed the octets counted. A monitor that can
// report more pad than traffic is adding to the wrong accumulator.
// ---------------------------------------------------------------------
property p_pad_within_total;
  @(posedge clk) disable iff (!rst_n)
    win_valid |-> (win_pad <= win_octets);
endproperty
a_pad_within_total: assert property (p_pad_within_total);
 
// ---------------------------------------------------------------------
// P16 -- Extremes survive a counter clear, because they describe the link
// rather than the window.
// ---------------------------------------------------------------------
property p_extremes_survive_clear;
  @(posedge clk) disable iff (!rst_n)
    clear |=> ($stable(smallest_seen) && $stable(worst_pad_permille));
endproperty
a_extremes_survive_clear: assert property (p_extremes_survive_clear);
 
// ---------------------------------------------------------------------
// P17 -- COVERAGE. A run with no padded frame never exercised this
// chapter at all.
// ---------------------------------------------------------------------
c_padded_frame_seen: cover property (
  @(posedge clk) disable iff (!rst_n) (tx_valid && tx_is_pad)
);
 
// ---------------------------------------------------------------------
// P18 -- COVERAGE. The exact boundary: a client supplying precisely the
// floor, which must produce NO pad and is one octet from producing some.
// ---------------------------------------------------------------------
c_exact_floor_seen: cover property (
  @(posedge clk) disable iff (!rst_n)
    (frame_start && client_octets == 11'(MIN_CLIENT))
);

14. Verification — Twenty-Two Scenarios and the Frame That Lies About Itself

#ScenarioStimulusWhat must be observed
1Zero-octet client transferclient_octets = 0, length form46 pad octets; emit_length = 0; frame is 64
2One-octet transferclient_octets = 145 pad; emit_length = 1 — not 46 (P1)
3Exactly at the floorclient_octets = 46zero pad; emit_length = 46; no tx_is_pad at all (P4)
4One below the floorclient_octets = 45exactly one pad octet — the smallest non-zero pad
5One above the floorclient_octets = 47zero pad; frame is 65
6Maximum client dataclient_octets = 1500zero pad; nothing in this module changes
7Type-form short frame3 octets, type formpadded to the floor; emit_length_valid low
8Receive, padded length formdeclared 4, 46 arrive4 delivered; stripped_octets = 42; cli_last on the 4th
9Receive, unpaddeddeclared 100, 100 arrive100 delivered; stripped_octets = 0
10Receive, type formtype 0x0800, 46 octetsall 46 delivered; pad_not_stripped high for the whole frame (P8)
11Receive, truncateddeclared 40, 20 arrivetruncated asserts; nothing beyond 20 delivered
12cli_last timingdeclared 4 in a 46-octet regioncli_last on octet 4, rx_last on octet 46 — different cycles (P7)
13Payload ending in zerosdeclared 46, last 10 octets zeroall 46 delivered — no trimming
14Non-zero pad from a peerdeclared 4, pad octets are 0xA54 delivered; pad reported by the monitor, no assertion fires
15Fragment40 octets, bad check sequenceis_fragment; c_fragment increments
16Undersized with a good check40 octets, good check sequenceis_undersized — a peer conformance fault, not a link fault (P10)
17Short-declared64 octets, declared 60, 46 presentis_short_declared; the other two classes stay low (P9)
18Smallest-seen trackinga 17-octet frame, then a clearsmallest_seen = 17 survives; counters zero (P16)
19One tag, minimum policytags_present = 1, POP_SAFE = 0client_floor = 42; frame_floor = 64
20One tag, pop-safe policytags_present = 1, POP_SAFE = 1client_floor = 46; frame_floor = 68 (P14)
21Tag count over the boundtags_present = 5, MAX_TAGS = 2rsp_ok low; floors held at defaults — no wrap (P12)
22Pad composition1000 frames, half at the floorwin_pad / win_octets matches the constructed ratio

15. Debugging — Short Frames, and Which Kind of Short

Symptom — a peer's application receives more data than was sent, and the extra bytes are zeros.

The classic signature of a length field carrying the padded size. Check emit_length against the client's octet count at the transmitter directly; the receiver cannot detect this, because the frame is self-consistent. If the transmitter is not accessible, the tell is in the data: the excess is exactly the difference between the client's count and 46, and it appears only on transfers below 46 octets. A transfer of 50 octets arrives correctly, which is what makes the report read as "sometimes we get extra bytes" rather than as a padding bug.

Symptom — runt counters climbing, and cable replacement changes nothing.

Read Section 8's split rather than the aggregate. If the frames are undersized — short with a good check sequence — then nothing in the transmission path damaged them; the peer emitted them that way, and the fault is in its transmit padding. If they are fragments — short with a bad check sequence — the frames were damaged after transmission, and the physical path is the right place to look. The aggregate counter cannot distinguish a defective peer from a defective cable, and both hypotheses are consistent with it.

Symptom — a link reports high utilisation, and the application throughput does not account for it.

Read win_pad against win_octets. A link that is 30% pad is delivering 70% of what its octet counters claim, and every conventional counter in the path agrees with the wrong number because pad is inside the frame. Then read frames_padded as well, because the response differs: many small transfers means the fix is batching at the application, while a few tiny frames among large ones means the pad is negligible and the throughput problem is elsewhere.

Symptom — frames are enormous on a link that should be carrying small ones.

Suspect the floor computation before suspecting the payload. Section 10's subtraction wraps if the tag bound is bypassed, producing a client floor near 2048 and a transmitter that pads every frame to it. Confirm by checking whether the frame size is constant — a wrapped floor pads everything to the same value, which no payload distribution does.

Symptom — a switch that pops tags emits frames the next hop counts as runts.

Expected behaviour with the minimum policy, and it means the tag removal is not repadding. A tagged 64-octet frame becomes 60 when four octets are removed, and 60 is below the floor. Either the popping device must repad, or the originating device must use the pop-safe policy of Section 10 and emit 68-octet tagged frames. Note which device is at fault: the one emitting runts is the one with the bug, but the cheapest fix may be at the source, and that is a system decision rather than a conformance one.

Symptom — pad octets contain recognisable fragments of other traffic.

Not a receiver bug and not a link fault. A transmitter somewhere is padding with uninitialised buffer memory, which is a real and catalogued information-disclosure class. Capture the source address and report it; do not attempt to remediate at the receiver, which cannot, and do not assert on it in RTL, for the reasons Section 13 gives at length.

16. Common Misconceptions

"64 octets is the minimum because a frame needs that much structure."

The wrong model: the number falls out of the header and trailer sizes.

What it costs: you cannot explain why full-duplex links still enforce it, why the number is 64 rather than 32 or 128, or why it did not change when the header grew by four octets for tagging. And you have no way to reason about what would have to be true for it to change.

The corrected model: it is 512 bit times, which is a round-trip propagation time across a maximum-length shared segment, converted to bits at the signalling rate. The frame format was the output of that calculation, not its input — and 46 is what remains after subtracting 18 octets of unavoidable structure.

"Padding is part of the payload."

The wrong model: the client-data region is the payload, and its whole extent belongs to the client.

What it costs: a receiver that delivers the region rather than the declared length, so every short transfer arrives with a tail of octets the sender never wrote. The application sees them as data, and the bug reports as "extra bytes", not as a padding fault.

The corrected model: the pad is inserted by the transmitting MAC and belongs to nobody. It is separated from the client's data by exactly one thing — the length field — and when the field is a type instead, that separation does not exist at this layer at all.

"Trailing zeros in a frame are padding."

The wrong model: you can find the boundary by looking at the data.

What it costs: both directions of the inference fail. A payload is entitled to end in zeros, so trimming truncates real data with no error and no counter. And the pad is not required to be zero, so a non-zero tail that is pad is delivered as data.

The corrected model: the boundary is metadata, not content. It is carried in the length field or it is not carried at all. Section 13's rejected property is what happens when a design forgets this and asserts the pad's value.

"A short frame means a bad cable."

The wrong model: runts are a physical-layer symptom.

What it costs: an investigation aimed at the link when the fault is in a peer's transmit path. Cables get replaced, optics get swapped, and the counters keep climbing because nothing on the path was ever damaging anything.

The corrected model: the check sequence splits them. A short frame with a bad check sequence was damaged in transit. A short frame with a good one was emitted that way by a peer whose padding is broken — and it is not a link problem in any sense.

"Padding costs a few bytes."

The wrong model: the overhead is the pad octets themselves.

What it costs: you size a receive path by bandwidth. A link saturated with minimum frames imposes roughly twenty times the per-frame work of one saturated with maximum frames at the same bit rate, and per-frame work is what actually runs out.

The corrected model: the cost is a frame rate, not an octet count. A minimum frame occupies 84 octet times once preamble and gap are counted, so at a fixed line rate the frame rate is fixed and high — and every lookup, filter decision and buffer is charged against it. The pad is also invisible to every octet counter in the system, so the link reports a utilisation it is not delivering.

17. Interview Reasoning

"Why is the minimum Ethernet frame 64 bytes?"

The weak answer names the header arithmetic. The answer that ends the topic runs the derivation in its real direction: a maximum segment length gives a propagation delay, the round trip of that delay is the time a station must still be transmitting to hear a collision, that time at the signalling rate is 512 bit times, and 512 bits is 64 octets — after which the frame format subtracts its 18 octets of structure to leave a 46-octet client floor. The payoff is that every term in the chain is about a shared medium, and none of them exists on a modern link.

"Full duplex has no collisions. Why do we still pad?"

The trap is to answer "legacy" and stop. The complete answer has three parts and grades on the third: frames cross links with different properties and the format is shared, so a short frame created on a full-duplex link can become undetectable elsewhere; every deployed receiver already enforces the floor, so removing it is not backward-compatible in the direction that matters; and — the good reason — shortness now reliably means damage, so the constraint stopped being a defence and became a detector.

"You see runts on a link. What do you check first?"

Not the cable. The strong answer asks for the check sequence on those frames, because it splits the population into two causes with two different owners: a bad one means damage in transit, a good one means a peer transmitted a frame that was already short — its padding is broken, and nothing about the link is wrong. Adding that a single "runt" counter cannot express this distinction, and that the third population — a full-size frame whose length field promises more than it delivered — is a different fault again, is what shows the question has been met before.

"Should you assert that padding bytes are zero?"

No, and the reason is the interesting part: a conformant implementation can violate it, because the MAC standard specifies the pad's size and not its contents. Then the two follow-ups that make the answer complete: non-zero pad from a peer is a real finding — it has been an exploited information-disclosure class, where drivers padded with uninitialised buffer memory — so the correct instrument is a monitor that names the source address, not an assertion in your own receiver that fires in the wrong device and gets disabled. And on the transmit side, where you do control it, pad with a constant.

18. Understanding Check

It is a distance converted to a time converted to bits.

A shared segment has a maximum end-to-end extent. A collision at the far end takes a round trip to come back, plus repeater delays in both directions. A transmitter must still be sending when it arrives, or it will finish, believe it succeeded, and never learn otherwise. That round trip is 512 bit times, and 512 bits is 64 octets.

The frame format enters last. Subtract the 18 octets every frame carries — two addresses, the length/type field, the check sequence — and 46 remain as the client-data floor.

It cannot be per-link because a station cannot know which link it is on. A shorter minimum is perfectly safe on a short segment and unsafe on a long one, and the property being defended is a property of the worst case in the network, not of the cable in front of this station.

And it is why the number outlived its cause. The frame travels; the segment does not. A format shared by links that no longer share the constraint keeps the constraint.

19. What's Next

The claim this chapter defended: the payload floor is inherited, and padding is what frames pay for a constraint that no longer applies to them.

64 octets is a round-trip propagation time across a shared segment, converted to bits — a measurement in metres, made once, that still shapes every frame transmitted today. Full duplex removed the collisions that produced it, and the floor stayed: partly because frames cross links that do not share the property, partly because every receiver already enforces it, and partly because shortness became a reliable indicator of damage once it stopped being a symptom of contention.

And padding is not data. It is inserted by the transmitting MAC, separated from the client's octets by the length field alone, and invisible to every counter in the system — which is why a receiver must strip by the declared count rather than by the frame's extent, must refuse to strip at all when the field is a type, and must never assume anything about what the pad octets contain.

Chapter 5.7 — MTU and Jumbo Frames takes the other end of the same axis. If the minimum is inherited from a mechanism that is gone, the maximum is a convention that was never derived from anything physical at all — and raising it is not a setting on a device but an agreement among every device on a path.

5.7 shows what a larger frame buys — the per-frame overhead this chapter measured at 84 octets, amortised over far more payload — and what it costs: a length field that cannot express the size, buffers that must grow at every hop, and a mismatch that manifests not as an error but as a black hole, in which frames of one size pass and frames of another silently do not.

The full path is on the Ethernet curriculum index.

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.