Skip to content
VLSI Mentor

USB · Module 6

Configuration Selection

Addressed is not configured. What selecting a configuration switches on, why configuration zero is an un-select rather than a choice, and the RTL consequence of a state entered and left by the same request.

After Chapter 6.4 the host knows everything about the device: what it is, what it offers, how much power it wants, which endpoints it has.

The device still does not work.

That gap — between fully described and usable — is this chapter, and it is the step engineers most often skim past, because the host reads the descriptors and then it works feels like a complete story. It is not. Something explicit happens in between, and a device that has not had it done to it will sit at its assigned address answering control requests and nothing else, indefinitely.

1. Why Describing Is Not Enabling

Start from what a description is.

Chapter 6.4 established that a device may offer more than one configuration. That plural is the whole point of this chapter. A description is a statement of alternatives — here is what I could be — and alternatives are not a state. Something must choose.

Why would a device offer more than one? Because a single device can reasonably have more than one mode of operation, and those modes may have genuinely different requirements. A device might offer a full-featured mode that needs substantial power and a reduced mode that needs much less. The host, which knows what the port can supply, is the one able to decide.

And that reveals what the choice actually is. It is not cosmetic and it is not a hint. Selecting a configuration is the host saying: of the things you told me you could be, be this one. Until it does, the device has no basis for being any of them.

2. What Selection Actually Does

Three things happen when a configuration is selected, and separating them is worth the effort because they fail differently.

It resolves the alternatives. The device stops being a menu and becomes one specific thing. This is the logical change, and it is what the state name refers to.

It brings up the endpoints. The endpoints described by the selected configuration begin operating. Before selection there were no operating endpoints beyond the control endpoint, because there was no fact about which set was the right one. Module 9 owns what an endpoint is and how it behaves; what matters here is that the set of live endpoints is a consequence of the selection, not an independent property.

It commits the device to a power budget. The selected configuration's power requirement is now the device's requirement, and the host — which agreed to it by making the selection — is now responsible for supplying it.

These fail in different ways, which is why separating them matters. A device that resolves the alternatives but does not bring up its endpoints enumerates perfectly and then transfers nothing. A device that brings up endpoints belonging to a configuration that was not selected appears to work until the host uses an endpoint it never asked for. A device that ignores the power budget works on a generous port and fails on a constrained one.

3. The Commit, and How It Differs From Addressing

Chapter 6.3 established a commit boundary: a request is received, captured, and applied when its transfer completes. Configuration selection has the same structure — and one property that addressing does not have at all.

The structural similarity. Selecting a configuration is a request carried by a transfer, and the selection must take effect at a defined point relative to that transfer's completion. The same capture then commit architecture applies, for the same reason.

The difference, and it is a large one. The device can be un-configured by the same mechanism that configured it.

Selecting configuration zero is not selecting a configuration called zero. Zero is reserved: it means no configuration, and selecting it returns the device from Configured to Address. Its endpoints stop operating. Its power commitment is released. It is reachable and unusable again — exactly where Chapter 6.4 found it.

Nothing in addressing works like this. There is no request that un-addresses a device; only a reset returns it to the default address, and a reset is a much blunter instrument that clears everything. Configuration is the first piece of protocol state in this module that has a deliberate, request-driven exit as well as an entry.

A state machine with two states, Address and Configured. A SetConfiguration request carrying a non-zero value moves the device from Address to Configured. A SetConfiguration request carrying zero moves the device from Configured back to Address. A SetConfiguration request carrying a non-zero value while already Configured re-selects and remains in Configured. A bus reset from either state returns the device to Default, which is drawn as a third state.DefaultAddressConfiguredaddress committed (6.3)address committed (6.3)addresscommitted…SetConfiguration, non-zeroSetConfiguration,non-zeroSetConfiguration 0 — un-selectSetConfiguration0 — un-selectnon-zero — re-selectnon-zero — re-selectbus resetbus resetbus resetbus reset
Figure 1 — the same request drives both transitions. Which one occurs is decided by the value it carries, not by which state the device is in.

Configuration selection — commit, re-select, and un-select

