Skip to content
VLSI Mentor

USB · Module 10

Bulk Transfers

Promised nothing and permitted everything: why throughput and guarantee are different axes, and why a fixed-priority arbiter starves an endpoint forever while every safety property passes.

Chapter 10.2 §3 showed the budget as a chain of guarantees: the periodic cap guarantees a non-periodic residue exists, and control traffic is protected within it. Bulk was at the end of that chain, described in three words — whatever survives both.

That is the whole of Bulk's service contract, and it produces an apparent contradiction:

Bulk is promised nothing, and on most buses it moves more data than every other type combined.

Understanding why those are compatible is this chapter, and it turns on a distinction that has come up twice already: throughput and guarantee are different axes.

1. What Bulk Is Not Promised

Start with the absences, because they are the definition.

No reservation. Chapter 10.1 §5 established that periodic types consume budget from configuration until un-configuration. Bulk consumes none — it asks for nothing in advance and is admitted unconditionally.

No service interval. An interrupt endpoint's configuration states how often it wants to be visited. A bulk endpoint states no such thing, and the host owes it no particular visit at any particular time.

No bounded latency. A bulk transfer may be served immediately or may wait, and nothing in the protocol caps the wait. A device that has been ignored for a long time has no complaint to make.

What it does get is correctness. A bulk transfer's data is delivered intact or the error is visible, and a failure can be retried — Chapter 10.1 §2's grid places Bulk firmly in the retry is useful column.

2. Throughput and Guarantee Are Different Axes

The distinction this chapter exists to establish, because the stereotype Bulk is slow comes from collapsing them.

Throughput is how much data moves per unit time, averaged. It depends on how much capacity is available and how efficiently the flow uses it.

A guarantee is a promise about a particular moment. It says you will be served within this interval, or this much capacity is yours.

They are independent, and all four combinations exist:

Has a guaranteeHas no guarantee
High achievable throughputIsochronous — reserved, and reservations can be largeBulk — takes the residue, which is usually most of the bus
Low achievable throughputInterrupt — reserved, but the reservation is typically smallControl — protected, but management traffic is rare by nature

Bulk sits in the top-right, and the stereotype puts it in the bottom-right.

The practical consequence is the one that matters in design: a device needing to move a lot of data should use Bulk precisely because it has no guarantee. Asking for a guarantee means asking for a reservation, reservations are capped, and a reservation large enough to carry bulk-scale data would consume the cap on its own — and then be wasted whenever the device was idle.

3. What Bulk Gives Up, and When It Hurts

The trade has a cost, and being precise about when it is paid matters more than the trade itself.

On a quiet bus, Bulk costs nothing. The residue is large, service is prompt, and a bulk endpoint is effectively served on demand.

On a busy bus, latency becomes unbounded and variable. Not merely long — variable, which is worse for anything downstream that has to size a buffer or a timeout.

And the sources of contention are worth separating, because they behave differently:

  • Periodic traffic elsewhere on the bus shrinks the residue by a fixed amount for as long as those devices are configured. Predictable, and visible in advance from what is attached.
  • Other bulk traffic shares the residue dynamically. Unpredictable, and it is what §4's arbitration is about.
  • Control traffic takes its protected share, which is small and rare.

The one a device engineer controls is the second, and it is the one that produces the module's most instructive failure.

4. Sharing the Residue

Several bulk endpoints — on one device or across devices — compete for the same leftover capacity. Something decides the order, and the decision has consequences that outlast any single transfer.

The naive answer is fixed priority. Serve the lowest-numbered pending endpoint. It is trivially simple, it is what a first implementation produces, and it has a failure mode that is total rather than gradual.

A continuously-busy high-priority endpoint starves every lower one, forever. Not slowly — never. And the starved endpoint is not broken, is not reporting an error, and has data waiting the whole time.

Round-robin fixes it by remembering where the last grant went and resuming from the next candidate. The cost is one register.

