USB · Module 11
Handshake Packets
Four one-byte packets and a silence: the difference between not now and not ever, and why a reference model reported zero divergence on a design that was wrong.
Chapter 11.2 left a device acknowledging. It said nothing about what happens when it cannot.
That is this chapter, and it is carried by the smallest packets in the protocol: a PID and nothing else. No address, no payload, no CRC beyond the PID's own check field. One byte.
1. Four Answers and a Silence
| Packet | PID | Low nibble | Means |
|---|---|---|---|
| ACK | 0xD2 | 0010 | Received and accepted |
| NAK | 0x5A | 1010 | Not now. Try again |
| STALL | 0x1E | 1110 | Not ever. Do not try again |
| NYET | 0x96 | 0110 | Took this one; no room for the next |
Every low nibble ends in 10 — the handshake group, one of Chapter 11.5's four groups of four.
And there is a fifth response that is not a packet: silence. A device that says nothing has said something quite specific, and §3 is about what.
2. Not Now Versus Not Ever
The central distinction, and the one that determines a device's entire error model.
NAK means the condition is transient. A buffer is full, data is not ready yet, the firmware has not got to it. Nothing is wrong. The host should retry, and it will succeed once the condition clears.
NAK is not an error. It is flow control.
On a typical bulk endpoint NAKs vastly outnumber ACKs — the host polls faster than the device produces, and most polls are answered not yet. Chapter 10.4 §4's nothing new is exactly this, seen from the wire.
STALL means the condition is permanent. The endpoint has halted, or the request is one this device cannot satisfy. Retrying is pointless, and the host must stop and take explicit recovery action — Chapter 11.2 §4's ClearFeature(ENDPOINT_HALT), which also resynchronises the toggle.
Confusing them fails in both directions, and neither failure is quiet:
- NAK where STALL belongs: the host retries a condition that will never clear. The endpoint spins forever, consuming bus capacity, with no error reported to anybody. From outside it looks like an extremely slow device.
- STALL where NAK belongs: the host abandons a transfer for a condition that would have cleared on its own microseconds later. A full buffer becomes a failed transfer, and the driver sees a halted endpoint that nothing was actually wrong with.
3. Silence Is an Answer
A device that transmits nothing has said something the four packets cannot.
Silence means I did not reliably receive this. Its only correct use is a transaction whose data failed its CRC — Chapter 11.6's verdict.
Why not answer a corrupted packet? Because every answer would be a lie:
- ACK claims the data was accepted. It was not.
- NAK claims the device could not take it now, implying it received it and understood the request. It did not.
- STALL claims a permanent condition. Corruption is not one.
And there is a deeper reason. A device whose CRC failed does not know that the packet was for it. Chapter 11.1 §4 made the same argument for tokens: a device that cannot trust what it received cannot know that a reply would be addressed to the right party. Answering risks transmitting over another device's transaction.
Silence is also what the host is prepared for. It timed out waiting; the timeout path already exists; and a timeout is exactly the signal nothing reliable happened, which is true.
4. NYET, and Why It Exists
NYET says: I took this packet, and I have no room for the next one.
It is high-speed only, and it exists as a bandwidth optimisation rather than a correctness mechanism. Without it a high-speed host discovers a full endpoint by sending a full-size data packet and being NAKed — the whole payload is transmitted and thrown away. At high speed that is an expensive way to ask a question.
NYET lets the device answer the question in advance. The host, told there is no room for the next packet, asks with a tiny PING packet instead of a full one, and only sends real data once the answer is affirmative.
Which makes NYET a hint, not a refusal — and this is the part that is easy to get backwards:
NYET acknowledges the current transaction. It is ACK plus a warning, not a form of NAK.
A device that treats NYET as a refusal will have accepted a packet and told the host it did not — and Chapter 11.2's toggle will then be one step apart at the two ends, which is the wedge that chapter measured.
5. The SETUP Exemption
One rule overrides everything above:
A SETUP transaction may never be refused. Not NAKed, not STALLed, not by a halted endpoint, not by a full one.
Because the alternative is a device that can make itself unmanageable. Chapter 10.2 §1 established that control transfers are how a host manages a device it has not yet agreed anything with; a device that could decline the opening of that conversation could refuse the very request that would fix it — including the ClearFeature(ENDPOINT_HALT) that clears the halt causing the refusal.
The design consequence is concrete and often missed: a control endpoint must always have room for a SETUP. Not usually. Always. That storage cannot be shared with anything that might be occupied, and it must survive whatever condition has halted the endpoint.
This rule is also where the module's own development went wrong, which §8 reports rather than hides.
6. The Handshake Selector, as RTL
// ─────────────────────────────────────────────────────────────────────────
// usb_handshake_select
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It models the
// choice of answer at the end of one transaction.
//
// WHAT IT MODELS. Sections 2 to 5 as a priority chain: silence beats every
// answer, a SETUP beats every refusal, permanent beats transient, and a
// warning rides along with an acceptance.
//
// WHAT IT DOES NOT MODEL. The handshake PACKET -- its PID bits, framing and
// transmission (Module 14); the halt condition's own lifecycle, which is
// set by a request or an error and cleared only by ClearFeature (Module 13);
// the PING protocol NYET enables (Module 12); the transaction timing that
// decides WHEN this answer must be on the wire; and the endpoint buffer
// whose occupancy produces `ep_ready` (Chapter 9.5).
//
// ── ON THE PRIORITY ORDER ───────────────────────────────────────────────
// The order of the branches below is the specification, not a style
// preference. Every adjacent swap is a real defect and section 7 measures
// three of them. Written as an if/else chain rather than a case so that the
// precedence is syntactically visible rather than implied.
// ─────────────────────────────────────────────────────────────────────────
module usb_handshake_select (
input logic clk,
input logic rst_n,
input logic txn_active, // a transaction is concluding now
input logic is_setup, // section 5 -- overrides every refusal
input logic ep_halted, // sticky; cleared only by ClearFeature
input logic ep_ready, // room to accept / data to send
input logic data_ok, // Chapter 11.6: the payload's CRC passed
input logic hs_capable, // high speed -- NYET exists at all
input logic space_after, // room for ANOTHER packet after this one
output logic send_ack,
output logic send_nak,
output logic send_stall,
output logic send_nyet,
output logic send_nothing // section 3 -- an answer, not an absence
);
// SECTION 5, AS ONE SIGNAL. Making the exemption a named term rather than
// repeating `&& !is_setup` on each refusal is what keeps the two refusal
// paths from drifting apart -- and section 8 reports what happened when
// this block did NOT have it.
logic may_refuse;
assign may_refuse = !is_setup;
logic refuse_forever, refuse_for_now;
assign refuse_forever = ep_halted && may_refuse;
assign refuse_for_now = !ep_ready && may_refuse;
always_comb begin
send_ack = 1'b0;
send_nak = 1'b0;
send_stall = 1'b0;
send_nyet = 1'b0;
send_nothing = 1'b0;
if (!txn_active) begin
send_nothing = 1'b1;
end else if (!data_ok) begin
// SECTION 3. Every possible answer would assert something false, and
// a device that cannot trust the packet cannot know a reply belongs
// to this transaction at all.
send_nothing = 1'b1;
end else if (refuse_forever) begin
// SECTION 2. Permanent beats transient: an endpoint that is both
// halted and full is halted, and saying NAK would invite a retry that
// can never succeed.
send_stall = 1'b1;
end else if (refuse_for_now) begin
send_nak = 1'b1;
end else if (hs_capable && !space_after && may_refuse) begin
// SECTION 4. NYET is an ACCEPTANCE carrying a warning. It sits here,
// below both refusals, because it is not one.
send_nyet = 1'b1;
end else begin
send_ack = 1'b1;
end
end
endmoduleWhat it models. The selection of one of five responses at the end of a transaction.
Engineering reason. Because the choice between not now and not ever is the whole of a device's error model, and it is one bit on the wire.
Inputs. Whether a transaction is concluding, whether it is a SETUP, the endpoint's halt and readiness, the CRC verdict, and two facts about high-speed capability.
State retained. None. The halt condition is sticky, but it is stored elsewhere and arrives here as an input — this block is the decision, not the memory.
Outputs. Five mutually exclusive indications, one of which is transmit nothing.
Hardware implied. A priority encoder over five conditions. Tens of gates.
Reset behaviour. Nothing to reset. clk and rst_n are present for §7's properties.
Assumptions. That data_ok is meaningful for this transaction; that ep_halted is the sticky condition rather than a momentary one; that is_setup comes from Chapter 11.1's filter and is therefore already addressed to this device; and that space_after refers to the packet after the one being answered.
Omissions. The packet itself, the halt lifecycle, the PING protocol, the timing and the buffer — in the header.
What DV should verify. That exactly one response is produced, always; that a SETUP is never refused; that a corrupted payload is never answered; that a transient condition never produces STALL; that a permanent condition never produces NAK; and that NYET never appears where a refusal belongs.
Every answer a device can give
8 cycles7. Mutation Test
Four mutations over 528 stimuli — all 128 input combinations exhaustively, plus 400 randomised. Exhaustive coverage is affordable here because the block is combinational with seven inputs, and §9 argues that changes what the bench owes.
The unmutated block's response histogram, which the mutant columns are read against:
ACK 77 · NAK 14 · STALL 32 · NYET 8 · silence 397 — with 0 violations of every obligation.
| ACK | NAK | STALL | NYET | silence | obligation violated | |
|---|---|---|---|---|---|---|
| golden | 77 | 14 | 32 | 8 | 397 | — |
| H1 NAK where STALL belongs | 77 | 46 | 0 | 8 | 397 | permanent made retryable: 32 |
| H2 STALL where NAK belongs | 77 | 0 | 46 | 8 | 397 | transient made fatal: 14 |
| H3 answer a corrupt payload | 77 | 164 | 32 | 8 | 247 | corrupt answered: 150 |
| H4 SETUP refusable | 20 | 29 | 73 | 9 | 397 | SETUP refused: 56 |
Each mutation zeroes or inflates exactly one response class and violates exactly one obligation — which is the shape a priority chain's mutation table should have, and a useful check that the obligations are independent rather than restatements of each other.
H1 — NAK where STALL belongs
Measured: STALL disappears entirely — 32 occurrences become NAKs.
The device now reports a permanent condition as transient. The host retries, forever, and every individual packet is well formed and correct. §2's callout is the consequence: nothing anywhere is told that anything is wrong.
And note what the histogram does not show. ACK, NYET and silence are unchanged. A bench checking that the device acknowledges what it should, refuses what it should and stays silent when it should passes completely — the defect is entirely in which kind of refusal.
H2 — STALL where NAK belongs
Measured: NAK disappears — 14 occurrences become STALLs.
The mirror image, and the less dangerous one. A full buffer becomes a halted endpoint; the driver is told a transfer failed; somebody gets an error to search for.
The measurement makes §2's asymmetry concrete: H1 and H2 are the same edit in opposite directions, H1 corrupts more responses (32 versus 14), and H2 is the one you will hear about.
H3 — answer a corrupt payload with NAK
Measured: silence drops from 397 to 247 — 150 transactions answered that should have had no answer.
The largest count of the four, and the least subtle failure, because it breaks §3's rule on every corrupted packet rather than on some condition. A device that NAKs a bad CRC tells the host I received this and cannot take it now, when the truth is I do not know what I received, or whether it was for me.
The practical damage is worse than a wrong answer. The device may be transmitting over another device's transaction — it never established that the packet was addressed to it.
H4 — allow a SETUP to be refused
Measured: 56 refused SETUPs. ACK falls from 77 to 20; STALL more than doubles, from 32 to 73.
§5's rule removed. A halted control endpoint now refuses the very request that would clear the halt, and the device becomes unrecoverable by any means short of a bus reset.
It is also the mutation with the largest footprint in the histogram, moving three of the five columns — because the SETUP exemption sits above both refusal tests and therefore affects every path below it.
8. What Went Wrong While Writing This Block
The first version of the RTL above refused SETUPs, and the reference model agreed with it.
The exemption was written as ep_halted && !is_setup on the permanent path and plain !ep_ready on the transient one. STALL was correctly suppressed for a SETUP; NAK was not. A SETUP arriving at a busy control endpoint was answered not now, which §5 says may never happen.
The reference model reported zero divergences, across all 128 exhaustive combinations, because it had been written from the same understanding as the RTL — the same !is_setup on one branch and not the other.
What caught it was a check written from the rule instead of from the design:
if (txn_active && data_ok && is_setup && (send_stall || send_nak)) setup_refused++;It fired 34 times.
9. The Assertions
// ─────────────────────────────────────────────────────────────────────────
// Classification: TEACHING ASSERTIONS about the handshake selector.
// X is exclusivity. O-properties are the OBLIGATIONS -- each one is a
// sentence from sections 2 to 5, written in the specification's terms and
// deliberately NOT derived from the RTL. Section 8 is why.
// ─────────────────────────────────────────────────────────────────────────
// X1 -- EXACTLY ONE RESPONSE, ALWAYS. Including "nothing", which is why this
// is $onehot and not $onehot0: a transaction with no response at all is a
// distinct defect from one with two.
property p_exactly_one_response;
@(posedge clk) disable iff (!rst_n)
$onehot({send_ack, send_nak, send_stall, send_nyet, send_nothing});
endproperty
assert property (p_exactly_one_response);
// O1 -- A SETUP IS NEVER REFUSED. Section 5. The property that would have
// caught the defect in section 8 on the first run.
property p_setup_never_refused;
@(posedge clk) disable iff (!rst_n)
(txn_active && data_ok && is_setup) |-> (!send_stall && !send_nak);
endproperty
assert property (p_setup_never_refused);
// O2 -- A CORRUPT PAYLOAD IS NEVER ANSWERED. Section 3.
property p_corrupt_gets_silence;
@(posedge clk) disable iff (!rst_n)
(txn_active && !data_ok) |-> send_nothing;
endproperty
assert property (p_corrupt_gets_silence);
// O3 -- A TRANSIENT CONDITION IS NEVER FATAL. Section 2. Note that the
// antecedent names the CONDITION (not halted, not ready) rather than the
// design's internal `refuse_for_now`: an obligation that referenced the
// design's own term would inherit the design's own misunderstanding.
property p_transient_not_fatal;
@(posedge clk) disable iff (!rst_n)
(txn_active && data_ok && !ep_halted && !ep_ready && !is_setup) |-> send_nak;
endproperty
assert property (p_transient_not_fatal);
// O4 -- A PERMANENT CONDITION IS NEVER RETRYABLE. Section 2, the other way.
property p_permanent_not_retryable;
@(posedge clk) disable iff (!rst_n)
(txn_active && data_ok && ep_halted && !is_setup) |-> send_stall;
endproperty
assert property (p_permanent_not_retryable);
// O5 -- NYET IS AN ACCEPTANCE. Section 4: it may only appear where the
// alternative was ACK, never where a refusal belonged. Stated as what must
// be TRUE when NYET is sent, so that it constrains NYET's placement in the
// priority chain rather than merely its encoding.
property p_nyet_only_over_ack;
@(posedge clk) disable iff (!rst_n)
send_nyet |-> (txn_active && data_ok && !ep_halted && ep_ready && hs_capable);
endproperty
assert property (p_nyet_only_over_ack);
// I1 -- AND NOTHING IS SENT OUTSIDE A TRANSACTION.
property p_idle_is_silent;
@(posedge clk) disable iff (!rst_n)
!txn_active |-> send_nothing;
endproperty
assert property (p_idle_is_silent);O3 and O4 are the pair that pins §2. Each states one direction of the not now / not ever distinction, and neither implies the other — H1 violates O4 while satisfying O3, and H2 does the reverse. A single property covering both would have to encode the whole decision, which would make it a reference model in property clothing and lose §8's independence.
O5's shape is the one to copy. The temptation is to write NYET implies not NAK, which is trivially true given X1. Instead it states the full set of conditions under which NYET is permitted — which constrains where it sits in the priority chain, not merely that it is distinct from the others.
10. Verification
This chapter's commit point is the host was told the right kind of thing about whether to try again.
Stimulus. All 128 combinations of the seven inputs, exhaustively — the block is combinational and small enough that this is affordable, plus 400 randomised stimuli to catch state that should not exist.
Observation. All five outputs plus a histogram of the response classes, which is what makes §7's table readable — a mutation that converts one class into another is visible as a zero, and a zero in a class that should occur is a stronger signal than a count that merely shifted.
Reference model. A five-way priority chain. §8 is an extended argument about its limits: it is worth having for the errors it does catch, and it must never be the only thing in the bench.
Coverage — the crosses are the 128 combinations, but three deserve naming because they are where the rules live:
is_setup×ep_halted×ep_ready— all eight, including SETUP, halted and full, which is §5's rule at full strength and the cell §8's defect lived indata_oklow × every other input — silence must dominate all of themhs_capable×space_after×ep_ready— NYET's placement relative to both refusals
Negative cases with defined outcomes: never two responses; never zero; never an answer to a corrupt payload; never a refused SETUP; never STALL for a transient condition; never NAK for a permanent one; and never NYET where a refusal belonged.
11. Debugging: the Transfer That Never Completes and Never Fails
A bulk transfer hangs. The application waits indefinitely. No error is reported. A protocol analyser shows continuous, well-formed traffic between host and device for as long as you care to watch.
What does well-formed traffic, forever tell you? That both ends are working correctly at the packet level and disagree about something at the transfer level. Nothing is malformed; the conversation simply never concludes.
What are the candidates? Exactly two, and the trace separates them immediately:
- The device is NAKing continuously. It is saying not yet, and the host is obediently retrying.
- The device is not responding and the host is timing out. Different trace, different fix.
Given a trace full of NAKs, what next? Ask whether the condition can clear. A device NAKing because its buffer is full will stop when the firmware drains it. A device NAKing because its endpoint is halted will never stop — and that is §7's H1.
How do you tell those apart from outside? Try to provoke the condition to clear. If the transfer completes when you reduce the rate or give the device idle time, it is genuine flow control. If nothing you do on the host side changes anything, the device is reporting a permanent condition as transient.
And the confirming test? Issue ClearFeature(ENDPOINT_HALT). If the endpoint recovers, it was halted all along — and the device should have been sending STALL, because STALL is what tells the host to issue that request in the first place. The device knew; it just described the condition wrongly.
Why is no error reported? Because no layer has anything to report. Every packet is valid, the device is answering promptly, and the host's retry logic is behaving exactly as designed. The error is in the meaning of a correct packet, and no amount of protocol validation detects that.
The signature to keep: a transfer that never completes and never fails, with a clean trace, is a NAK that should have been a STALL — and the test that confirms it is whether an explicit halt-clear makes the problem disappear.
12. Common Misconceptions
13. Reason It Through
A device's bulk IN endpoint returns STALL under heavy load and works fine when lightly loaded. The firmware author insists nothing sets the halt condition, and inspection of the code confirms it — there is no code path that halts this endpoint.
If nothing halts the endpoint, where is the STALL coming from? Either the halt input is asserted by something other than the intended code path, or the selection logic is producing STALL without a halt. §7's H2 is exactly the second.
What does only under load suggest? That the trigger is the condition which load produces — a full buffer, which is §2's transient case. So the shape is: the transient condition is being answered with the permanent response, and load simply makes the transient condition frequent.
Is the firmware author wrong? No, and this matters. They are right that nothing halts the endpoint — and the bug is real anyway, because STALL is being sent without the halt. Looking for the code that sets the halt is looking for something that does not exist, which is why this class of bug consumes days.
What is the single most useful next observation? The value of the halt condition at the moment STALL is transmitted. If halt is low and STALL goes out, the selection logic is wrong and the search should move out of the firmware entirely.
Could it be the host's interpretation instead? Possible but unlikely, and testable: a second host would have to make the same mistake. Test the cheap hypothesis first — but note that the device sends STALL and the host reports a stall are different claims, and only a bus trace distinguishes them.
And what makes this expensive in practice? The symptom points at the halt condition, and the halt condition is innocent. The evidence for the real fault is a signal that is not asserted — and a missing assertion leaves no trace anywhere, which is why the trace shows only the consequence and never the cause.
14. Understanding Check
15. Summary
Handshake packets are one byte: a PID and nothing else. Four of them — ACK, NAK, STALL, NYET — plus silence, which is a defined response rather than an absence.
The central distinction is not now versus not ever. NAK is flow control, not an error, and outnumbers ACK on a typical bulk endpoint. STALL is permanent and requires explicit recovery. They differ by one bit in the PID's low nibble — because PID values are chosen for detectability, not semantic distance.
Confusing them fails asymmetrically. STALL-for-NAK fails a transfer and gets reported. NAK-for-STALL spins forever with every packet well formed and nothing anywhere reporting a fault — the device has told the truth in each packet and lied about the whole.
Silence answers a corrupted payload, because every alternative asserts something false and because a device that cannot trust the packet cannot know its reply belongs to this transaction.
NYET is an acceptance with a warning, not a refusal — high-speed only, and a bandwidth optimisation rather than a correctness mechanism.
And a SETUP may never be refused, which forces a control endpoint to always have room for one. §7 measured all four mutations against 128 exhaustive input combinations: NAK-for-STALL corrupted 32 responses; STALL-for-NAK, 14; answering a corrupt payload, 150; a refusable SETUP, 56 — each violating exactly one obligation and leaving the others clean.
§8 is the chapter's hardest lesson, and it is about this module's own development. The first version of the block refused SETUPs on the NAK path. The reference model reported zero divergences across all 128 combinations, because it had been written from the same misunderstanding. A one-line obligation, phrased in the specification's own words, found it immediately.
Zero divergence against a reference model is evidence that the design matches your understanding. It is not evidence that your understanding is right.
And §10 pairs that with Chapter 11.1's finding to make the general point: that bench asked the right question of too little stimulus; this one asked the wrong question of all of it. Exhaustive coverage removes every excuse about reachability and none about what you are checking — and a coverage report measures only the first.
16. What Comes Next
Three packet types down, and every one of them was addressed to somebody. The next is addressed to nobody at all.
Chapter 11.4 is SOF Packets — the start-of-frame, broadcast to the whole bus, carrying an 11-bit frame number and expecting no reply. It is how every periodic guarantee in Module 10 actually gets its timebase, and Chapter 11.1 §2 already showed what happens to a decoder that mistakes its frame number for an address.
The chapter's real subject is what a device should do when an SOF fails to arrive — because the frame number is a shared clock, and a clock you only hear about intermittently has to be maintained rather than merely received.
Browse the full path on the USB tutorials index.
Continue learning
Related tutorials
- Related topic
The Parallel Port
Why presenting eight data lines at once forces an explicit data/strobe/acknowledge handshake, what that costs in timing discipline, a synthesizable teaching FSM that implements it with the assertions that protect it, and why an interface shaped around one peripheral's operational model cannot generalise.
- Related topic
Token Packets
The packet that names a destination before anything is sent to it — four reasons to ignore one, and the SOF whose eleven bits are not an address at all.
- Related topic
Data Packets
Two PIDs carrying identical data and one alternating bit: why it advances on acknowledgement rather than arrival, and the two bench defects that hid two of four mutations.
- Related topic
SOF Packets
The packet addressed to nobody, carrying the timebase every periodic guarantee depends on — and why a device must keep counting frames it was never told about.
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.
