Skip to content
VLSI Mentor

USB · Module 12

Token Stage

The stage that moves no data and decides everything after it — why the token's contents must be captured rather than read, and what a data packet means when no transaction is open.

Module 11 built every packet on the bus and then stopped at each one's boundary. Not one relationship between them was specified.

A token is followed by a data packet, which is answered by a handshake — and those two words, followed and answered, are the whole of Module 12.

This chapter is the first of the three stages, and its distinguishing feature is that it transfers nothing.

1. A Stage That Moves No Data

Chapter 11.1 described the token packet: a PID, an address, an endpoint, a CRC5. What it did not describe is what happens next, because nothing in the packet says.

The token stage transfers no data. It creates a context.

Everything after it is interpreted in that context, and nothing after it repeats the context. The data packet has no address and no endpoint. The handshake has neither, and no payload at all.

Which produces the constraint the whole chapter turns on:

The token is said once. Every packet that follows depends on it, and none of them can ask again.

Three obligations follow immediately, and §5's RTL is precisely these three:

  • The device must capture what the token said — not observe it, capture it — because the decoder's outputs are gone the moment the packet ends.
  • The device must know a transaction is open, because a data packet arriving outside one has no meaning that could be recovered.
  • The device must know when it closes, because a context that outlives its transaction makes the next stray packet look legitimate.

2. What the Token Commits Each Side To

A token is not a request that may be ignored. It opens a window in which both ends owe each other something specific, and the two obligations are not symmetric.

