Skip to content
VLSI Mentor

USB · Module 4

Architectural Evolution

What the sequence of generations shows rather than what each one did: accretion at the bottom with nothing removed, an untouched device model, and the cost that pattern imposes — including which state a multi-mode controller must isolate, and the bug when it does not.

Four chapters have described four generations. This one stops describing them individually and asks what the sequence shows.

There is a pattern, it is sharp enough to be predictive, and it explains two things at once: why the architecture survived decades of requirements it was not designed for, and what each survival cost the people building silicon. It also leads directly to the first question in this module with a concrete hardware answer — what must a controller supporting several generations keep separate?

1. The Pattern

Lay the four transitions side by side.

GenerationWhat was addedWhat was removedWhat software saw
USB 1.xthe architecture itselfthe device model, established
USB 2.0a rate on the same conductorsnothingunchanged
USB 3.xconductors, and an architecture on themnothingextended, not replaced
USB4a carrier that tunnels protocolsnothingextended, not replaced

Two columns are doing the work.

The "removed" column is empty, four times. The older rates survived generation two. The older pair survived generation three. It survived USB4 as well, outside the tunnelling arrangement. Nothing in USB's history has ever been taken away.

And the "software saw" column barely moves. A device still describes itself, is addressed, and is configured from what it declared — the framework Chapter 4.1 §3 identified as generation one's most consequential commitment. Each generation extended what could be declared; none replaced the declaring.

USB's layers annotated with how much each changed across four generations. The software-visible device model and the identity and configuration framework were extended but never replaced, so software written for the first generation still works. The host-owned transaction model was preserved while its mechanisms were extended. The transport layer changed substantially at every generation, gaining a rate, then a second coexisting bus, then a tunnelling carrier. The physical layer changed most of all, gaining electrical arrangements, additional conductors and a required connector. Change accumulated at the bottom and did not propagate upward.Software-visible device modelextended, never replaced — four generationsextended, never replaced — four generationsIdentity and configurationnever rebuilt — the load-bearing commitmentnever rebuilt — the load-bearing commitmentHost-owned transaction modelpreserved; mechanisms extendedpreserved; mechanisms extendedTransporta rate · then a coexisting bus · then a tunnelling carriera rate · then a coexisting bus · then a tunnelling carrierPhysicalarrangements · conductors · a required connectorarrangements · conductors · a required connector
Figure 1 — change accumulated at the bottom and stopped there. The layers software depends on absorbed four generations without being rebuilt, which is why the installed base stayed valid.

2. Why the Pattern Was Possible

Accretion is not automatic. It worked because of a property established long before it was tested: Chapter 1.7's layering, in which upper layers were structurally forbidden to depend on the transport beneath them.

A device describing itself does not care how the description travels. A host binding a class driver does not care what signalling carried the descriptors. Because those independences were designed in, a new transport could be slid underneath without disturbing anything above — and each generation could be, as Chapter 4.2 put it, confined to one layer.

The counterfactual makes the point. Had the identity framework depended on the signalling rate — had a descriptor's format or an address's meaning been tied to a transport detail — then generation two would have required every device, driver and operating system to change. At that price the industry does not follow, and USB's history would have ended with a second incompatible standard rather than a second generation.

Accretion is what good layering buys you. It is also what makes the layering worth the discipline it costs, because the discipline's payoff is entirely in the future.

3. What the Pattern Costs

Now the other half, which advocacy usually omits.

Nothing retires, so cost accumulates. A current controller may carry an arrangement introduced four generations ago. Its verification never retires. Its silicon is present in every product. Its bugs are still possible. Chapter 4.2 §3's translator is still in hubs.

Implementations grow in kinds, not sizes. Chapter 4.3 doubled parts of a controller and added a seam; Chapter 4.4 added an allocator and protocol adaptation. None of that is a bigger version of something already present.

The verification space multiplies rather than adds. Each generation contributes a dimension — a mode, a path, a tenant — and dimensions cross.