8 cycles
A device-controller register view of configuration selection. A configuration request carrying a non-zero value is captured as pending and committed when the transfer completes, at which point the active configuration becomes that value, the device state becomes Configured, and the endpoints-live indication goes high. A later request carrying a different non-zero value re-selects, changing the active configuration while the device remains Configured. A final request carrying zero commits zero, returning the device to the Address state and taking the endpoints-live indication low. The figure shows controller registers rather than bus signalling and depicts no real durations.commit non-zero — endpoints upcommit non-zero — endpointsupre-select — still Configuredre-select — stillConfiguredcommit zero — endpoints down, back to Addresscommit zero — endpointsdown, back to Addresscfg_reqxfer_completepending_cfg--1112200active_cfg00111220dev_stateADDRADDRCFGDCFGDCFGDCFGDCFGDADDRendpoints_livet0t1t2t3t4t5t6t7
Figure 2 — one request, three outcomes. The value decides; the current state does not.

4. The RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// usb_config_commit
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It models the
// configuration commit boundary and the bidirectional transition it drives,
// and no other USB mechanism.
//
// WHAT IT MODELS. Section 3: a requested configuration value is captured
// when the request is decoded and applied when the transfer completes; the
// VALUE decides the resulting state, so the same request both configures
// and un-configures; and a bus reset returns the device to Default with no
// configuration, per Chapter 6.2.
//
// WHAT IT DOES NOT MODEL. Endpoints (Module 9 -- endpoints_live is a single
// abstract indication here), power negotiation or budgeting, control
// transfer mechanics (Module 13 -- the completion is an already-qualified
// event), packets (Modules 11-12), the descriptor tree (Module 7), or
// addressing (Chapter 6.3 owns it; the address commit arrives as an event).
//
// NOTE ON SCOPE. This deliberately overlaps Chapter 6.2's usb_reset_scope
// on the Address/Configured transitions. 6.2 modelled reset SCOPE across
// the whole state set; this models the configuration commit in detail,
// including the zero case 6.2 handled in one line. Read them as two views
// of one machine, not as two machines.
// ─────────────────────────────────────────────────────────────────────────
package usb_cfg_pkg;
  typedef enum logic [1:0] {
    CS_DEFAULT    = 2'b00,   // no address (Chapter 6.2)
    CS_ADDRESS    = 2'b01,   // reachable, not usable
    CS_CONFIGURED = 2'b10    // endpoints live
  } cfg_state_e;

  // Reserved: "no configuration". NOT a selectable configuration number.
  localparam logic [7:0] CFG_NONE = 8'd0;
endpackage

module usb_config_commit
  import usb_cfg_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,

  input  logic       bus_reset,       // decoded, LEVEL while asserted

  input  logic       addr_committed,  // from Chapter 6.3, 1-cycle pulse

  // A decoded SetConfiguration request. 1-cycle pulse; cfg_req_value
  // carries the requested value, which MAY be CFG_NONE.
  input  logic       cfg_req,
  input  logic [7:0] cfg_req_value,

  // The transfer carrying the request completed / failed. Qualified
  // upstream, mutually exclusive, 1-cycle pulses.
  input  logic       xfer_complete,
  input  logic       xfer_failed,

  output cfg_state_e dev_state,
  output logic [7:0] active_config,
  output logic       endpoints_live,
  output logic       cfg_committed    // 1-cycle pulse: a commit happened
);

  logic [7:0] pending_cfg;
  logic       pending_valid;

  // Endpoints are live exactly when a configuration is selected. Written as
  // a derivation rather than as a separately-maintained register, because
  // two registers that must always agree are two registers that eventually
  // will not. Section 6 shows what the separate-register version costs.
  assign endpoints_live = (dev_state == CS_CONFIGURED);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      dev_state     <= CS_DEFAULT;
      active_config <= CFG_NONE;
      pending_cfg   <= CFG_NONE;
      pending_valid <= 1'b0;
      cfg_committed <= 1'b0;
    end else begin
      cfg_committed <= 1'b0;                 // single-cycle pulse

      if (bus_reset) begin
        dev_state     <= CS_DEFAULT;
        active_config <= CFG_NONE;
        pending_cfg   <= CFG_NONE;
        pending_valid <= 1'b0;
      end else begin
        if (addr_committed) dev_state <= CS_ADDRESS;

        // ── Capture, do not apply (the Chapter 6.3 architecture) ────────
        if (cfg_req) begin
          pending_cfg   <= cfg_req_value;
          pending_valid <= 1'b1;
        end

        // ── Commit ──────────────────────────────────────────────────────
        if (xfer_complete && pending_valid) begin
          active_config <= pending_cfg;
          pending_valid <= 1'b0;
          cfg_committed <= 1'b1;

          // THE POINT OF THIS MODULE. The resulting state is decided by the
          // VALUE, not by where the device currently is. Committing
          // CFG_NONE is an un-select and must return the device to Address.
          // Writing this as "go to CS_CONFIGURED" is correct for every
          // non-zero value and wrong for zero -- section 6 measures it.
          //
          // if/else rather than a ternary: a conditional between two enum
          // literals is not directly assignable to an enum without a cast.
          if (pending_cfg == CFG_NONE) dev_state <= CS_ADDRESS;
          else                         dev_state <= CS_CONFIGURED;
        end else if (xfer_failed) begin
          // Discard. The device keeps the configuration it had, so a failed
          // selection leaves it in a defined state and the host can retry.
          pending_valid <= 1'b0;
        end
      end
    end
  end

