Skip to content
VLSI Mentor

USB · Module 2

The USB Device

What is left when every decision belongs to the host: the responder discipline, why a device must answer even when it has nothing to say, the split between the USB-facing device controller and the function it exists to provide, and a teaching abstraction with the assertion that protects the ownership model.

Chapter 2.1 gave every decision on the bus to the host: addressing, configuration, bus time, power, error policy. That invites an unflattering conclusion about the other participant — if the device decides nothing, surely it is trivial?

It is not, and the reason is worth stating before the detail. Subordinate is not the same as simple. A device must maintain state the protocol can see, behave correctly according to that state, answer within expectations it did not set, distinguish its USB-facing machinery from the actual product it exists to be, and do all of this under a reset it does not control. Those are real design obligations, and getting the first of them wrong produces a device that fails on real hosts while passing its own testbench.

This chapter also draws the distinction that matters most to an RTL engineer in Module 2: the device controller is not the device function.

1. Two Things Inside One Box

An engineer says “USB device” and means a physical object — a keyboard, a drive, a camera. An architecture says something more precise, and the precision is what makes RTL partitioning possible.

A USB device decomposed into layers. At the bus end, a device PHY handles electrical signalling. Above it, the device controller implements USB-facing protocol behaviour and holds protocol-visible state. Above that, device firmware supplies identity and policy. At the top, the device function is the actual useful behaviour of the peripheral, such as storage, input or capture. The controller transports the function's data; it is not itself the function.Device functionthe product — storage, input, capture, audioDevice firmwareidentity, configuration policy, class behaviourDevice controllerUSB-facing protocol engine + protocol-visible stateDevice PHYelectrical signalling — Module 3 owns thisdata and eventsconfiguration andbuffersprotocol boundary12
Figure 1 — the controller transports; the function is the product. Confusing the two is how USB logic ends up entangled with application logic.

The device function is why the product exists. It is the storage medium, the key matrix, the image sensor, the audio converter. It has nothing to do with USB and would exist, in some form, behind any interface.

The device controller is the USB-facing engine. It holds the state the protocol can observe, answers the host, and moves the function's data across the bus. It knows nothing about images or filesystems.

The reason this is an architectural distinction rather than a naming convention is that it predicts a reuse boundary. A device controller is worth designing once and reusing across many products, because it implements the layers that do not vary per product — exactly the argument Chapter 1.7 made about where an architecture should place variety. Entangle the two and you have built a USB controller that only works for one product, which is the most expensive possible outcome.

2. The Responder Discipline

Now the behavioural rule, in the form the architecture actually imposes.

A device does not act on its own state. A keyboard with a keystroke buffered, a drive with a sector ready, a sensor with a fresh sample — none of them causes bus activity. Each holds what it has until the host addresses it, and only then does anything happen.

Put as a design rule for the engineer building the controller:

The device's outputs on the bus are a function of host-originated requests, not of the function's internal readiness.

That sentence is the one to keep, because it is falsifiable in RTL. A device-side design in which the function's data_ready can reach the bus-driving logic without passing through a host request has violated the architecture, and the violation is structural rather than a matter of timing or degree.

Chapter 2.1 §3 scoped this across generations, and the scoping carries here: in USB 3.x a device may asynchronously notify the host that an endpoint has become ready. That is the device influencing when it is asked — not the device transferring on its own authority. The rule above survives, because a notification is a request to be asked rather than an act of sending.

3. Answering Is Mandatory — Including “Nothing Right Now”

Here is the part that catches designers who reason from the word “responder” alone.

When a host addresses a device asking for data, and the device has none, the device does not fall silent. Silence is not an answer. The host cannot distinguish a device that has nothing to say from a device that has failed, been unplugged, or never received the request — so an architecture that allowed silence as a legitimate response would make every empty poll indistinguishable from an error.

So a device must answer in all three cases: here is your data, I have nothing for you right now, and, where applicable, I am busy or cannot comply. A negative answer is a successful protocol exchange. It is information.