A comparison of two arbitration policies for bulk endpoints sharing leftover bus capacity. Under fixed priority, three pending endpoints are considered in a fixed order and the lowest-numbered one is always chosen, so a continuously busy endpoint means the other two are never served at all despite having data ready. Under round-robin, a stored pointer records where the previous grant went and the search resumes from the next candidate, so each pending endpoint is served in turn and the delay any endpoint experiences is bounded by the number of competing endpoints.Residuewhat periodic and controlleaveFixed priorityalways the lowest pendingEP1 always · EP2, EP3neverstarvation — permanent,silentRound robinresume after the last grantEP1, EP2, EP3, EP1 …bounded delay — oneregisterwho gets it?who gets it?resultresult12
Figure 1 — the same three pending endpoints under two policies. Fixed priority serves the first one indefinitely while the others wait with data ready; round-robin's single stored pointer is the whole difference, and it converts a permanent failure into a bounded delay.
A sequence diagram of five consecutive service opportunities on a bus with three bulk endpoints pending continuously and one periodic endpoint. In the first opportunity the scheduler finds no periodic obligation due and grants the first bulk endpoint. In the second it grants the second bulk endpoint. In the third a periodic obligation is due, so the periodic endpoint is served and no bulk endpoint is granted; the rotation pointer does not move. In the fourth opportunity the third bulk endpoint is granted. In the fifth the rotation returns to the first bulk endpoint. An annotation notes that under fixed priority every one of these opportunities would have gone to the first bulk endpoint.Who gets the next opportunityService opportunityArbiterBulk endpointsopportunity 1 — noperiodic duegrant EP1 · pointer→ EP2opportunity 2 — noperiodic duegrant EP2 · pointer→ EP3opportunity 3 —PERIODIC DUEperiodic wins · nobulk grant · pointerunchangedopportunity 4 — noperiodic duegrant EP3 · pointer→ EP1opportunity 5 — noperiodic duegrant EP1 — therotation closesunder fixedpriority: all fivewent to EP1
Figure 2 — five consecutive service opportunities at the service level. The periodic obligation takes the third one outright; the remaining four are shared among the bulk endpoints in turn. Note that the opportunity consumed by periodic traffic does not use up anybody's turn — the rotation resumes exactly where it was.

5. The Opportunistic Arbiter, as RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// usb_bulk_arbiter
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It models the
// selection of one bulk endpoint from several pending ones, and the
// precedence of a periodic obligation over all of them.
//
// WHAT IT MODELS. Section 3's two rules: opportunistic traffic runs only
// when no periodic obligation is due, and among competing opportunistic
// requesters the choice is round-robin so that none is starved.
//
// WHAT IT DOES NOT MODEL -- AND THIS LIST MATTERS. This is NOT a USB host
// scheduler. It does not model frames or microframes, bandwidth accounting,
// the admission of periodic reservations, transaction boundaries (Module
// 12), packets (Module 11), split transactions, multiple devices, or the
// order in which a real host walks its schedule. It models ONE decision --
// which requester is selected when an opportunity exists -- because that
// decision is where starvation is introduced, and starvation is the lesson.
//
// A real host controller's scheduling is a substantially larger subject
// that later modules and Module 22 own.
// ─────────────────────────────────────────────────────────────────────────
module usb_bulk_arbiter #(
  parameter int unsigned N_REQ = 4
)(
  input  logic clk,
  input  logic rst_n,

  // A service opportunity exists this cycle. In a real controller this
  // arrives from the scheduler; here it is simply "there is room to do
  // something".
  input  logic opportunity,

  // A periodic endpoint is owed service now. Section 3: periodic
  // obligations are reservations that have come due, and opportunistic
  // traffic must not displace them.
  input  logic periodic_due,

  // Which opportunistic requesters have data waiting.
  input  logic [N_REQ-1:0] req,

  output logic [N_REQ-1:0] grant,       // one-hot, or zero
  output logic             grant_valid,
  output logic             periodic_win // the opportunity went to periodic
);

  localparam int unsigned PTR_W = (N_REQ > 1) ? $clog2(N_REQ) : 1;

  // WHERE THE LAST GRANT WENT. This one register is the entire difference
  // between round-robin and fixed priority (section 4), and section 6
  // measures what its absence costs.
  logic [PTR_W-1:0] rr_ptr;

  logic [N_REQ-1:0] sel;
  logic             found;

  // ── Round-robin selection ───────────────────────────────────────────────
  // Walk the requesters starting from rr_ptr and take the first pending
  // one. Written as an explicit ordered search rather than as a rotated
  // priority encoder because the ordering is the point: a reader can see
  // that the starting position moves, which is what prevents starvation.
  always_comb begin
    int unsigned idx;              // block-scoped: no lifetime override, so
                                   // every tool in the flow accepts it
    sel   = '0;
    found = 1'b0;
    idx   = 0;
    for (int unsigned i = 0; i < N_REQ; i++) begin
      // (rr_ptr + i) modulo N_REQ -- the rotation, and the only thing that
      // separates this from a plain priority encoder.
      idx = (rr_ptr + i) % N_REQ;
      if (!found && req[idx]) begin
        sel[idx] = 1'b1;
        found    = 1'b1;
      end
    end
  end

  // ── The decision ────────────────────────────────────────────────────────
  // PRIORITY: a periodic obligation beats every opportunistic requester.
  // This is section 3's rule and it is the one mutation in section 6 that
  // breaks a guarantee rather than merely degrading service.
  assign periodic_win = opportunity &&  periodic_due;
  assign grant_valid  = opportunity && !periodic_due && found;
  assign grant        = grant_valid ? sel : '0;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      rr_ptr <= '0;
    end else if (grant_valid) begin
      // Resume AFTER the endpoint just served. Advancing past the grant --
      // rather than to it -- is what makes the served endpoint last in line
      // next time, which is the fairness property.
      for (int unsigned i = 0; i < N_REQ; i++)
        if (sel[i]) rr_ptr <= (i + 1) % N_REQ;
    end
  end