endmodule

What it models. The commit boundary of §3 and the bidirectional transition the committed value drives.

Why this hardware exists. Because the protocol makes one request mean two opposite things depending on its value, and because — as in Chapter 6.3 — the request and its effect are separated by the transfer that carries them.

Inputs. Clock and local reset; a decoded bus reset; the address-commit event from 6.3; a decoded request with its value; and qualified completion and failure events.

State retained. The device state, the active configuration, the pending configuration, and a validity flag.

Outputs. The state, the active configuration, an abstract endpoints-live indication, and a commit pulse.

Reset behaviour. Local reset clears everything. A bus reset returns to Default with no configuration and discards anything pending, for the reason Chapter 6.2 §3 gives.

Hardware implied. A three-state register, two 8-bit registers, a flag, and a comparator against the reserved value.

Assumptions. That cfg_req, xfer_complete, xfer_failed and addr_committed are decoded, qualified single-cycle pulses in this clock domain; that completion and failure are mutually exclusive per transfer; and that the requested value has already been checked against what the device actually offers — validating the value is not modelled here, and a real controller must reject a configuration it never advertised.

Deliberately omits. Endpoints, power, control-transfer mechanics, packets, descriptors, and value validation.

What DV should verify. That a request alone does not change the configuration; that a commit uses the captured value; that committing the reserved value returns the device to Address and takes the endpoints down; that committing a non-zero value from Configured re-selects without leaving Configured; that a failure discards; and that the endpoints-live indication never disagrees with the state.

5. The Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Assertions for usb_config_commit.
//
// Classification: TEACHING ASSERTIONS about this model's commit and
// transition semantics. Not a USB compliance suite.
// ─────────────────────────────────────────────────────────────────────────

// C1 -- the Chapter 6.3 rule again: receiving the request is not applying it.
property p_no_commit_on_request;
  @(posedge clk) disable iff (!rst_n)
    (cfg_req && !xfer_complete && !bus_reset) |=> $stable(active_config);
endproperty
assert property (p_no_commit_on_request);

// C2 -- THE CENTRAL ONE. The state after a commit is decided by the VALUE.
// Both directions are asserted, deliberately: a property that only checks
// the non-zero case is satisfied by the design section 6 mutates.
property p_value_decides_state;
  @(posedge clk) disable iff (!rst_n)
    cfg_committed |-> ((active_config == CFG_NONE) ? (dev_state == CS_ADDRESS)
                                                   : (dev_state == CS_CONFIGURED));
endproperty
assert property (p_value_decides_state);

// C3 -- the abstract endpoints indication must never disagree with the
// state. Trivially true for a derived signal, and the point of asserting it
// is that it stops being trivial the moment someone registers it instead.
property p_endpoints_track_state;
  @(posedge clk) disable iff (!rst_n)
    endpoints_live == (dev_state == CS_CONFIGURED);
endproperty
assert property (p_endpoints_track_state);

// C4 -- a commit uses the captured value, not the request bus at commit
// time. Chapter 6.3's A3, for the same reason and with the same caveat:
// it is only falsifiable if the stimulus disturbs the bus.
property p_commit_uses_pending;
  @(posedge clk) disable iff (!rst_n)
    cfg_committed |-> (active_config == $past(pending_cfg));
endproperty
assert property (p_commit_uses_pending);

// C5 -- a failed selection must not commit; the device keeps what it had.
property p_failure_does_not_commit;
  @(posedge clk) disable iff (!rst_n)
    (xfer_failed && !xfer_complete) |=> !cfg_committed;