This has a direct hardware consequence that the RTL below makes visible: the controller's response path must be able to produce an answer with no help from the function at all. If the only way to emit a response is to have data from the function, the device becomes silent exactly when the function is empty — which is most of the time for an input device, and is a bug that only appears against a real host.

USB has specific names and encodings for these answers. Module 11 owns packet formats and Module 12 owns the transaction model; this chapter deliberately uses plain words, because the architectural obligation exists independently of how it is encoded.

4. The Discipline as Hardware

This is a small piece of teaching RTL whose only purpose is to make §2 and §3 inspectable. Read the classification line carefully — it is doing real work.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// responder_discipline
//
// Classification: CONCEPTUAL ARCHITECTURE ABSTRACTION. Synthesizable, but
// it is NOT USB and must never be read as a USB device controller.
//
// WHAT IT IS. A minimal model of ONE architectural invariant: a responder
// emits on the bus only in answer to a request addressed to it, and it
// always answers -- with data when it has data, and with an explicit
// "nothing right now" when it does not.
//
// WHAT IT IS NOT. There are no USB packets, tokens, handshakes, endpoints,
// addresses, CRCs, bit stuffing, speeds or device states here, and the
// signal names deliberately avoid USB's vocabulary so this cannot be
// mistaken for a protocol implementation. USB's real encodings of these
// answers belong to Modules 11 and 12; endpoints to Module 9; device
// states to Module 8; an actual device controller to Module 21.
//
// A real USB device controller is several orders of magnitude larger than
// this. What survives from this example is the SHAPE of the ownership rule,
// which is what section 5's assertions protect.
// ─────────────────────────────────────────────────────────────────────────
module responder_discipline #(
  parameter int PAYLOAD_W = 8
) (
  input  logic                  clk,
  input  logic                  rst_n,

  // ── Bus-facing request side ────────────────────────────────────────────
  // req_valid: a request is present on the bus this cycle.
  // req_for_me: that request is addressed to THIS responder. Abstracts
  //             address decode; it is not a USB address comparison.
  input  logic                  req_valid,
  input  logic                  req_for_me,

  // ── Function-facing side ───────────────────────────────────────────────
  // The function offers data whenever it happens to have some. Note that
  // fn_valid NEVER reaches the bus outputs except through a request -- that
  // is the whole architectural point.
  input  logic                  fn_valid,
  input  logic [PAYLOAD_W-1:0]  fn_data,
  output logic                  fn_take,      // 1-cycle: data consumed

  // ── Bus-facing response side ───────────────────────────────────────────
  output logic                  rsp_valid,    // 1-cycle: an answer is issued
  output logic                  rsp_has_data, // qualifies rsp_data
  output logic [PAYLOAD_W-1:0]  rsp_data,
  output logic                  rsp_nothing   // the explicit empty answer
);

  typedef enum logic [0:0] {
    S_IDLE,    // armed; may not drive the bus
    S_ANSWER   // a request is owed exactly one answer
  } state_e;

  state_e state_q;
  logic   take_data_q;               // decided at request time, not answer time

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state_q      <= S_IDLE;
      take_data_q  <= 1'b0;
      fn_take      <= 1'b0;
      rsp_valid    <= 1'b0;
      rsp_has_data <= 1'b0;
      rsp_nothing  <= 1'b0;
      rsp_data     <= '0;
    end else begin
      // Every bus-facing output is a single-cycle pulse.
      fn_take      <= 1'b0;
      rsp_valid    <= 1'b0;
      rsp_has_data <= 1'b0;
      rsp_nothing  <= 1'b0;

      unique case (state_q)
        S_IDLE: begin
          // THE INVARIANT. The only path out of idle is a request addressed
          // to us. fn_valid appears nowhere in this condition, and that
          // omission is the architecture.
          if (req_valid && req_for_me) begin
            state_q     <= S_ANSWER;
            // Sample the function's readiness AT REQUEST TIME so the answer
            // cannot change under us if the function updates meanwhile.
            take_data_q <= fn_valid;
            if (fn_valid) rsp_data <= fn_data;
          end
        end

        S_ANSWER: begin
          // An answer is always produced -- never silence. Which answer was
          // decided in S_IDLE.
          state_q      <= S_IDLE;
          rsp_valid    <= 1'b1;
          if (take_data_q) begin
            rsp_has_data <= 1'b1;
            fn_take      <= 1'b1;
          end else begin
            rsp_nothing  <= 1'b1;
          end
        end

        default: state_q <= S_IDLE;
      endcase
    end
  end