endmodule

What it models. One selection decision: periodic precedence, and round-robin among opportunistic requesters.

Engineering reason. Because the decision is where starvation is introduced, and starvation is a failure that produces no error anywhere.

Inputs. Clock and reset, a service opportunity, a periodic-due indication, and the pending requesters.

State retained. One pointer — $clog2(N_REQ) bits. For four requesters, two flip-flops.

Outputs. A one-hot grant with its validity, and an indication that the opportunity went to periodic traffic.

Hardware implied. A small ordered search — a rotated priority encoder in practice — plus the pointer register.

Reset behaviour. The pointer returns to zero. Nothing else is retained, so there is no stale-grant state to clear.

Assumptions. That req is stable during the cycle a grant is evaluated; that opportunity and periodic_due come from whatever owns the schedule; and that a granted requester is actually served, since the pointer advances on the grant.

Omissions. Everything in the header's list — and the list is long deliberately, because a block named arbiter invites being read as a scheduler.

What DV should verify. That a grant is one-hot or zero; that no grant occurs without a request; that no grant occurs while a periodic obligation is due; that a continuously-pending requester does not prevent others from being served; that every pending requester is eventually granted; and that the pointer advances past the granted requester rather than to it.

Round-robin over the residue — controller-domain view

8 cycles
A waveform of the bulk arbiter over eight cycles. In the first cycle no service opportunity exists and nothing is pending, so there is no grant. In the second, third, fifth, sixth and seventh cycles an opportunity exists with no periodic obligation due, and the arbiter grants endpoint zero, endpoint one, endpoint two, endpoint three and endpoint zero again in rotation, with the round-robin pointer advancing past each granted requester. In the fourth cycle an opportunity exists but a periodic obligation is due, so the periodic-win output asserts, no bulk endpoint is granted, and the pointer does not move. In the sixth cycle endpoint two has withdrawn its request, and the search continues past it to endpoint three. In the eighth cycle nothing is pending, so despite an opportunity there is no grant.rotation begins at EP0rotation begins at EP0periodic wins — pointer does NOT moveperiodic wins — pointerdoes NOT moveEP2 withdrew; the search continues to EP3EP2 withdrew; the searchcontinues to EP3opportunity, nothing pending — no grantopportunity, nothingpending — no grantopportunityperiodic_duereq[3:0]00001111111111111111101100110000grant_validgrantEP0EP1EP2EP3EP0periodic_winrr_ptr00122301t0t1t2t3t4t5t6t7
Figure 3 — eight cycles of the arbiter, with every column taken from a simulation of the block in §5 rather than drawn by hand. Watch the pointer: it advances past each granted requester, it does not move on the cycle periodic traffic wins, and when EP2 withdraws before its turn the search simply continues to EP3 rather than stalling. These are controller-domain decoded events, not USB bus signalling, and the figure depicts no real durations.

6. Mutation Test

Four mutations, run against the block in §5 and the checks in §7. The measurement bench applies 294 evaluated cycles across seven stimulus phases; the numbers below are what it reported.