And the ecosystem carries combinations that no one designed. A USB4 host, a USB 3.x hub, a USB 2.0 device and a cable of uncertain capability is a configuration nobody specified and everybody must support. Chapter 4.7 is about reasoning over exactly that.

4. The Question That Has a Hardware Answer

Everything above is architecture. Here is where it becomes RTL.

A controller supporting several generations holds state for each. Some of that state can be shared — buffers, memory interfaces, register space, interrupt paths — and sharing is attractive because it saves area. Some of it must be isolated, because it means different things in different modes or is meaningless outside its own.

Getting that division wrong produces a specific and nasty bug: state from a previous mode surviving into the next one, where it is interpreted under different rules. The symptom appears after a mode change, which means it appears after an attachment, a reset or a fallback — and it is intermittent, because it depends on what the previous mode left behind.

The design principle is stateable:

State that means different things in different modes must not survive a mode change. State that means the same thing in all modes may be shared — and should be, because duplicating it creates a coherence problem of its own.

5. Mode-Isolated State, as RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// mode_scoped_state
//
// Classification: CONCEPTUAL ARCHITECTURE ABSTRACTION. Synthesizable, but
// it is NOT a USB controller and implements no USB mechanism.
//
// WHAT IT MODELS. The shared-versus-isolated division of section 4: which
// state survives a change of operating mode and which must not. That is the
// whole content -- the "modes" here are opaque identifiers and carry no USB
// semantics whatsoever.
//
// WHAT IT IS NOT. There is no generation FSM, no negotiation, no speed
// detection, no protocol engine and no capability advertisement. A USB
// generation is NOT an enumeration value and a controller does not support
// one by widening a parameter; modelling it that way is the error this
// module has argued against throughout. Module 5 owns speed modes, Module
// 20 owns USB 3.x, and real controller design belongs to Modules 21-23.
//
// The transferable idea is the CLEAR-ON-MODE-CHANGE rule and, just as
// importantly, the fact that the shared counter deliberately does NOT clear.
// ─────────────────────────────────────────────────────────────────────────
module mode_scoped_state #(
  parameter int MODE_W = 2,
  parameter int CNT_W  = 16
) (
  input  logic              clk,
  input  logic              rst_n,

  // Opaque mode identifier. Deliberately NOT an enum of USB generations --
  // see the header. What matters is only that it can change.
  input  logic [MODE_W-1:0] mode,

  input  logic              err_event,     // a mode-scoped error occurred
  input  logic              xfer_event,    // a mode-agnostic transfer completed

  // MODE-SCOPED: meaningful only within the current mode. An error count
  // accumulated under one set of protocol rules says nothing about another.
  output logic [CNT_W-1:0]  mode_err_count,

  // MODE-AGNOSTIC: means the same thing in every mode, so it is shared and
  // must SURVIVE a mode change. Clearing it would silently lose history that
  // nothing else records -- the mirror-image bug of failing to clear.
  output logic [CNT_W-1:0]  total_xfer_count,

  output logic              mode_changed   // 1-cycle pulse
);

  logic [MODE_W-1:0] mode_q;

  // A mode change is detected, not commanded. The controller is TOLD what
  // mode it is in by whatever established it; this block's only job is to
  // react correctly when that changes.
  assign mode_changed = (mode != mode_q);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      mode_q           <= '0;
      mode_err_count   <= '0;
      total_xfer_count <= '0;
    end else begin
      mode_q <= mode;

      // ── Mode-scoped state ────────────────────────────────────────────
      // Clear takes priority over accumulate. An event arriving in the same
      // cycle as a mode change belongs to the OLD mode, and carrying it into
      // the new one is exactly the leak this module exists to prevent.
      if (mode_changed) begin
        mode_err_count <= '0;
      end else if (err_event && !(&mode_err_count)) begin
        mode_err_count <= mode_err_count + 1'b1;   // saturate, do not wrap
      end

      // ── Mode-agnostic state ──────────────────────────────────────────
      // Deliberately unaffected by mode_changed. A completed transfer is a
      // completed transfer whatever mode carried it.
      if (xfer_event && !(&total_xfer_count)) begin
        total_xfer_count <= total_xfer_count + 1'b1;
      end
    end
  end