endmodule

What it models. One invariant and one obligation: emit only in answer to a request, and always answer.

Hardware implied. A single state bit, one decision flop, a payload register and four pulse outputs. Deliberately tiny — the point is which signals are absent from which conditions, not the gate count.

Why the structure is as it is. fn_valid is read only inside the req_valid && req_for_me branch, so there is no path by which function readiness alone can produce bus activity. Readiness is sampled at request time into take_data_q rather than being re-evaluated when the answer is driven, so an answer promising data cannot arrive after the function has withdrawn it. And S_ANSWER has no conditional exit: once a request is accepted, an answer is owed and will be produced.

Assumptions. That req_valid/req_for_me are already in this clock domain and already decoded; that exactly one answer per request is the contract; and that the function tolerates a one-cycle fn_take pulse.

What it deliberately omits. Everything that makes USB USB. No packets, tokens, handshakes, endpoints, addresses, error detection, retries, speeds or device states. It is an architecture diagram that happens to elaborate.

5. The Invariant, as Assertions

The value of writing §4 as RTL is that the architecture becomes checkable. These are the checks.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Assertions for responder_discipline.
//
// Classification: ARCHITECTURAL OWNERSHIP ASSERTIONS. They protect the
// host-owns-initiation model in THIS abstraction. They are not USB
// compliance checks and assert nothing about any USB mechanism.
// ─────────────────────────────────────────────────────────────────────────

// D1 -- THE OWNERSHIP INVARIANT, and the reason this module exists. An
// answer may only appear where a request addressed to us was accepted in
// the previous cycle. A device that can drive the bus without this is not
// a USB device, whatever else it implements correctly.
property p_no_answer_without_request;
  @(posedge clk) disable iff (!rst_n)
    rsp_valid |-> $past((state_q == S_IDLE) && req_valid && req_for_me, 2);
endproperty
assert property (p_no_answer_without_request);

// D2 -- FUNCTION READINESS ALONE MUST CAUSE NOTHING. This is D1 stated from
// the direction a designer is most likely to break it: by wiring the
// function's ready signal toward the bus as an "optimisation".
property p_readiness_is_not_permission;
  @(posedge clk) disable iff (!rst_n)
    (state_q == S_IDLE && !(req_valid && req_for_me)) |=> !rsp_valid;
endproperty
assert property (p_readiness_is_not_permission);

// D3 -- SILENCE IS NOT AN ANSWER. Every accepted request produces exactly
// one response in the next cycle. A device that answers only when it has
// data goes quiet whenever the function is empty -- which for an input
// device is nearly always, and which a real host reads as a fault.
property p_every_request_is_answered;
  @(posedge clk) disable iff (!rst_n)
    (state_q == S_IDLE && req_valid && req_for_me) |-> ##2 rsp_valid;
endproperty
assert property (p_every_request_is_answered);

// D4 -- the two answers are mutually exclusive and one is always chosen.
property p_answer_is_exactly_one_kind;
  @(posedge clk) disable iff (!rst_n)
    rsp_valid |-> (rsp_has_data ^ rsp_nothing);
endproperty
assert property (p_answer_is_exactly_one_kind);