The unmutated block first, so the mutant columns mean something:

A1 one-hotA2 grant≤reqA3 periodicA4 opportunityF1 fairnessref model
golden000000
B1 fixed priority00005494
B2 bulk beats periodic00410072
B3 pointer to, not past00005199
B4 grant without opportunity00044072

The golden block also distributed exactly 10 grants to each of the four requesters over the 40-opportunity sustained-contention phase, which is the arithmetic round-robin promises.

B1 — fixed priority instead of round-robin

Remove the pointer and always search from zero.

Measured. Over the 40 opportunities with all four requesters pending continuously: EP0 = 40, EP1 = 0, EP2 = 0, EP3 = 0. Three requesters with data ready, never served once.

And every safety property passed — A1, A2, A3 and A4 all reported zero violations, exactly as the table shows. Every grant was one-hot, every grant had a request behind it, no grant occurred without an opportunity, and no periodic obligation was displaced.

F1 reported 54 violations. It is the only property that fired, and §7 is about why the natural ones cannot substitute for it.

B2 — let opportunistic traffic win over a periodic obligation

Invert the precedence.

Measured. A3 fired 41 times; F1 stayed clean at zero. That split is the interesting part: the arbiter remained perfectly fair and still broke a promise. Fairness among the bulk endpoints was never the issue — the reservation belonged to somebody who was not in the rotation at all.

This breaks a guarantee rather than degrading a service. The interrupt or isochronous endpoint was promised a visit, capacity was reserved for it, and the reservation is being spent on traffic that asked for nothing.

And the symptom is remote from the cause, which is what makes it expensive in the lab: the bulk device works perfectly — better than perfectly — and some other device's periodic endpoint misses its interval. On a bus with several devices the two may have nothing to do with each other.

B3 — advance the pointer to the granted requester rather than past it

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (sel[i]) rr_ptr <= i;   // MUTANT B3: to, not past

Measured. Over the same sustained-contention phase: EP0 = 40, EP1 = 0, EP2 = 0, EP3 = 0bit-for-bit the same distribution B1 produced. A1 through A4 clean; F1 fired 51 times.

The mechanism is that the search restarts at the requester just served, which is still pending, so it is immediately selected again. This is fixed priority with extra steps, and it produces exactly B1's starvation while appearing to implement round-robin.

The off-by-one is the entire fairness property, and it is invisible in code review unless you know to look: the line reads plausibly, the register is present, and a reviewer sees round-robin.

B4 — grant without an opportunity

Remove the opportunity term.

Measured. A4 fired 44 times; F1 stayed clean.

Grants are issued on cycles when no service is available, so a requester is marked served when nothing happened — and the pointer advances, moving that endpoint to the back of the queue for a transfer that never occurred.

A grant that does not correspond to service is worse than no grant, because the fairness mechanism is being driven by fictional events. F1's silence is the point: fairness over fictional grants is still fairness, and it is worthless.

7. The Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Classification: TEACHING ASSERTIONS about the arbiter.
// A-properties are SAFETY; F-properties are FAIRNESS, which is a bounded
// form of progress. Section 6 measured why safety alone is insufficient.
// ─────────────────────────────────────────────────────────────────────────

// A1 -- ONE-HOT. At most one requester is selected.
property p_grant_onehot;
  @(posedge clk) disable iff (!rst_n)
    grant_valid |-> $onehot(grant);
endproperty
assert property (p_grant_onehot);

// A2 -- NO GRANT WITHOUT A REQUEST. The granted requester must have been
// asking. Catches a selection that ignores `req` entirely.
property p_grant_implies_request;
  @(posedge clk) disable iff (!rst_n)
    grant_valid |-> ((grant & req) == grant);
endproperty
assert property (p_grant_implies_request);

// A3 -- PERIODIC PRECEDENCE. Section 3's rule, and the one whose violation
// breaks a promise made to a different endpoint (section 6's B2).
property p_periodic_wins;
  @(posedge clk) disable iff (!rst_n)
    (opportunity && periodic_due) |-> (!grant_valid && periodic_win);
endproperty
assert property (p_periodic_wins);

// A4 -- NO GRANT WITHOUT AN OPPORTUNITY. A grant must correspond to real
// service, or the fairness mechanism is driven by events that did not
// happen (section 6's B4).
property p_grant_implies_opportunity;
  @(posedge clk) disable iff (!rst_n)
    grant_valid |-> opportunity;
