USB · Module 6
Address Assignment
Receiving an address and beginning to answer to it are not the same event. Why the default address exists, where the commit boundary falls, the pending-versus-active register architecture that follows, and three mutation tests — one of which forced a sixth assertion.
Chapter 6.2 left the device in Default: reachable, unconfigured, and with no address.
That last property is a problem, and it is the same circularity reset solved, reappearing one layer up. A bus is shared — every device below a hub sees traffic and decides whether it is the intended recipient by comparing an address. A freshly reset device has no address to compare against. But the host cannot give it one without first being able to talk to it.
This chapter resolves that, and in doing so reaches the most interesting timing boundary in enumeration: receiving an address and beginning to answer to it are different events, separated by a gap that a great deal of device-controller RTL gets wrong.
1. Why a Default Address Must Exist
Derive the answer rather than memorising it.
The host needs to address a device that has no address. The options are few.
Broadcast to everything. Unworkable: several devices may be newly attached, and all would answer at once.
Let the device choose its own address. Unworkable: two devices could choose the same one, and there is no arbitration mechanism — Chapter 2.6 established that USB deliberately has none.
Reserve an address that means the device being enumerated. This works, provided exactly one device is ever in that condition at a time.
USB takes the third. Address zero is the default address, and a device in Default answers to it. The constraint that makes it sound is that the host enumerates one device at a time on a bus: it enables one port, resets it, addresses that device, and only then moves on. A second device sitting at the default address simultaneously would be ambiguous, so the host's sequencing prevents it.
That is an elegant trade. One reserved value out of the address space buys a way to talk to the unaddressed, at the cost of serialising a step that would otherwise be parallel. The address field is seven bits, giving 128 values — Chapter 2.8 §3 derived that limit — and reserving one leaves 127 assignable addresses.
2. The Commit Boundary
Now the chapter's core, and the reason address assignment deserves more attention than the host sends a request and the device stores a number.
Consider what has to happen. The host issues a request telling the device its new address. The device must acknowledge that request. But the acknowledgement is itself traffic, and traffic is addressed.
So: does the device answer that acknowledgement at the old address or the new one?
Both answers break something if chosen naively.
If the device switches immediately on receiving the request, it will answer at the new address — but the host is still conducting a transfer it began at the default address, and does not yet know the device has switched. The completion arrives from an address the host was not expecting.
If the device never switches, the address assignment is meaningless.
The resolution is a defined commit point. The device receives the request, remembers the address, completes the current transfer at the old address, and only then begins using the new one. The switch happens at the end of the exchange, not at its beginning — and both ends know exactly where that boundary falls, because it is the completion of the transfer itself.
Stated as a rule for the RTL engineer:
The address takes effect after the SetAddress transfer completes, not when the request is received. Until completion, the device is still reachable at the address it had.
And there is a recovery interval afterwards. The host does not immediately begin using the new address; it allows the device a defined settling period before the next request. Chapter 6.7 treats that duration; what matters here is that it exists, and why — the device has internal work to do, and the host allowing for it is cheaper than requiring every device to switch instantaneously.
3. The Boundary as a Waveform
Pending and active address — device-controller register view
7 cycles4. The Address Register, as RTL
This is the module's headline example, because it is a case where protocol timing creates hardware state.
// ─────────────────────────────────────────────────────────────────────────
// usb_address_reg
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It models the
// pending/active address architecture and its commit boundary, and no other
// USB mechanism.
//
// WHAT IT MODELS. Section 2's rule: a requested address is captured when
// the request is decoded, and becomes ACTIVE only when the transfer that
// carried it completes. Until then the device remains reachable at the
// address it already had. It also models bus reset returning the address
// to the default, per Chapter 6.2.
//
// WHAT IT DOES NOT MODEL. Control-transfer mechanics (Module 13 -- the
// completion here is an already-qualified event), packets or tokens
// (Modules 11-12), endpoints (Module 9), the wider device-state machine
// (Module 8), or the recovery interval after commit, which is a HOST-side
// timing obligation (Chapter 6.7) rather than device logic.
// ─────────────────────────────────────────────────────────────────────────
module usb_address_reg
import usb_enum_pkg::*;
(
input logic clk,
input logic rst_n, // LOCAL hardware reset
// Decoded protocol bus reset -- a LEVEL while asserted (Chapter 6.2).
input logic bus_reset,
// A decoded SetAddress request. 1-cycle pulse; addr_req carries the value.
input logic setaddr_req,
input logic [6:0] addr_req,
// The transfer that carried the request has COMPLETED successfully.
// 1-cycle pulse, qualified upstream. This is the commit boundary.
input logic xfer_complete,
// The transfer was aborted or failed. The pending value must be discarded
// without ever becoming active -- a failed assignment leaves the device
// where it was, which is what lets the host retry.
input logic xfer_failed,
output logic [6:0] active_addr, // what the device answers to, now
output logic addr_committed // 1-cycle pulse: a commit happened
);
logic [6:0] pending_addr;
logic pending_valid;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
active_addr <= USB_DEFAULT_ADDR;
pending_addr <= USB_DEFAULT_ADDR;
pending_valid <= 1'b0;
addr_committed <= 1'b0;
end else begin
addr_committed <= 1'b0; // single-cycle pulse
if (bus_reset) begin
// Chapter 6.2's scope rule. Note this clears the PENDING value too:
// a request in flight when a reset arrives must not survive it and
// commit afterwards, which would assign an address the host has
// already forgotten about.
active_addr <= USB_DEFAULT_ADDR;
pending_addr <= USB_DEFAULT_ADDR;
pending_valid <= 1'b0;
end else begin
// ── Capture, but do NOT apply ──────────────────────────────────
if (setaddr_req) begin
pending_addr <= addr_req;
pending_valid <= 1'b1;
end
// ── Commit, at the defined boundary ────────────────────────────
// Only a completion commits, and only if something is pending.
// A completion with nothing pending belongs to some other transfer
// and must leave the address alone.
if (xfer_complete && pending_valid) begin
active_addr <= pending_addr;
// Clearing this is NOT bookkeeping. Leaving it set lets the next
// unrelated completion commit again, producing a spurious
// addr_committed pulse that costs the device its configuration
// downstream. Sections 6 and 7 measure it; A6 is what catches it.
pending_valid <= 1'b0;
addr_committed <= 1'b1;
end else if (xfer_failed) begin
// Discard without committing. The device stays reachable where it
// was, so the host can retry the assignment.
pending_valid <= 1'b0;
end
end
end
end
endmoduleWhat it models. The capture/commit split and its three exits: commit on completion, discard on failure, discard on bus reset.
Why this hardware exists. Because the protocol places the address change at the end of an exchange that is itself addressed. One register cannot represent both "the address I will use" and "the address I am using" when those differ for the duration of a transfer — so there must be two.
Inputs. A local hardware reset; a decoded bus reset; a decoded request with its value; and qualified completion and failure events.
State retained. The active address, the pending address, and a validity flag saying whether a pending value is meaningful.
Outputs. The active address, and a commit pulse for the state machine of Chapter 6.2 to consume.
Reset behaviour. The local reset clears everything. A bus reset clears both active and pending — the comment explains why: a request in flight when a reset arrives must not survive and commit afterwards.
Hardware implied. Two 7-bit registers, a validity flag, and a small amount of control.
Assumptions. That setaddr_req, xfer_complete and xfer_failed are decoded, qualified, single-cycle pulses in this clock domain, and that completion and failure are mutually exclusive for a given transfer.
Deliberately omits. All control-transfer mechanics, packets, endpoints, the wider state machine, and the host-side recovery interval — which is the host's obligation, not device logic.
What DV should verify. That the active address does not change when a request is merely received; that it changes only on a completion with something pending; that a failure discards without committing; that a bus reset clears both registers; that a completion with nothing pending leaves the address untouched; and — the requirement §7 had to discover the hard way — that no commit pulse is ever produced without a request to justify it.
5. The Assertions
// ─────────────────────────────────────────────────────────────────────────
// Assertions for usb_address_reg.
//
// Classification: TEACHING ASSERTIONS about this model's commit semantics.
// They express the protocol's timing rule as a local invariant; they are
// not a USB compliance suite.
// ─────────────────────────────────────────────────────────────────────────
// A1 -- THE CENTRAL ONE. Receiving the request must not change the active
// address. This is section 2's rule stated as a property, and it catches
// the single most common device-controller bug in enumeration.
property p_no_commit_on_request;
@(posedge clk) disable iff (!rst_n)
(setaddr_req && !xfer_complete && !bus_reset) |=> $stable(active_addr);
endproperty
assert property (p_no_commit_on_request);
// A2 -- the active address changes only at a commit or a reset. Any other
// change means something else is writing the register.
property p_change_only_on_commit_or_reset;
@(posedge clk) disable iff (!rst_n)
!$stable(active_addr) |-> ($past(addr_committed) || $past(bus_reset) || addr_committed);
endproperty
assert property (p_change_only_on_commit_or_reset);
// A3 -- a commit uses the PENDING value, not whatever is on the request
// bus at commit time. Catches a design that samples addr_req at completion,
// which works only because the bus happens to be stable.
property p_commit_uses_pending;
@(posedge clk) disable iff (!rst_n)
addr_committed |-> (active_addr == $past(pending_addr));
endproperty
assert property (p_commit_uses_pending);
// A4 -- a failed transfer must not commit. The device stays where it was so
// the host can retry; a design that commits anyway leaves the device at an
// address the host believes the assignment failed to set.
property p_failure_does_not_commit;
@(posedge clk) disable iff (!rst_n)
(xfer_failed && !xfer_complete) |=> !addr_committed;
endproperty
assert property (p_failure_does_not_commit);
// A5 -- bus reset returns to the default address, and a pending value does
// not survive to commit afterwards.
property p_reset_clears_both;
@(posedge clk) disable iff (!rst_n)
bus_reset |=> (active_addr == USB_DEFAULT_ADDR && !addr_committed);
endproperty
assert property (p_reset_clears_both);
// A6 -- a commit must correspond to a request that has not already been
// committed. This one exists because simulation found a defect the other
// five all miss; section 6 describes it and section 7 shows the evidence.
// The outstanding-request bit is tracked in the CHECKER, deliberately not
// reusing the design's own pending_valid -- a property that trusts the
// signal under suspicion cannot detect its misuse.
logic req_outstanding;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) req_outstanding <= 1'b0;
else if (bus_reset) req_outstanding <= 1'b0;
else if (setaddr_req) req_outstanding <= 1'b1;
else if (addr_committed) req_outstanding <= 1'b0;
else if (xfer_failed) req_outstanding <= 1'b0;
end
property p_no_spurious_commit;
@(posedge clk) disable iff (!rst_n)
addr_committed |-> req_outstanding;
endproperty
assert property (p_no_spurious_commit);A1 is the assertion this chapter exists to produce. It states the protocol's timing rule as something checkable on every cycle of every run, and §6 shows what it catches.
A3 constrains the committed value's provenance, which A1 does not. A design that reads the request bus at completion time rather than using the captured value commits at exactly the right moment and is still wrong, because the bus may have been disturbed meanwhile.
But A3 has a limitation worth stating plainly, because §7 measured it: if the stimulus leaves the request bus holding its value until the commit, then the bus value and the pending value are equal and A3 passes on the broken design. The property is only as strong as the stimulus that exercises it — a point §8 turns into a concrete requirement.
A6 exists because the first five were not enough. Writing this chapter's mutation tests produced a defect that A1 through A5 all miss, and adding a sixth property was the honest response. §6 describes the defect; §7 is the evidence.
6. The Bug: Committing Too Early
A device controller applies the new address as soon as the SetAddress request is decoded. Enumeration fails at exactly that point: the host's transfer never completes, the host retries, and eventually reports that the device is unresponsive.
What happened, precisely? The host began a transfer addressed to the default address. Mid-transfer, the device switched to the new address. The remainder of that exchange — which the host is still conducting at the default address — is now ignored by the device, because its address comparison no longer matches. The host sees a transfer that started normally and never finished.
Why is the symptom so specific? Because the failure is deterministic and always at the same point. Works up to SetAddress, then the device disappears is nearly diagnostic on its own, and it is one of the most recognisable signatures in USB bring-up.
Why might it be missed in testing? Because a device model that switches at the same moment as the DUT will agree with it. An environment whose reference model was written from the same misunderstanding produces a passing test — which is why A1 is written against the protocol rule rather than against a model's expectation.
What catches it? A1 in simulation, immediately, on the first SetAddress.
The second variant, which is much harder to see
A controller that commits at exactly the right moment but forgets to clear the pending flag afterwards has a defect of a completely different shape, and it is worth studying because of how well it hides.
Reason about what such a design does. It commits address N correctly. The pending value — still N, still marked valid — remains. The next completion belonging to some unrelated transfer finds a pending value waiting, and commits again.
Now ask what is actually observable. It re-commits the same address, so the address never becomes wrong. Every check on the address value passes. A1 passes, because the commit did not happen at a request. A2 passes, because the address did not change at all. A3 passes, because the committed value really is the pending value. A4 and A5 are irrelevant. §7 confirms this by measurement: the defect survives every one of the first five properties and every address check in the testbench.
So where is the damage? Not in the value — in the event. addr_committed is a pulse, and Chapter 6.2's state machine consumes it as the device has been addressed, moving to the Address state. A spurious pulse arriving while the device is Configured therefore knocks it back to Address, and its configuration is silently lost.
That is the real failure, and it is a nasty one in the field: the device works, is configured, and then at some unpredictable later moment stops working, with nothing in the address path looking wrong. §7 wires this chapter's register to 6.2's state machine and shows it happening.
What catches it? A6 — and only A6, because A6 is the only property that treats a commit as an event requiring a cause rather than as a value to be checked.
7. Mutation Test
Three mutants, each a plausible way to get this register wrong. All were run; the results below are measured, not predicted, and one of them changed the chapter.
M1 — commit on receipt
The bug of §6: the design applies the address in the same branch that captures it.
if (setaddr_req) begin
pending_addr <= addr_req;
pending_valid <= 1'b1;
active_addr <= addr_req; // MUTANT M1: commit on receipt
addr_committed <= 1'b1;
endResult. A1 fires on the very first SetAddress — active changed on a mere request (0 -> 5) — and again at every subsequent one. A3 fires alongside it, because the value committed at the request cannot equal a pending value that has not yet been captured.
And the part that matters pedagogically: a functional check asking only does the device eventually hold the address the host assigned? passes on this mutant. The device does end up at address 5. Everything about the end state is right; only the timing is wrong, and timing is exactly what a value check cannot see. That is the whole argument for writing A1.
M2 — sample the request bus at commit time
Commits at the right moment, but reads the wrong source.
active_addr <= addr_req; // MUTANT M2: sample the bus instead of the captureResult, and it is two results. Against a testbench that drives the request bus back to zero after the request — the realistic case, since the bus belongs to whatever traffic comes next — A3 fires and the address checks fail. Against a testbench that simply leaves the bus holding its value, the mutant passes every check, A3 included, with zero errors.
That is the finding to take away. The defect did not change; the stimulus did. A property that compares two signals cannot distinguish them while the environment keeps them equal. This is why §8 makes disturbing the request bus a stated stimulus requirement rather than an incidental detail — the assertion is necessary, and by itself it is not sufficient.
M3 — commit correctly, but never clear the pending flag
active_addr <= pending_addr;
// MUTANT M3: pending_valid left set after a correct commitResult: it survived everything. Both testbenches, all address checks, and properties A1 through A5 — zero errors. The functional check passes too. On the evidence available before this mutant was run, the design was verified.
It is not. As §6 reasons out, an unrelated completion re-commits the same address, producing a spurious commit pulse with no wrong value anywhere to reveal it.
Wiring 6.3's register to 6.2's state machine shows the consequence directly. The sequence is: reset, assign address 5, select a configuration, then let one unrelated transfer complete.
GOLDEN
after SetAddress : state=DEV_ADDRESS addr=5
after SetConfig : state=DEV_CONFIGURED cfg=1
after unrelated transfer completion : state=DEV_CONFIGURED cfg=1
>>> device remained CONFIGURED
M3 (pending flag never cleared)
after SetAddress : state=DEV_ADDRESS addr=5
after SetConfig : state=DEV_CONFIGURED cfg=1
after unrelated transfer completion : state=DEV_ADDRESS cfg=1
>>> DEVICE FELL OUT OF CONFIGURED -- configuration silently lostA configured, working device silently stops being configured because a transfer elsewhere finished. A6 catches it, firing once, at exactly that completion: commit with no outstanding request (spurious commit).
8. Verification
This chapter's commit point is address active, the third in the module's chain.
Stimulus. A normal assignment; an assignment whose transfer fails; a bus reset between request and completion; a completion with nothing pending; two SetAddress requests without an intervening completion; and — the case most environments omit — a re-assignment after the device already has an address.
Two stimulus requirements that §7 proved are not optional:
- Drive the request bus to something else after the request. A generator that helpfully holds the value until commit makes A3 unfalsifiable, and M2 passes clean. The bus in real hardware belongs to whatever traffic comes next, so holding it is the unrealistic choice as well as the weaker one.
- Let unrelated transfers complete while the device is addressed and configured. Without a completion that has nothing to do with addressing, M3's spurious commit never occurs and the defect is invisible. This is a stimulus most address-focused testbenches have no reason to generate — which is exactly why the bug survives.
Observation. The active address as the device actually uses it, not the value the host requested. This is Chapter 5.3 §2's requested-versus-active distinction reappearing: the host requested address N, and whether the device answers at N is a separate fact to be observed.
Reference model. Small and worth building here: expected pending value, expected valid flag, expected active address. Compare after every event. Chapter 6.6 grows it into a full enumeration model.
Representative coverage — crosses:
- request × completion, request × failure, request × bus reset before completion
- completion with pending valid × completion with nothing pending
- address values at the boundaries of the assignable range — 1 and 127
- re-assignment while already addressed
- bus reset at each point between request and commit
- unrelated completions crossed with device state, Address and Configured especially, since that cross is what exposes M3
Negative cases with defined outcomes: a completion belonging to some other transfer must not move the address; a failed assignment must leave the device reachable where it was, so the host's retry succeeds.
9. Common Misconceptions
10. Reason It Through
Trace the following, tracking protocol state, pending address and active address after each event: reset; SetAddress(9) received; transfer completes; bus reset; SetAddress(9) received; transfer fails.
After reset. State Default, pending invalid, active = 0. The device answers at the default address.
After SetAddress(9) received. State Default, pending = 9 and valid, active still 0. The device is still answering at 0 — which is essential, because the transfer carrying this request is being conducted there.
After completion. Commit fires. State Address, pending invalid, active = 9. The device now answers at 9, and the host will allow a recovery interval before using it.
After bus reset. State Default, pending invalid, active = 0. Both registers cleared — Chapter 6.2 §3's scope rule, and note that this is the case §6's second variant would get wrong.
After SetAddress(9) received again. State Default, pending = 9 and valid, active still 0.
After the transfer fails. Pending discarded, active remains 0, state remains Default. The device is exactly where the host believes it to be, which is what makes the host's retry meaningful.
What does the trace teach? That the two registers hold genuinely different facts at three separate moments, and that every exit from the pending state — commit, fail, reset — has a defined and different outcome. A single register cannot express any of it.
11. Understanding Check
12. Summary
A shared bus needs addresses, and a freshly reset device has none — the same circularity reset solved, one layer up. USB resolves it with a reserved default address, which works because the host enumerates one device at a time. The address field is seven bits, so reserving one leaves 127 assignable addresses.
The chapter's core is the commit boundary. The acknowledgement of the address request is itself addressed traffic, so the device captures the new address, completes the transfer at the old address, and commits afterwards. Both ends know where that boundary falls because it is the completion of the transfer itself — and the host then allows a recovery interval before using the new address.
That protocol rule creates hardware state: a pending register, an active register, and a validity flag, because two different facts are true simultaneously for the duration of a transfer. Every exit from pending has a defined outcome — commit on completion, discard on failure so the host can retry, discard on bus reset so a stale assignment cannot surface later.
The bug it prevents is committing on receipt, whose signature — works up to SetAddress, then the device disappears — is among the most recognisable in USB bring-up. Mutation testing confirmed both halves of the argument for asserting it: the mutant fires A1 immediately, and a functional check asking only whether the device ends up at the right address passes.
Two harder lessons came out of the same exercise. A mutant that samples the request bus at commit passes every check, A3 included, whenever the stimulus leaves the bus held — the property is correct and the environment makes it unfalsifiable. And a mutant that never clears the pending flag survived all five original properties, because its damage is a spurious commit event rather than a wrong value, and that event costs the device its configuration in a different module. It forced a sixth property: every commit must have a cause, tracked independently of the flag under suspicion.
And the verification lesson running under all of it: a reference model written from the same misunderstanding will agree with a buggy DUT, which is why the assertions state the protocol rule rather than a model's expectation.
13. What Comes Next
The device has an address. The host can now reach it uniquely, and every subsequent request goes to that address rather than to the default.
What the host still does not have is any idea what the device is. It knows a thing exists at address N, operating at a known speed, in the Address state. Not its type, its capabilities, its power requirements, or what it can do.
Chapter 6.4 is how that changes — the host asks the device to describe itself, and reads the answer. The interesting part is not that descriptors are read but why the reading has an order, and why the host's very first read is often deliberately incomplete. That turns out to follow from the same reasoning that has governed this whole module: at each step the host may only act on what it already knows, and the first read happens when it knows least.
Browse the full path on the USB tutorials index.
Continue learning
Related tutorials
- Related topic
Descriptor Discovery
Why the host's first descriptor read is deliberately incomplete, why that partial read is a recurring pattern rather than a workaround, why the walk order is forced, and the gated sequencer RTL that intuition gets wrong.
- Related topic
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.
- Related topic
USB Reset
Reset as an assertion rather than a question: what a bus reset clears, what it deliberately does not, and why a protocol bus reset and an RTL reset are different mechanisms with different scopes.
- Related topic
Enumeration Timing
The numbers enumeration runs on and why each one exists — debounce, reset duration and recovery, the SetAddress recovery interval — plus the layered-timeout structure that decides when a slow device becomes a broken one.
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.