endproperty
assert property (p_failure_does_not_commit);

// C6 -- a bus reset returns to Default with no configuration.
property p_reset_unconfigures;
  @(posedge clk) disable iff (!rst_n)
    bus_reset |=> (dev_state == CS_DEFAULT && active_config == CFG_NONE
                   && !endpoints_live);
endproperty
assert property (p_reset_unconfigures);

// C7 -- the configuration changes only at a commit or a reset.
property p_change_only_on_commit_or_reset;
  @(posedge clk) disable iff (!rst_n)
    !$stable(active_config) |-> ($past(cfg_committed) || $past(bus_reset)
                                 || cfg_committed);
endproperty
assert property (p_change_only_on_commit_or_reset);

C2 is the one this chapter exists to produce, and the note in its comment is the substance. A property written as a commit means the device is Configured is true for every value a normal test selects and false only for zero. Writing both directions is what makes it a real check rather than a restatement of the common case.

C3 looks like a tautology and is deliberately kept. Against the design in §4 it cannot fail, because the signal is derived. It exists for the design that registers the indication separately — which is what §6 mutates, and which is a genuinely tempting thing to write when the endpoints in question are real hardware with their own enables.

6. Mutation Test

Three mutants. All were run against three stimulus variants — one that selects the reserved value, one that never does, and a functional check that asks only whether the device ends up correctly configured. The results below are measured.

P1 — treat the request as enter the Configured state

The defect §3's callout predicts, and the most natural way to write this wrong.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (xfer_complete && pending_valid) begin
  active_config <= pending_cfg;
  cfg_committed <= 1'b1;
  dev_state     <= CS_CONFIGURED;   // MUTANT P1: the value is ignored
end

Result. Against a stimulus that selects only non-zero configurations, this mutant passes every check with zero errors — it is indistinguishable from the correct design. Add a single selection of the reserved value and C2 fires, alone: no other property detects it.

That C2 fires alone is worth pausing on. C3 stays silent, because endpoints_live is derived from the state and the state is consistently wrong — the endpoints agree perfectly with a state that should not exist. A defect can be entirely self-consistent and still be a defect, which is why a property that compares two of the design's own signals is weaker than one that compares a signal against the protocol rule.

That combination is worth looking at directly, because it is what the hardware would actually do: a device reporting itself configured, with endpoints operating, while its configuration value says it has none. The host believes it un-configured it. The device believes otherwise. Nothing errors.

P2 — register the endpoints indication instead of deriving it

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// endpoints_live becomes a register, set at commit:
if (xfer_complete && pending_valid) begin
  ...
  endpoints_live <= 1'b1;   // MUTANT P2: set on commit, never cleared here
end

Result. C3 fires on the first un-select and keeps firing for as long as the device stays in Address — the state returned correctly while the indication stayed high. C2 passes throughout, because the state machine itself is perfectly correct; only the derived-turned-registered signal is wrong.

And this mutant, too, passes with zero errors against the stimulus that never selects the reserved value. Both P1 and P2 are invisible without that one stimulus, by measurement — from opposite sides of the same transition.

This is why C3 was kept despite looking tautological. Against §4's design it cannot fail. Against the design a real implementation is likely to grow — where the indication becomes a genuine set of endpoint enables with their own registers — it is the only property that catches a device whose endpoints keep operating after the host has withdrawn their authorisation.

P3 — commit on receipt

Chapter 6.3 §7's M1, repeated here to confirm the same rule applies to configuration.

Result. C1 fires on the first request, with C4 alongside it — the value committed at the request cannot equal a pending value not yet captured. As in 6.3, a functional check asking only whether the device ends up correctly configured passes.

Unlike P1 and P2, this one is caught by any stimulus, zero-selecting or not: it is wrong on the common path, which is exactly why it is the mutant least likely to reach silicon.

7. Verification

This chapter's commit point is device configured, the fifth in the module's chain — and the first that can be undone without a reset.

Stimulus. Selection of a valid non-zero configuration; selection of the reserved value from Configured; re-selection of a different non-zero value while already Configured; re-selection of the same value; a failed selection; a bus reset from Configured; and a selection attempted from Address before any address was committed.