endproperty
assert property (p_grant_implies_opportunity);

// ── FAIRNESS ────────────────────────────────────────────────────────────
// F1 -- BOUNDED FAIRNESS. A requester that stays pending is granted within
// N_REQ opportunities. Bounded deliberately: an unbounded "eventually" is
// not checkable in simulation, and the bound is exactly what round-robin
// promises -- every other requester goes at most once before this one does.
//
// This is the ONLY property that catches section 6's B1 and B3, both of
// which satisfy every safety property above.
generate
  for (genvar gi = 0; gi < N_REQ; gi++) begin : g_fair
    // The genvar is elaborated into the property rather than passed as an
    // argument: an index that is constant at elaboration keeps this within
    // the subset every tool in the flow accepts.
    property p_bounded_fairness;
      @(posedge clk) disable iff (!rst_n)
        (req[gi] && opportunity && !periodic_due)
          |-> ##[0:N_REQ-1] (grant[gi] || !req[gi]);
    endproperty
    assert property (p_bounded_fairness);
  end
endgenerate

A1 to A4 are satisfied completely by a fixed-priority arbiter, which is §6's measurement stated as a property fact. They constrain what a grant means and say nothing about who gets one.

F1 is the fairness property, and three things about its form are deliberate:

  • It is bounded. An unbounded eventually cannot fail in a finite simulation, so it would pass vacuously on B1 forever. The bound is N_REQ opportunities, which is exactly what round-robin promises.
  • Its antecedent is the request, not the grant. Chapter 9.5 §7 measured a progress property gated on the very signal whose failure it was meant to detect. Gating this on grant would make it vacuous under precisely B1.
  • It excuses a requester that withdraws. || !req[i] covers a requester that stops asking, which is not a fairness violation.

8. Verification

This chapter's commit point is the opportunity went to the right requester.

Stimulus. One requester pending; all requesters pending continuously — the case B1 and B3 need; requesters arriving and withdrawing; a periodic obligation coincident with pending requesters; an opportunity with nothing pending; and a reset with the pointer at each value.

The stimulus requirement §6 makes non-negotiable: all requesters pending simultaneously and continuously, for longer than N_REQ opportunities. Starvation is invisible with one requester, invisible with requesters that take turns naturally, and invisible in a short run. It requires sustained contention, which is the condition a functional test rarely creates and a real bus creates constantly.

Observation. Which requester was granted, across time. A single-cycle observation cannot see starvation at all — the defect is entirely in the distribution of grants over a window.

Reference model. A pointer and an ordered search, which is a five-line model. Its value is that it is written from the fairness rule rather than from the RTL, so an off-by-one in the pointer update — §6's B3 — shows up as a divergence rather than being reproduced.

And the measurement makes an argument for writing it. The reference model diverged on all four mutations — 94, 72, 99 and 72 mismatching cycles respectively — while no single property caught more than one of them. That is not an accident of this design:

  • A property encodes one rule, and each of §6's mutants breaks a different one.
  • A model written from the specification encodes the whole rule at once, so it diverges whenever the implementation departs from the specification in any direction, including directions nobody wrote a property for.

The corollary is the one worth keeping: properties tell you which rule broke, which is what you need while debugging; a reference model tells you that something broke, which is what you need to find out at all. Neither replaces the other, and a bench with only properties is only as complete as the list of rules somebody thought to write down.

Coverage — crosses:

  • number of simultaneous requesters: 0, 1, 2, all
  • periodic due × each number of pending requesters
  • opportunity present × absent, with requests pending
  • pointer value × which requesters are pending — all combinations for small N_REQ
  • a requester withdrawing between the request and the grant

Negative cases with defined outcomes: no grant without an opportunity; no grant without a request; no grant while a periodic obligation is due; and no requester starved over a window of N_REQ opportunities.

9. Debugging: the Endpoint That Is Never Served

A device has two bulk IN endpoints. One works. The other has data continuously available and the host never collects any of it. No error is reported anywhere.

What does no error tell you? That nothing thinks it has failed. The endpoint has data, the host is not asking, and neither side considers that a fault — Chapter 9.4 §2 established that a device with data simply waits.

What is the first observation? Not the endpoint. The grants. Which requester is being selected, over a window long enough to see a pattern.