// D5 -- the function's data is consumed only when it was actually sent.
// Taking data and then answering "nothing" loses a payload silently.
property p_take_only_when_sent;
  @(posedge clk) disable iff (!rst_n)
    fn_take |-> (rsp_valid && rsp_has_data);
endproperty
assert property (p_take_only_when_sent);

// D6 -- reset leaves the responder silent. A device that drives the bus out
// of reset can corrupt an exchange belonging to a different device, since
// the bus is shared with everything below the same hub.
property p_reset_is_silent;
  @(posedge clk)
    !rst_n |=> !rsp_valid;
endproperty
assert property (p_reset_is_silent);

Note the two-cycle latency those properties encode: a request is accepted in S_IDLE, the responder enters S_ANSWER, and the answer pulses the cycle after that. Assertions about a request/response contract must be written against the design's actual latency — a property written |=> here would fail on correct hardware, which is the commonest way a good assertion gets deleted for being “wrong”.

D1 and D2 are the same rule from two directions, and that redundancy is intentional. D1 catches an answer with no cause; D2 catches the specific design move that produces one — routing function readiness toward the bus because it seems efficient. Verification engineers write both because the second is the one that fails first when someone “optimises”.

D3 is the one teams forget. It is easy to verify that a device sends correct data and never notice that it sends nothing when it has nothing, because a testbench that only asks when data is ready — the failure Chapter 2.1 §9 describes — never creates the empty case.

6. What a Device Must Remember

A responder is stateful, and its state comes in two kinds that RTL engineers must keep apart.

Protocol-visible state is state the host can observe or change through the bus: broadly, whether the device has been addressed yet, whether it has been configured, and per-data-path condition such as whether a path is currently able to accept or supply data. The host acts on this state, so the device and host must agree about it — and disagreement is a real failure class, not a theoretical one.

Implementation state is everything else: buffer occupancy, internal counters, the function's own condition. The host cannot see it and must not need to.

Module 8 owns device states as a formal model and Module 9 owns the per-data-path abstraction, so this chapter names the categories rather than enumerating them. What belongs here is the design consequence: the boundary between these two kinds of state is where a device controller's reset behaviour gets decided, and getting it wrong is subtle. A bus-originated reset must return the protocol-visible state to its defined starting condition — but need not, and often should not, discard everything the function was doing. An implementation that treats every reset as a full wipe loses work unnecessarily; one that treats it as cosmetic leaves the host and device disagreeing about what the device is.

Note carefully that a bus reset and an internal RTL reset are not the same event. One is a protocol-level instruction arriving over the interface; the other is a hardware condition asserted by the local system. A controller must handle both, and they need not have identical effects. Conflating them in RTL is a recognisable bring-up bug.

7. Verification: a Device's Correctness Is Conditional

Chapter 2.1 §6 established that stimulus originates on the host side. From the device's perspective that has a sharp consequence: there is no such thing as checking a device in isolation. Every statement about correct behaviour has the form given this host-originated request, in this state, the device must answer thus.

Stimulus dimensions that matter for a responder abstraction like §4's: requests arriving while the function is empty and while it is full; requests not addressed to this responder, which must produce nothing at all; back-to-back requests with no idle gap; long idle periods with the function full, which is where D2 breaks if it is going to; and reset asserted while an answer is owed.

Representative coverage dimensions — not a verification plan:

  • answer kind: data and nothing, each reached at least once
  • request addressed to us versus addressed elsewhere
  • function empty at request time, function becoming ready after request time
  • reset in S_IDLE and in S_ANSWER
  • request density: back-to-back, sparse, and none for a long interval

The error injections that teach something: a request removed mid-exchange; a function that withdraws fn_valid between the request and the answer, which is exactly what take_data_q exists to survive; and a second request arriving while an answer is owed.

The broader point is the one Module 24 will build on: because the device is conditional, a device-side verification environment is mostly a host model, and the quality of that model bounds everything the environment can find.

8. A Failure Traced

A device-side controller passes its unit tests. On real hardware it works intermittently, and an analyser shows the device transmitting at moments the host did not request.