The host has committed bus time. It has decided this transaction happens now, allocated the slot (Chapter 10.3's arbitration produced it), and will not use the bus for anything else until the transaction resolves.

The device has committed to respond within a bounded window, and the bound is Chapter 12.5's subject. What matters here is that the window exists and that it is short.

HostDevice
After the tokenwaitsmust act
If the other side is silenttimes out, retriesnothing to wait for
Cost of being slowa retrythe transaction is lost
Can abandon the transactionyesno

3. A Data Packet With No Transaction

A data packet arrives. No token preceded it. What does it mean?

Nothing, and that is not a figure of speech. A data packet carries a PID, a payload and a CRC16 — no address, no endpoint, no direction. Without a token there is no fact anywhere on the bus saying whether it was for this device, which endpoint it belongs to, or which direction it is travelling.

So it must be discarded, silently. Chapter 11.3 §3's argument applies exactly: answering would require knowing that a reply belongs to this device, and that is precisely what is not known.

How does this happen on a real bus? Three ways, and all three are ordinary:

  • The token was corrupted. The host sent one; this device's Chapter 11.1 filter rejected it on CRC. The host does not know that, and sends the data.
  • The token was for someone else and this device's own token was lost. Another device is mid-transaction; its data packet is on the bus and this device sees it.
  • The device aborted — §2's silent withdrawal — and the host has not yet timed out.

The third is the one that catches designs, because the device did have a transaction a moment ago, and the difference between a context that closed and a context that never opened is exactly one register bit.

A sequence diagram of an OUT transaction followed by a stray data packet. The host broadcasts an OUT token naming endpoint three. The device's token filter accepts it and captures the endpoint number and direction into a transaction context, which opens. A gap follows during which the token packet no longer exists on the bus but the captured context persists. The host then sends a data packet carrying no address or endpoint of its own; the device interprets it using the captured context and accepts it for endpoint three. The device returns a handshake and the transaction closes, discarding the context. A further data packet then arrives with no token before it; because no context is open, the device has no way to know which endpoint or direction it belongs to, and discards it silently.The context the token createsHostToken filterTransactioncontextEndpointOUT · ADDR=mine ·ENDP=3capture: EP3,direction OUTtransaction OPEN…gap. the token nolonger existsanywhereDATA0 · payload — noaddress, no endpointinterpreted as: EP3,inboundhandshaketransaction CLOSED ·context goneDATA1 · payload — notoken preceded thisnothing to interpretit against —discard, silently
Figure 1 — one OUT transaction and one stray packet. The context comes into existence with the token, persists through the gap and the data stage, and is gone the instant the transaction closes — which is why the stray data packet that follows has nothing to be interpreted against.

4. Capture, Do Not Observe

The distinction that produces §6's headline defect.

The naive structure wires Chapter 11.1's decoder outputs straight to whatever consumes them. The decoder is combinational — it said so explicitly — so endp and is_in are valid exactly while the token packet's bits are present and meaningless afterwards.

The data stage happens later. Not much later on a fast bus, but later: a gap, then a packet that takes microseconds to arrive. By then the decoder's outputs describe nothing.

5. The Transaction Context, as RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// usb_txn_context
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It models section
// 1's three obligations: capture what the token said, know a transaction is
// open, and know when it closes.
//
// WHAT IT MODELS. The state that exists BETWEEN packets -- which is the
// thing Module 11 never needed, because every chapter there began and ended
// inside one packet.
//
// WHAT IT DOES NOT MODEL. The token packet or its filtering (Chapter 11.1
// -- `token_accept` is that block's output); the data stage itself (Chapter
// 12.2); the handshake (Chapter 12.3); the timeout that produces most
// `txn_abort` pulses (Chapter 12.5); and the full stage sequencing, which
// is Chapter 12.4 and consumes this block rather than replacing it.
//
// ── WHY EVERY FIELD IS REGISTERED ───────────────────────────────────────
// Chapter 11.1's decoder is combinational by design -- its outputs are
// valid only while the token's bits are on the wire. The data stage happens
// AFTER that. So this block's registers are not an optimisation or a
// pipeline stage: they are the only place the token's contents exist once
// the token is over. Section 6's X1 measures what their absence costs, and
// the answer is that the device uses somebody else's endpoint number.
// ─────────────────────────────────────────────────────────────────────────
module usb_txn_context (
  input  logic       clk,
  input  logic       rst_n,
  input  logic       bus_reset,     // Chapter 8.3

  // From Chapter 11.1's filter: a token addressed to US, CRC good, not an SOF.
  input  logic       token_accept,
  input  logic [3:0] token_endp,
  input  logic       token_is_in,
  input  logic       token_is_out,
  input  logic       token_is_setup,

  // The transaction ended. `txn_done` is a normal conclusion; `txn_abort`
  // covers section 2's silent withdrawal and Chapter 12.5's timeout, NEITHER
  // of which corresponds to a packet on the wire.
  input  logic       txn_done,
  input  logic       txn_abort,

  output logic       txn_open,
  output logic [3:0] ctx_endp,
  output logic       ctx_is_in,
  output logic       ctx_is_setup,

  // Section 3: a data packet is interpretable ONLY inside an inbound
  // transaction. Outside one there is no address, endpoint or direction
  // anywhere on the bus that could apply to it.
  output logic       data_may_be_accepted,

  // A token arriving while one is already open. Not an error this block can
  // fix -- it is evidence the host gave up on the previous transaction and
  // this device did not notice. Reporting it is how that becomes findable.
  output logic       token_overrun
);

  logic       open_q;
  logic [3:0] endp_q;
  logic       in_q, setup_q;
  logic       overrun_q;

  assign txn_open      = open_q;
  assign ctx_endp      = endp_q;
  assign ctx_is_in     = in_q;
  assign ctx_is_setup  = setup_q;
  assign token_overrun = overrun_q;

  // Derived (Chapter 8.4), so it cannot disagree with the context it
  // summarises. Note the !in_q: during an IN the DEVICE transmits, so an
  // inbound data packet is as meaningless as one with no transaction at all.
  assign data_may_be_accepted = open_q && !in_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      open_q <= 1'b0; endp_q <= '0; in_q <= 1'b0; setup_q <= 1'b0;
      overrun_q <= 1'b0;
    end else if (bus_reset) begin
      open_q <= 1'b0; endp_q <= '0; in_q <= 1'b0; setup_q <= 1'b0;
      overrun_q <= 1'b0;
    end else begin
      overrun_q <= 1'b0;                 // a pulse, not a level

      if (token_accept) begin
        // A NEW TOKEN ALWAYS WINS, even mid-transaction. The host owns the
        // bus (section 2): if it has issued a new token, the previous
        // transaction is over whatever this device believed, and continuing
        // to act on the old context would mean answering the new token with
        // the old endpoint's data.
        if (open_q) overrun_q <= 1'b1;

        open_q  <= 1'b1;
        endp_q  <= token_endp;
        in_q    <= token_is_in;
        setup_q <= token_is_setup;
      end else if (txn_done || txn_abort) begin
        open_q <= 1'b0;
        // The context fields are deliberately NOT cleared. They are only
        // readable while open_q is set, so clearing them would spend logic
        // making an already-unreadable value more unreadable -- and would
        // hide, rather than prevent, a consumer that reads them anyway.
      end
    end
  end

endmodule

What it models. The state that exists between the packets of one transaction.

Engineering reason. Because the token is said once and everything after it depends on what it said.

Inputs. A bus reset, an accepted token with its fields, and the two ways a transaction ends.

State retained. One open flag, 4 bits of endpoint, two type bits, and the overrun pulse — 8 flip-flops.

Outputs. The open indication, the captured context, a derived acceptance permission, and the overrun report.

Hardware implied. A small register file with a two-way priority on its write enables.

Reset behaviour. Everything clears on hard reset and bus reset. A transaction does not survive a bus reset — Chapter 8.3 is rebuilding the relationship the transaction was conducted under.

Assumptions. That token_accept has already passed Chapter 11.1's four filters, SOF exclusion included; that txn_done and txn_abort are single-cycle; and that the consumer reads ctx_* only while txn_open is set.

Omissions. The packets, the stages, the timeout and the full sequencing — in the header.

What DV should verify. That the context matches the token that opened it, for the whole transaction and not merely the cycle after; that no data is accepted with no transaction open; that no data is accepted during an IN; that a mid-transaction token replaces the context and is reported; that a transaction closes on either ending; and that a bus reset closes one.

Context: created, held, discarded

10 cycles
A waveform of the transaction context over ten cycles. The first cycle is idle with no transaction open. In the second an OUT token for endpoint three is accepted. From the third cycle the transaction is open and the context reads endpoint three outbound, and data may be accepted. The third and fourth cycles are gaps in which no packet is present but the context persists. In the fifth a data packet arrives and is interpretable. In the sixth the transaction is concluded. From the seventh the transaction is closed, the context reads none, and data may no longer be accepted — so the stray data packet in the eighth cycle has nothing to be interpreted against. In the ninth an IN token for endpoint five is accepted, and from the tenth the context reads endpoint five inbound with data acceptance off, because during an IN the device transmits rather than receives.token — the only packet that names anythingtoken — the only packetthat names anythinggap: the token is gone, the context is notgap: the token is gone, thecontext is notstray DATA — nothing to read it againststray DATA — nothing toread it againstIN: the device transmits, so accept is offIN: the device transmits,so accept is offstageidleOUT tokgapgapDATAACKidlestrayIN tokdev txtoken_accepttxn_opencontextnonenoneEP3 OUTEP3 OUTEP3 OUTEP3 OUTnonenonenoneEP5 INdata_may_accepttxn_endt0t1t2t3t4t5t6t7t8t9
Figure 2 — ten cycles spanning one OUT transaction, a stray packet and the start of an IN, every column taken from a simulation of the block above. The context appears one cycle after the token, survives the gap, and vanishes on completion — after which the stray data packet has nothing to be read against.

6. Mutation Test

Five mutations over 666 checks spanning 123 transactions, including a phase with twelve idle cycles between the token and the data — the gap §4 is about.

The unmutated block: 22 data packets declared acceptable, and 0 errors of every kind.

acceptableendpoint wrongdirection wrongdata with no txndata during INoverrun unreported
golden2200000
X1 context not captured563402130340
X2 no context required940038340
X3 second token ignored221941160978
X4 transaction never closes3000800
X5 direction not captured5602130340

X1 — read the decoder instead of capturing it

Measured: wrong endpoint on 340 checks, wrong direction on 213, and 34 data packets accepted during IN transactions.

§4's defect. The device acts on whatever the token decoder's outputs are now, and after the token they describe nothing.

The IN-transaction figure is the one to read. Thirty-four times the device prepared to receive during a stage in which it was supposed to transmit — which is not a data error but a direction error, and produces a device and a host both driving the bus.

X2 — accept data without an open transaction

Measured: 38 data packets accepted with no transaction at all, and 34 during IN transactions. 94 declared acceptable against a correct 22 — more than four times too many.

§3's rule removed. The device becomes a promiscuous receiver, absorbing packets belonging to other devices' transactions and to its own aborted ones.

And the damage is not confined to this device. A device that accepts another's data packet will Chapter 12.3-answer it, putting a handshake on the bus during somebody else's transaction — so the corruption lands on a device that is entirely correct.

X3 — ignore a token arriving mid-transaction

Measured: 78 unreported overruns, 194 wrong endpoints, 116 wrong directions.

The device holds its stale context while the host has moved on. Every subsequent packet of the new transaction is interpreted against the old transaction's endpoint and direction.

The host is not misbehaving. §2: it owns the bus, and a new token means the previous transaction is over whether or not this device noticed. A device that disagrees is wrong by construction, because it is the only party without the authority to decide.

X4 — never close the transaction

Measured: 196 wrong txn_open, and 8 data packets accepted with no transaction.

The context outlives its transaction, so a stray packet arriving afterwards is interpreted against the previous transaction's endpoint — plausibly, consistently, and entirely wrongly.

This is §3's third case made permanent: the difference between a context that closed and one that never opened is one register bit, and this mutation removes it.

X5 — capture the endpoint but not the direction

Measured: 213 wrong directions and 34 data packets accepted during IN transactions — identical to X1's figures on both counts.

X5 is X1's damage without X1's obviousness. X1 also corrupts the endpoint, which shows up as data going to the wrong place; X5 leaves the endpoint perfect and gets only the direction wrong.

7. The Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Classification: TEACHING ASSERTIONS about the transaction context.
// C-properties are the CAPTURE contract; O the OPEN/CLOSE contract;
// D the acceptance rule.
// ─────────────────────────────────────────────────────────────────────────

// C1 -- THE CONTEXT MATCHES THE TOKEN THAT OPENED IT, FOR AS LONG AS IT IS
// OPEN. The `throughout` is the entire point: a property checking only the
// cycle after the token would pass on section 6's X1, because X1's error
// appears LATER, when the decoder's outputs have moved on.
property p_context_holds;
  @(posedge clk) disable iff (!rst_n)
    (token_accept && !bus_reset) |=>
      ( (ctx_endp == $past(token_endp) && ctx_is_in == $past(token_is_in))
        throughout (txn_open [->1:$] ##0 (!txn_open || token_accept || bus_reset)) );
endproperty
assert property (p_context_holds);

// C2 -- AND IT NEVER CHANGES WITHOUT A TOKEN. The complement of C1: C1 says
// the right value was captured, this says nothing else writes it.
property p_context_stable;
  @(posedge clk) disable iff (!rst_n)
    (ctx_endp != $past(ctx_endp) || ctx_is_in != $past(ctx_is_in))
      |-> $past(token_accept || bus_reset);
endproperty
assert property (p_context_stable);

// O1 -- A TOKEN OPENS A TRANSACTION, ALWAYS -- including one already open.
// Section 6's X3: the host owns the bus, so a new token is not a request.
property p_token_opens;
  @(posedge clk) disable iff (!rst_n)
    (token_accept && !bus_reset) |=> txn_open;
endproperty
assert property (p_token_opens);

// O2 -- AND A MID-TRANSACTION TOKEN IS REPORTED. Without this, X3's silent
// context replacement would be indistinguishable from a normal one.
property p_overrun_reported;
  @(posedge clk) disable iff (!rst_n)
    (token_accept && txn_open && !bus_reset) |=> token_overrun;
endproperty
assert property (p_overrun_reported);

// O3 -- EITHER ENDING CLOSES IT. Section 6's X4.
property p_ending_closes;
  @(posedge clk) disable iff (!rst_n)
    ((txn_done || txn_abort) && !token_accept && !bus_reset) |=> !txn_open;
endproperty
assert property (p_ending_closes);

// O4 -- AND NOTHING ELSE OPENS ONE. Catches an open flag that sets itself.
property p_open_has_a_cause;
  @(posedge clk) disable iff (!rst_n)
    $rose(txn_open) |-> $past(token_accept);
endproperty
assert property (p_open_has_a_cause);

// D1 -- DATA IS ACCEPTABLE EXACTLY INSIDE AN INBOUND TRANSACTION. An
// EQUIVALENCE, and the ONLY property that catches section 6's X5 -- the
// mutation whose endpoint handling is perfect.
property p_accept_iff_inbound_open;
  @(posedge clk) disable iff (!rst_n)
    data_may_be_accepted == (txn_open && !ctx_is_in);
endproperty
assert property (p_accept_iff_inbound_open);

// R1 -- A BUS RESET ENDS ANY TRANSACTION. Chapter 8.3: the relationship the
// transaction was conducted under is being rebuilt.
property p_bus_reset_closes;
  @(posedge clk) disable iff (!rst_n)
    bus_reset |=> (!txn_open && !token_overrun);
endproperty
assert property (p_bus_reset_closes);

C1 is the property this chapter exists for, and its throughout is doing the work. The obvious formulation — one cycle after a token, the context equals the token — passes on X1, because X1's error is not in the capture but in the persistence: the value is briefly right and then drifts to whatever the decoder is showing.

A property about state must span the state's lifetime, not its first cycle.

D1 is the property that sounds redundant and is not. It reads like a restatement of one assignment. It is the only statement in the set that constrains the direction half of the context, and §6's X5 satisfies every other property here.

8. Verification

This chapter's commit point is every packet after the token was interpreted against what that token actually said.

Stimulus. An OUT transaction; an IN transaction; a SETUP; data packets with no transaction open — four of them consecutively, which is the case X2 needs; a second token arriving mid-transaction; an abort followed immediately by a data packet; a twelve-cycle gap between token and data; a bus reset mid-transaction; and 600 randomised events mixing tokens, data presentations, completions and aborts.

Observation. txn_open, the whole captured context, and the acceptance permission — every cycle, not merely at stage boundaries, because X1's error lives entirely between them.

Reference model. Four variables updated by the same rules stated in §1. Its value here is limited and worth being honest about: it is structurally similar to the design. The checks that actually catch things are the two obligations — no data without a transaction and no data during an IN — which are stated in the protocol's terms and not in the design's.

Coverage — crosses:

  • token type (IN / OUT / SETUP) × transaction already open or not
  • gap length between token and data: 0, 1, 2, 12 cycles
  • ending type (done / abort / bus reset / superseding token) × each stage
  • data presented with: no transaction, an IN open, an OUT open, an aborted one
  • endpoint number 0 to 15 × each direction

Negative cases with defined outcomes: no data accepted with no transaction; none during an IN; txn_open never rises without a token; the context never changes without one; and nothing survives a bus reset.

9. Debugging: the Device That Only Works on Endpoint 0

A device enumerates perfectly and every control transfer succeeds. Its bulk endpoints do not work at all — data sent to EP2 appears to arrive at EP0, or is silently lost. The failure is completely reproducible.

What does enumeration works tell you? That the token filter, the data path, the handshake and the toggle are all functional. Enumeration exercises nearly everything — Chapter 10.2 — so a device that enumerates has most of its stack working.

What is different about enumeration? It is entirely endpoint 0. Chapter 9.3 §2: EP0 exists before any configuration, and every control transfer uses it.

So what breaks when the endpoint number is not zero? Anything that reads an endpoint number from somewhere it should not. §6's X1: a device reading its token decoder live, between packets, sees idle — which decodes to endpoint 0.

Why does that look like data "arriving at EP0"? Because it does. The endpoint number the device used was the one it read after the token was over, and the idle decode is stable and consistent — §4's point that the value is plausible rather than random.

What is the fastest confirming test? Send to EP1 and EP2 and see whether both land in the same place. A wrong-but-consistent destination is a captured-value problem; a random one is a timing problem. They look similar in a log and have different causes.

What if the data is lost rather than misdelivered? Then the direction is also wrong — §6's X5. The device decoded an idle bus as an IN, prepared to transmit, and discarded an inbound packet it should have accepted. Look for the device driving the bus during a stage where it should be listening, which a differential probe shows immediately and a protocol analyser often does not.

And why is it completely reproducible? Because the idle decode is deterministic. This class of bug does not present as flakiness — which misleads, because reproducible suggests a logic error and this is a lifetime error.

The signature to keep: works on endpoint 0 and nowhere else means a value was read after it stopped being valid — and idle decodes to zero.

10. Common Misconceptions

11. Reason It Through

A device works perfectly when it is the only thing plugged in. On a hub with other active devices it corrupts transfers — but only occasionally, and only under load. Its own traffic pattern does not affect the rate; other devices' traffic does.

What does other devices' traffic matters and mine does not tell you? That the device is being affected by packets that are not addressed to it. Its own load would matter if the fault were in its own datapath.

Which packets does a device see that are not its own? All of them. Chapter 11.1 §1: every device sees every packet, and filtering is the device's job.

So which filter could be leaking? Two candidates, and they are distinguishable:

  • The address filter — Chapter 11.1 §6's T2. The device would accept other devices' tokens, and the corruption would track the number of other devices.
  • The transaction-context requirement — §6's X2. The device accepts other devices' data packets, in the gaps of its own transactions, and the corruption tracks other devices' traffic volume.

How would you tell them apart? Add a second device that is attached but idle. An address-filter fault gets worse; a context fault does not, because an idle device generates no data packets.

Why is it only occasional? Because it needs a coincidence: another device's data packet arriving while this device has no transaction open but is listening. Load raises the probability and does not create the mechanism.

Is there a third candidate? Yes — the device might be correct and the hub faulty. Testable by moving it to a direct port. Rule out the cheap external hypothesis before opening the RTL, because the cost of being wrong in that direction is a week.

And the transferable point: a fault whose rate tracks somebody else's activity is a filtering fault, and the filter in question is whichever one distinguishes this device's traffic from traffic this device can see. On a shared bus those are different sets, and every device is responsible for the difference.

12. Understanding Check

13. Summary

The token stage transfers nothing. It creates a context — endpoint, direction, transaction type — and every packet after it is interpreted in that context while carrying none of it. The token is said once and cannot be asked again.

The obligations it creates are asymmetric. The host has committed bus time and can abandon the transaction at any moment; the device has committed to respond within a bounded window and cannot withdraw — its only option is silence, which means it must survive transactions that stop with nothing on the wire marking the end.

A data packet outside a transaction means nothing, literally: no address, no endpoint, no direction exists anywhere for it. It is discarded silently, and it happens routinely — a corrupted token, another device's traffic, or this device's own abort.

Which makes capture, not observation, the design's centre. Chapter 11.1's decoder is combinational; the token's contents exist after the packet only in registers you wrote. §6 measured the alternative at 340 wrong endpoints and 213 wrong directions — and the failure is not randomness: idle decodes to a stable, plausible endpoint 0, which is why such a device enumerates flawlessly and fails on every other endpoint.

Five mutations, each with a distinct signature: no capture (everything wrong), no context required (38 foreign packets absorbed, with the handshake landing in another device's transaction), a second token ignored (78 overruns and a stale context the host has already left), a transaction that never closes (a stray packet read against the previous transaction), and direction not captured — which is the interesting one: every payload at the right endpoint, 213 wrong directions, and a destination-checking bench passes.

And §8 adds a fourth shape to Module 11's catalogue of benches that pass wrongly: not an unreachable state, a shared misunderstanding, an empty negative space or a missing anchor, but a compressed timeline.

A bench that runs a protocol faster than the protocol runs removes the very intervals the design exists to survive.

14. What Comes Next

The context is open. Chapter 12.2 is the Data Stage, where something finally moves — in the direction the token specified, from whichever end the token made the transmitter.

Its subject is not the data packet, which Chapter 11.2 already built. It is the rule that decides when a transfer is over, and it is not a length: a packet shorter than the endpoint's maximum ends the transfer, which makes a zero-length packet a meaningful message rather than an empty one. The Linux kernel carries both halves as flags — URB_SHORT_NOT_OK and URB_ZERO_PACKET — and a hardware quirk bit for controllers that get the rule wrong.

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.