The stimulus requirements §6 proved are not optional:

  • Select the reserved value at least once. Without it, P1 is invisible and passes with zero errors. This single stimulus is worth more than exhaustively selecting every valid configuration.
  • Check the endpoints indication after the un-select, not only after the select. P2 is wrong only on the downward transition.
  • Disturb the request bus between capture and commit, as Chapter 6.3 §8 requires, or C4 is unfalsifiable.

Observation. The device state, the active configuration, and the endpoints indication together. Each is checkable and each can be right while the combination is wrong — P1 produces a correct configuration value with an incorrect state, and P2 the reverse.

Reference model. State, active configuration, pending configuration and validity. Chapter 6.6 combines it with 6.3's and 6.4's into one enumeration model.

Representative coverage — crosses:

  • requested value zero × non-zero × a value the device never advertised
  • current state Address × Configured, crossed with both request values
  • commit × failure × bus reset between request and commit
  • re-selection of the same value × a different value
  • endpoints indication checked on both the upward and the downward transition

Negative cases with defined outcomes: a failed selection must leave the previous configuration intact; a selection of a value the device never advertised must be rejected rather than committed — not modelled in §4, and therefore a case the DV plan must carry explicitly rather than assume.

8. Common Misconceptions

9. Reason It Through

A composite device works correctly. The host later un-configures it, intending to put it into a low-power state, and then reads its status. The device reports itself in the Address state — correctly — but data continues to arrive on an endpoint that belongs to the configuration that was just cancelled.

What is inconsistent here? The state and the endpoint behaviour disagree. The state machine performed the un-select correctly; the endpoints did not follow.

Which mutant is this? §6's P2. The state is derived correctly, the configuration value is correct, and the endpoint enable is separately registered — set at commit and never cleared on the downward transition.

Why would this survive a test suite? Because everything checked at configure time is right. The defect exists only on the un-configure transition, and a suite that configures devices and verifies they work has no reason to generate it.

What is the actual risk? An endpoint operating without authorisation. The host has released the device's power commitment and stopped expecting traffic from it; the device is still producing it. Depending on what that endpoint does, the consequences range from wasted power to data arriving in a buffer nobody owns any more.

What property would have caught it, and how expensive is it? C3 — one line asserting that the endpoints indication equals the state comparison. It is trivially true of the correct design, which is precisely why it is easy to dismiss as not worth writing. It costs one line and it is the only thing between this defect and a customer.

And the transferable lesson? When a design derives a signal, assert the derivation anyway. The assertion is free while the derivation holds, and it is the only warning you get when someone later replaces the derivation with a register — which they will, the first time that signal needs to drive real hardware with its own timing.

10. Understanding Check

11. Summary

A described device is not a usable one. Descriptors state alternatives — a device may offer more than one configuration — and alternatives are not a state, so something must choose. Addressed means reachable; configured means usable.

Selection does three separable things: it resolves the alternatives, it brings up the endpoints belonging to the chosen configuration, and it commits the device to a power budget the host has agreed to supply. They are worth separating because they fail differently.

The commit has Chapter 6.3's structure — capture, complete, apply — plus one property addressing does not have: a request-driven exit. Configuration zero is reserved and means no configuration, so the same request that configures a device also un-configures it. The state that results is decided by the value, not by the request.

That makes the transition logic bidirectional, and §6 measured what it costs to miss: a design that treats the request as enter the Configured state passes every check with zero errors until something selects the reserved value — then it reports itself configured, endpoints live, over a configuration value of zero, with the host believing the opposite and nothing erroring.

A second mutant made the case for deriving the endpoints indication rather than registering it: registered, it is set on the upward transition and forgotten on the downward one, leaving endpoints operating after the host withdrew their authorisation. The one-line property that catches it is trivially true of the correct design, which is exactly why it is worth keeping.

And the pattern across every surviving mutant in this module: each is correct on the path a test naturally walks. The value of a stimulus is what the common path does not already reach.

12. What Comes Next

Five commit points are now established: attached, reset, addressed, described, configured. Each was studied on its own, with its own RTL, its own properties, and its own characteristic way of going wrong.

Chapter 6.6 puts them together — the whole sequence from attach to a usable device, as one flow and one state machine. That is worth doing for a reason beyond review: the failures that appear only at the seams have not been visible yet. Every chapter so far has verified one step against its own rules. The interesting bugs in enumeration live between steps, in the ordering and the shared state — and Chapter 6.3 §7 has already shown one, where a defect in the address register did its damage inside the state machine of Chapter 6.2.

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.