The wrong mental model. “My device has data, so it should send it.” This is the reflex of anyone who has designed a streaming or master-capable interface before, and it is exactly backwards here.

The implementation mistake. A path by which fn_valid — or something derived from it, such as a FIFO's not-empty flag — reaches the bus-driving logic without a request in the condition.

The observable failure. On a bus shared with other devices below the same hub, an unrequested transmission collides with traffic that belongs to somebody else. The symptom is therefore often not on the offending device: another device's transfer fails, or the host reports errors attributed to the wrong participant. That misattribution is what makes the bug expensive.

The debug evidence. A bus trace showing device activity with no preceding host-originated request addressed to it. This is exactly assertion D1, which is why D1 is worth having in simulation long before silicon.

The correct model. Readiness is not permission. The function's state determines what the answer contains, never whether an answer occurs.

9. Common Misconceptions

10. Reason It Through

A reviewer reads a device-side RTL block and finds this in the bus-response path: if (req_valid && req_for_me && fn_valid) drive_response(); — the function's readiness has been added to the condition, to avoid answering when there is nothing to send.

Does this violate the ownership rule of §2? No, and that is what makes it dangerous. A request is still required, so nothing is transmitted unrequested. Assertion D1 passes. D2 passes.

What does it break? §3. The device now answers only when it has data and is silent otherwise, so an empty poll produces nothing — which the host cannot distinguish from a device that has failed, been removed, or never heard the request.

What does the failure look like? It depends on the host's error policy and therefore varies between systems: retries, escalating recovery, a port reset, a device that disappears and returns. It is worst for input devices, which are empty most of the time, so the device is silent in its normal condition and appears healthy only while data happens to be flowing. It will also behave differently on different operating systems, which sends teams hunting in the wrong place.

Which assertion catches it? D3 — every accepted request produces an answer. Note that D1 and D2 both pass, so a team that wrote only the ownership assertions would ship this.

What is the general lesson? An architecture imposes obligations in both directions. The host owns initiation, and in exchange the device owes an answer to everything it is asked. Implementing only the constraint you find intuitive — do not speak out of turn — while dropping the one you do not is how a design satisfies half a contract and fails in the field.

11. Understanding Check

12. Summary

A USB device is a function wrapped in a protocol engine that answers when asked, and the two halves are architecturally separable. The device function is the product; the device controller is the USB-facing engine that holds protocol-visible state and answers the host. The split is a reuse boundary, which is why entangling them is expensive.

The behavioural rule is the responder discipline: bus-facing outputs are a function of host-originated requests, never of the function's readiness. Expressed for RTL, no path may carry the function's ready signal to the bus-driving logic without a request in the condition. USB 3.x lets a device notify the host that it has become ready, which is asking to be asked rather than sending, so the rule survives the generation.

The obligation runs the other way too. Silence is not an answer. A device must respond to every request addressed to it, including with an explicit nothing right now, because a quiet device is indistinguishable from a failed or absent one. That forces a response path which works with no help from the function — the requirement a design implementing only the intuitive half of the contract will drop.

A device's state divides into protocol-visible and implementation state, and the boundary between them is where reset behaviour gets decided — remembering that a bus reset and an RTL reset are different events whose effects need not be identical.

For verification, a device's correctness is conditional, so a device-side environment is mostly a host model, and that model's willingness to be inconvenient bounds what the environment can discover.

13. What Comes Next

Two participants so far, and one link between them. Real systems have many devices and one host, which raises a question the architecture has to answer without surrendering anything established in these two chapters: how do you attach more devices without giving any of them authority?

Chapter 2.3 introduces the hub, and the interesting part is not that it multiplies ports. It is that a hub is itself a device — enumerated, addressed and configured like any other — while simultaneously being the thing that lets other devices attach. That chapter also disposes of the most damaging misreading in Module 2: that a branching tree implies devices can talk to one another.

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.