endmodule

What it models. One rule and its mirror image: mode-scoped state clears on a mode change, mode-agnostic state does not.

Why the structure exists. mode_changed is derived from comparing the input against a registered copy, because the block is told its mode rather than deciding it — a controller does not choose its generation, it is informed of the outcome. Clear takes priority over accumulate so that an event coinciding with a mode change is attributed to the old mode and discarded with it; the opposite ordering carries one event across the boundary, which is a small leak that is very hard to see. Both counters saturate rather than wrap, because a wrapping diagnostic counter silently understates.

Hardware implied. Two counters, a mode register, a comparator.

State retained. The previous mode, plus the two counters with deliberately different lifetimes.

Assumptions. That mode is stable and already synchronised into this domain — a mode value crossing from a PHY domain needs the treatment Chapter 3.1 §7 described, and a multi-bit value is not safe to synchronise bit by bit.

What DV should verify. That the scoped counter is zero after any mode change; that an error event coinciding with a mode change does not appear in the new mode's count; that the agnostic counter is unaffected by mode changes; and that both saturate.

What it deliberately omits. All USB semantics. The modes are opaque; there is no generation FSM, no negotiation and no capability logic, because a generation is not an enumeration value.

6. The Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Assertions for mode_scoped_state.
//
// Classification: ARCHITECTURAL TEACHING ASSERTIONS about state lifetime.
// They prove nothing about USB and make no compliance claim.
// ─────────────────────────────────────────────────────────────────────────