What does the pattern tell you? If the working endpoint is granted every opportunity, the arbitration is unfair and §6's B1 or B3 is the cause. If grants alternate correctly but the second endpoint's transfers fail, the problem is in the endpoint, not the arbiter.

How do you distinguish B1 from B3? By reading the pointer. B1 has none; B3 has one that does not advance past the granted requester. Both produce identical grant patterns, which is §6's point — the behaviour is the same and only the code differs.

What would a protocol analyser show? Traffic to one endpoint and none to the other. Correct traffic, correctly formed — the host is addressing what it was told to address. The absence is the evidence, and absences are hard to see in a trace full of activity.

And the signature to keep: an endpoint with data that is never addressed, with no error anywhere, is an arbitration failure — and it is diagnosed by looking at the distribution of service rather than at the endpoint that is not receiving it.

10. Common Misconceptions

11. Reason It Through

A device's bulk endpoint achieves its rated throughput on a bus by itself. Plugged into a hub alongside a webcam, throughput drops by more than half — and the drop is larger than the webcam's own data rate.

Why would the loss exceed the webcam's data rate? Because the webcam is not merely consuming bandwidth — it is reserving it. Chapter 10.1 §5: periodic reservations are held from configuration until un-configuration, used or not.

So what is the bulk device actually losing? The reservation, not the usage. If the webcam reserves capacity for its worst-case frame and typically sends less, the difference is unavailable to anyone — reserved for a flow that is not using it.

Is that a defect? No. It is the price of the guarantee, and Chapter 10.2 §3's chain is explicit: a guarantee to one party is a restriction on everybody else. The webcam's reservation is what makes its stream work.

What can the bulk device do about it? Nothing, and that is worth being clear about. It has no reservation to defend and no priority to raise. Its contract is the residue.

What can the system do? Move one device to a different bus, or — if the webcam is the engineer's own device — use Chapter 7.3 §3's alternate settings so it reserves nothing while idle. That is what alternate settings are for, and this is the scenario that makes them worth the complexity.

And the transferable point? Reserved capacity is consumed by the reservation, not by the traffic. A flow that reserves generously and sends sparsely hurts every non-reserved flow on the bus by the full reservation — which is why asking for a guarantee should be proportionate to what is actually needed rather than to the worst case anyone can imagine.

12. Understanding Check

13. Summary

Bulk is defined by its absences: no reservation, no service interval, no bounded latency. What it gets is correctness and retryability.

And the apparent contradiction resolves in one sentence: Bulk is promised no share of the bus and permitted all of it. Nothing reserves capacity for it and nothing caps it, so on a typical bus it takes nearly everything — which is why a storage device saturates a link while a reserved endpoint moves a few bytes per frame.

Throughput and guarantee are independent axes, and the stereotype Bulk is slow collapses them. A device needing high throughput should use Bulk because it has no guarantee: guarantees mean reservations, reservations are capped, and one large enough for bulk-scale data would consume the cap and be wasted whenever the device was idle.

The cost is variable, unbounded latency, which is harder than long latency for anything sizing a buffer downstream.

Sharing the residue is where the module's most instructive defect lives. §6 measured four mutations: fixed priority starves every lower requester permanently with every safety property passing; inverted precedence spends a reservation on traffic that asked for nothing, with the symptom appearing on a different device; advancing the pointer to the granted requester instead of past it produces identical starvation while keeping every structural sign of fairness — and is therefore invisible in review; and granting without an opportunity drives the fairness mechanism with events that did not happen.

Which forces the module's verification lesson: fairness is behavioural, not structural. It cannot be confirmed by reading code, and the only check that catches both starvation mutations is a bounded fairness property observing grants over a window — bounded because an unbounded eventually passes vacuously forever, and gated on the request rather than the grant because Chapter 9.5 measured what gating on the wrong signal costs.

14. What Comes Next

Bulk asked for nothing. The next type asks for something very specific, and the thing it asks for is not bandwidth.

Chapter 10.4 is Interrupt, and it begins by dismantling its own name: the device does not interrupt anything. USB remains host-driven, and what an interrupt endpoint actually buys is a promise about how often it will be asked — which is a guarantee about opportunity, not about data.

That distinction is the chapter, and it explains why a mouse producing a few bytes per second needs a reservation at all.

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.