// E1 -- THE CENTRAL ONE. Mode-scoped state does not survive a mode change.
// The bug it catches is stale state interpreted under new rules, whose
// symptom appears after an attachment, reset or fallback and is intermittent
// because it depends on what the previous mode left behind.
property p_scoped_state_clears;
  @(posedge clk) disable iff (!rst_n)
    mode_changed |=> (mode_err_count == '0);
endproperty
assert property (p_scoped_state_clears);

// E2 -- the mirror image, and the one teams forget. Mode-AGNOSTIC state must
// NOT be cleared by a mode change. Over-clearing is as real a defect as
// under-clearing: it silently destroys history nothing else records, and it
// produces no error at all.
property p_agnostic_state_survives;
  @(posedge clk) disable iff (!rst_n)
    (mode_changed && !xfer_event) |=> $stable(total_xfer_count);
endproperty
assert property (p_agnostic_state_survives);

// E3 -- an event coinciding with a mode change belongs to the OLD mode and
// must not be carried across. This catches the priority inversion: writing
// the accumulate branch ahead of the clear leaks exactly one event per mode
// change, which is small enough to survive casual testing indefinitely.
property p_no_event_leaks_across;
  @(posedge clk) disable iff (!rst_n)
    (mode_changed && err_event) |=> (mode_err_count == '0);
endproperty
assert property (p_no_event_leaks_across);

E1 and E2 are a pair and must both be written. A team that writes only E1 will clear too much; a team that writes only E2 will clear too little. Each alone looks like diligence and together they state the actual rule — which is why §4 phrased it as two clauses.

E3 is the subtle one. A single leaked event per mode change is a defect that can survive an entire test campaign, because it never produces a wrong result, only a slightly wrong count — and counts are rarely checked exactly.

7. What This Means for Verification

The module-wide consequence of accretion is that the configuration space is a product of dimensions that never retire, and it is worth stating how a DV engineer should hold that.

Dimensions accumulated so far, each contributed by a generation: signalling arrangement; path; concurrent tenant; plus the ever-present topology, attachment order and reset.

They cross rather than add. Three modes and four topologies is not seven cases.

And the corner that matters most is the transition, not the steady state. Each mode works or it does not, and that is relatively easy to establish. The interesting failures live at mode changes — which is what §5's block is about, and which is where an environment that only ever attaches a device once will find nothing.

So the highest-value stimulus is churn: attach, detach, re-attach at a different capability, reset mid-operation, force a fallback, and do it repeatedly. That is the pattern that exposes state leakage, and it is cheap to generate and rarely generated.

8. Common Misconceptions

9. Reason It Through

A multi-mode controller occasionally reports errors immediately after a device re-attaches at a different capability. The errors are not reproducible on demand, the device is healthy, and once traffic settles everything works normally.

What does the timing tell you? The symptom is tied to a mode change, which §4 identifies as the boundary where state lifetime is decided. That is a strong prior before any other evidence.

What does the intermittency tell you? That the outcome depends on something left over — because a deterministic logic fault would fail identically every time. State leakage is intermittent precisely because it depends on what the previous mode happened to leave behind, which depends on how the previous session went.

What does “settles down afterwards” tell you? That nothing is structurally broken in the new mode. The controller works; it simply began in an inconsistent condition and recovered. That combination — wrong at the boundary, right afterwards — is close to diagnostic for stale state.

Which direction is the error? Reported errors suggest state that should have cleared and did not, so counters or condition flags from the old mode are being interpreted under new rules. The opposite defect, over-clearing, produces no errors at all — it silently loses history, which is why E2 exists and why this symptom would not reveal it.

What would confirm it cheaply? Read the mode-scoped state immediately after a mode change and check that it is at its reset value. That is a direct observation of E1, and it requires no reproduction of the intermittent failure.

The general lesson. Intermittent + tied to a transition + self-correcting is the signature of stale state, and it points at a lifetime bug rather than a logic bug. Logic bugs do not recover on their own.

10. Understanding Check

11. Summary

The sequence shows one pattern: accretion at the bottom. Capability was added underneath, nothing was ever removed across four generations, and the device model and identity framework were extended but never replaced — which is why software written for the first generation still describes a device the same way.

It was possible because Chapter 1.7's layering forbade upper layers from depending on the transport. Had identity depended on a transport detail, generation two would have obsoleted every device, driver and operating system, and the industry would not have followed.

Its cost is permanence. Nothing retires, so every arrangement ships in every product forever, its verification never ends, and the ecosystem carries combinations nobody designed.

The pattern's concrete hardware consequence is a rule about state lifetime: state meaning different things in different modes must not survive a mode change; state meaning the same thing in all modes should be shared, because duplication creates a coherence problem. Both directions are defects — under-clearing leaks stale state into a mode that misinterprets it, and over-clearing silently destroys history while producing no error at all.

For verification, the dimensions each generation contributed cross rather than add and never retire, and the failures live at transitions rather than steady states — so the highest-value stimulus is churn, which is cheap to generate and rarely generated.

12. What Comes Next

This chapter has deliberately avoided the number everyone reaches for first. Four generations have been described by what they changed architecturally, and the rates have appeared only in passing.

Chapter 4.6 addresses them properly, and the interesting content is not the sequence of figures. It is what had to change about the encoding for each rate to be reachable — because a line code is not free, the cost changed between generations, and the relationship between a signalling rate and the payload it can actually carry is exactly where the difference between a marketing number and an engineering number lives.

Browse the full path on the USB tutorials index.

Continue learning

Standards & specifications

Governing standard
USB-IF (Universal Serial Bus Specification)(opens USB Implementers Forum (USB-IF) in a new tab)

Defines the USB bus — its electrical signalling, connectors, packet and transaction model, device framework and the descriptors a device must expose — together with the device-class specifications layered on it. It does not define host-controller register interfaces (xHCI and EHCI are separate documents) nor any operating system's driver architecture.

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 USB curriculum.