Skip to content
VLSI Mentor

USB · Module 12

Transaction Flow

Three stages in order, as a state machine — and the hardest problem in the module: two ends maintain separate beliefs about one transaction, and nothing on the wire reconciles them.

Three chapters described three stages in isolation. This one puts them in order, and the order is where the interesting failures live — because a sequencer has to answer three questions none of the stage chapters could.

What happens when a packet arrives that belongs to a different stage? How does a transaction end when the next stage never comes? And the hard one: what does a device believe about a transaction the host has already given up on?

1. The Shape, and Why It Is Not Three Stages

The canonical transaction is token → data → handshake. It is also not always three stages, and the exceptions are not edge cases:

TransactionStagesWhy
OUT / SETUPtoken → data → handshakethe device receives, so the device answers
INtoken → data → handshakethe device sends, so the host answers
Isochronoustoken → datano retry, so nothing to acknowledge — Chapter 12.3 §2
Any, interruptedtoken → nothingthe device could not continue — Chapter 12.1 §2

The last row is the one a state machine must be designed around rather than patched for. A transaction that stops partway through is not an exceptional case: it is what every failure on the bus looks like from inside the sequencer, because Chapter 11.3 §3 established that the response to anything untrustworthy is silence.

A USB transaction has no error packet. Every failure presents identically: the next stage does not happen.

Which means a sequencer cannot be driven by events alone. Events tell it what arrived; nothing tells it what failed to arrive, and that is the more common case.

2. The Two Beliefs

The module's hardest idea, and it is worth stating before any RTL.

The host maintains a belief about the transaction. The device maintains its own. Nothing on the wire carries either one.

Each end infers the other's state from what it has seen — and the inferences diverge whenever a packet is lost, because a lost packet is invisible to the end that would have received it.

The four disagreements, and they are not symmetric:

Lost packetHost believesDevice believes
Tokena transaction is runningnothing is happening
OUT datadata was deliveredthe token was never followed
IN datathe device is lateit has sent its data
Handshakethe transaction failedthe transaction succeeded

The last row is the dangerous one, and it is the reason Chapter 11.2 exists: both ends are certain, both are reasoning correctly from what they saw, and they disagree about whether data was delivered.

3. Out of Phase

A packet arrives that belongs to a stage other than the current one. A handshake during the token stage; a data packet while waiting for a handshake.

It is not noise, and it is not usually a host error. The commonest cause is the disagreement of §2: the host has moved on and this device has not, so the host's packets are perfectly correct for its belief and wrong for this one.

Three possible responses, and only one is right:

  • Act on it. Treat the handshake as though it concluded the stage. Wrong — it concludes a transaction this device is not in.
  • Ignore it silently. Safe, and throws away the only evidence that the two ends have diverged.
  • Ignore it and report it. Right. The packet cannot be acted on, and the fact that it arrived is diagnostic information available nowhere else.

An out-of-phase packet is the only direct evidence a device ever gets that its belief and the host's have diverged.

And it is cheap — a flag and a pulse. §6's F3 measures what its absence costs, and the answer is that it costs nothing functionally and removes the one signal that would explain a field failure.

A sequence diagram showing an OUT transaction in which the handshake is lost, viewed from both ends. The host sends an OUT token and then a data packet. The device receives the data, accepts it, passes it to the endpoint and transmits an acknowledgement, concluding that the transaction succeeded. The acknowledgement is corrupted in transit and never reaches the host. The host's timeout expires, and it concludes that the transaction failed and the data was not delivered. The two ends now hold opposite beliefs about the same transaction, and nothing on the wire carries either belief. The host retransmits the identical data packet; the device recognises it by its data toggle as a retransmission of something it already has, discards the payload and acknowledges again. This acknowledgement arrives, and the two beliefs are reconciled — by the host's retry and the device's toggle, not by any mechanism that compared them.Two correct ends, two opposite beliefsHost's beliefThe wireDevice's beliefOUT token · DATA0received, accepted,handed upACK — "thissucceeded"…corrupted. itreaches nobodydevice: TRANSACTIONSUCCEEDEDtimeout · host:TRANSACTION FAILEDretransmit —identical DATA0toggle says: alreadyhave thisdiscard payload ·ACK again…this one arrivesreconciled — byretry, not bycomparison
Figure 1 — the same transaction from both ends, with the handshake lost. Each end reasons correctly from what it saw and reaches the opposite conclusion. Nothing in the protocol reconciles them; the host's retry does, and the device's only contribution is to be in a state where that retry works.

4. Ending a Transaction Three Ways

A transaction leaves the sequencer by exactly one of three routes, and confusing them is §6's most damaging mutation.

Completed. The final stage happened. The endpoint's state advances — Chapter 11.2's toggle moves, the buffer is committed.

Abandoned. Either a timeout fired, or a new token arrived while this one was still open — Chapter 12.1 §6's overrun. Nothing advances. Partial state is discarded, and the endpoint is left exactly as it was before the token.

Ended by a bus reset. The whole device is being rebuilt (Chapter 8.3), so there is no transaction-level cleanup to do.

5. The Transaction Sequencer, as RTL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// usb_txn_pkg + usb_transaction_fsm
//
// Classification: SIMPLIFIED SYNTHESIZABLE TEACHING RTL. It sequences the
// three stages and, more importantly, handles the case where the next one
// never arrives.
//
// WHAT IT MODELS. Section 1's stage order including its exceptions, section
// 3's out-of-phase detection, and section 4's three endings.
//
// WHAT IT DOES NOT MODEL. Any packet (Module 11); the context the token
// creates (Chapter 12.1, consumed here as `token_accept` and its
// qualifiers); the data stage's termination rules (Chapter 12.2); which
// handshake to send (Chapter 11.3) or who sends it (Chapter 12.3); the
// timeout's duration and derivation (Chapter 12.5 -- `timeout` arrives here
// already expired); and the endpoint state that a completion advances.
//
// ── WHY THERE IS NO ERROR STATE ─────────────────────────────────────────
// Section 1: a USB transaction has no error packet, so there is no event
// that means "this went wrong". Every failure is the ABSENCE of the next
// stage, and absence is not an input -- which is why `timeout` is the only
// thing that can end a stalled transaction, and why removing it (section
// 6's F1) leaves the machine stuck rather than erroring.
// ─────────────────────────────────────────────────────────────────────────
package usb_txn_pkg;
  typedef enum logic [2:0] {
    T_IDLE      = 3'd0,
    T_TOKEN     = 3'd1,   // a token was accepted; awaiting the data stage
    T_DATA_OUT  = 3'd2,   // data received from the host
    T_DATA_IN   = 3'd3,   // data transmitted to the host
    T_HS_SEND   = 3'd4,   // we owe a handshake      (Chapter 12.3)
    T_HS_WAIT   = 3'd5,   // we await the host's     (Chapter 12.3)
    T_DONE      = 3'd6
  } txn_state_e;
endpackage

module usb_transaction_fsm
  import usb_txn_pkg::*;
(
  input  logic       clk,
  input  logic       rst_n,
  input  logic       bus_reset,

  // Chapter 12.1's filtered, captured token.
  input  logic       token_accept,
  input  logic       token_is_in,
  input  logic       token_is_iso,

  // Stage events. Note there is no `data_failed` or `hs_failed`: see the
  // header. Failure is the absence of one of these.
  input  logic       data_seen,
  input  logic       data_sent,
  input  logic       hs_seen,
  input  logic       hs_sent,
  input  logic       timeout,        // Chapter 12.5

  output txn_state_e state,
  output logic       txn_active,
  output logic       txn_complete,   // section 4: the endpoint may advance
  output logic       txn_abandoned,  // section 4: discard partial state
  output logic       out_of_phase    // section 3: the beliefs have diverged
);

  txn_state_e st_q;
  logic complete_q, abandoned_q, oop_q;

  assign state         = st_q;
  assign txn_active    = (st_q != T_IDLE) && (st_q != T_DONE);
  assign txn_complete  = complete_q;
  assign txn_abandoned = abandoned_q;
  assign out_of_phase  = oop_q;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st_q <= T_IDLE; complete_q <= 1'b0; abandoned_q <= 1'b0; oop_q <= 1'b0;
    end else if (bus_reset) begin
      // Section 4's third ending. Note it does NOT raise `abandoned` -- see
      // the callout there: every consumer of that signal is being reset too.
      st_q <= T_IDLE; complete_q <= 1'b0; abandoned_q <= 1'b0; oop_q <= 1'b0;
    end else begin
      complete_q  <= 1'b0;      // all three are pulses, not levels
      abandoned_q <= 1'b0;
      oop_q       <= 1'b0;

      // ── A NEW TOKEN ALWAYS RESTARTS, FROM ANY STATE ────────────────────
      // Chapter 12.1 section 2: the host owns the bus. A token arriving
      // mid-transaction is not a request to be queued -- it is evidence the
      // host has already abandoned the previous one. Gating this on T_IDLE
      // (section 6's F4) makes the device unreachable after any disagreement.
      if (token_accept) begin
        if (st_q != T_IDLE && st_q != T_DONE) abandoned_q <= 1'b1;
        st_q <= T_TOKEN;

      end else if (timeout && st_q != T_IDLE && st_q != T_DONE) begin
        // THE ONLY WAY A STALLED TRANSACTION ENDS. See the header.
        st_q        <= T_IDLE;
        abandoned_q <= 1'b1;

      end else begin
        case (st_q)
          T_TOKEN: begin
            if      (data_seen)          st_q <= T_DATA_OUT;
            else if (data_sent)          st_q <= T_DATA_IN;
            // Section 3: a handshake here belongs to a transaction we are
            // not in. Not acted on -- but recorded, because it is the only
            // direct evidence the two beliefs have diverged.
            else if (hs_seen || hs_sent) oop_q <= 1'b1;
          end

          T_DATA_OUT: begin
            // Chapter 12.3 section 2: isochronous has no third stage.
            if (token_is_iso) begin st_q <= T_DONE; complete_q <= 1'b1; end
            else                    st_q <= T_HS_SEND;
          end

          T_DATA_IN: begin
            if (token_is_iso) begin st_q <= T_DONE; complete_q <= 1'b1; end
            else                    st_q <= T_HS_WAIT;
          end

          T_HS_SEND: begin
            if (hs_sent) begin st_q <= T_DONE; complete_q <= 1'b1; end
            else if (data_seen) oop_q <= 1'b1;
          end

          T_HS_WAIT: begin
            if (hs_seen) begin st_q <= T_DONE; complete_q <= 1'b1; end
            else if (data_seen) oop_q <= 1'b1;
          end

          T_DONE:  st_q <= T_IDLE;
          default: st_q <= T_IDLE;
        endcase
      end
    end
  end

endmodule

What it models. The order of the three stages, and every way that order can fail to complete.

Engineering reason. Because failure on a USB bus has no packet — it is the absence of the next stage — so the sequencer is the only place that can notice.

Inputs. A token with two qualifiers, four stage events, a timeout, and a bus reset.

State retained. Three bits of state plus three pulse registers — 6 flip-flops.

Outputs. The state, an activity indication, and §4's three endings as pulses.

Hardware implied. A seven-state machine with a two-level priority in front of it.

Reset behaviour. Everything to T_IDLE on hard reset and bus reset, with no abandonment report — §4's documented exclusion.

Assumptions. That token_accept has passed Chapter 11.1's filters and Chapter 12.1's capture; that the four stage events are single-cycle and mutually exclusive within a stage; and that timeout arrives already expired rather than as a running count.

Omissions. Every packet, the context, the data-stage rules, the handshake choice, the timeout derivation and the endpoint state — in the header.

What DV should verify. That every accepted token ends exactly once — §4's law; that a transaction never remains active indefinitely; that a timeout while active always ends it on the next cycle; that a new token restarts from any state; that out-of-phase packets are reported and never acted on; and that completion and abandonment are never simultaneous.

One completed, one abandoned

10 cycles
A waveform of the transaction sequencer over ten cycles covering two transactions. In the first cycle the machine is idle and an OUT token is accepted. It moves to the token state, where a data packet is seen, then to the data-out state, then to the handshake-send state where the device transmits its acknowledgement, then to the done state where the complete pulse is asserted, and back to idle. In the sixth cycle an IN token is accepted. The machine moves to the token state, the device transmits its data, and the machine moves to the data-in state and then to the handshake-wait state. No handshake arrives, so a timeout fires, and on the next cycle the machine returns to idle with the abandoned pulse asserted rather than the complete pulse.completed — the endpoint may advancecompleted — the endpointmay advancenothing came. only a timeout can end thisnothing came. only atimeout can end thisabandoned — nothing advancesabandoned — nothingadvancesstateIDLETOKENDATA-OHS-SNDDONEIDLETOKENDATA-IHS-WAITIDLEeventOUT tokDATA—we ACK—IN tokwe send—TIMEOUT—txn_activecompleteabandonedt0t1t2t3t4t5t6t7t8t9
Figure 2 — two transactions, every column taken from a simulation of the block above. The first completes normally through all three stages. The second is an IN whose handshake never arrives: the device waits, the timeout fires, and the transaction is abandoned rather than completed — which is the difference between the endpoint advancing and the endpoint staying exactly as it was.

6. Mutation Test

Five mutations over 1,295 cycles and 180 accepted tokens, with a randomised phase driving tokens, all four stage events and timeouts independently — so that stage events routinely arrive in stages that do not expect them.

The unmutated design: 180 tokens = 47 completed + 132 abandoned + 1 ended by bus reset, conservation exact; 142 out-of-phase packets reported; 0 timeouts ignored; not stuck; longest active span 52 cycles.

conservationout of phasetimeout ignoredstuck at endlongest active
goldenholds (47+132+1)1420052
F1 no timeout pathFAILS (60+118+1)16166154
F2 handshake accepted earlyFAILS (27+79+1)160026
F3 no out-of-phase detectionholds1210052
F4 token restarts only from idleFAILS (57+29+1)1180035
F5 T_DONE never returns to idleholds1420052

F1 — remove the timeout path

Measured: 66 timeouts ignored, conservation broken by 61 transactions, and the machine left stuck active at the end of the run.

The only exit from a stalled stage is gone. §1: there is no error packet, so nothing else can end a transaction whose next stage never arrives.

And the machine is not wedged permanently — a later token restarts it, which is why 118 abandonments still occurred. That is what makes it insidious: the device recovers on the next transaction, so the symptom is one lost transaction rather than a dead endpoint, and it recurs at exactly the rate packets are lost.

F2 — accept a handshake during the token stage

Measured: conservation broken by 73 transactions, and out-of-phase reports collapse from 142 to 16.

§3's first wrong answer. A handshake belonging to a transaction this device is not in is treated as concluding the current one — so a transaction ends on evidence from a different transaction.

Both effects are visible and they point in opposite directions, which is what makes this mutation legible: fewer transactions end properly and fewer anomalies are reported, because the anomalies are being consumed as normal events.

F3 — stop reporting out-of-phase packets

Measured: conservation holds. Nothing stuck. No timeouts ignored. Every transaction ends exactly as it should. The only change is 142 out-of-phase reports falling to 121.

No functional defect whatsoever — and §3's point arriving as a measurement. What is lost is the only direct evidence a device ever gets that its belief and the host's have diverged.

Whether that matters is a product decision, not an RTL one. A device that will never be debugged in the field does not need it. A device that will be is losing the signal that distinguishes the host is retrying because the bus is noisy from the host and I disagree about what transaction we are in — and those have entirely different fixes.

F4 — allow a token to restart only from idle

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (token_accept && st_q == T_IDLE) begin   // MUTANT F4

Measured: conservation broken by 93 transactions — the largest gap of the five. Abandonments collapse from 132 to 29.

Chapter 12.1 §2's rule removed. A device mid-transaction now ignores the host's new token entirely — and since the host has moved on, the device is waiting for a stage that will never come while refusing the only event that could rescue it.

The timeout does eventually free it, which is why it is not a permanent wedge. But the device misses every transaction issued during the wait, so a single disagreement costs several transactions rather than one.

F5 — T_DONE never returns to idle

Measured: nothing. Conservation holds, out-of-phase count identical, nothing stuck, longest active span unchanged at 52 cycles. Every observable is bit-identical to the golden design.

And it survives because it is genuinely unobservable — for a reason worth more than the mutation.

7. The Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Classification: TEACHING ASSERTIONS about the transaction sequencer.
// L-properties are the LIFECYCLE law; P the progress guarantees;
// O the out-of-phase contract.
// ─────────────────────────────────────────────────────────────────────────

// L1 -- EVERY TOKEN ENDS EXACTLY ONCE. Section 4's law, as a property over
// one transaction rather than as a run-level count. The three disjuncts are
// its three endings, and there is no fourth.
property p_token_ends_once;
  @(posedge clk) disable iff (!rst_n)
    token_accept |=> (!txn_complete && !txn_abandoned)[*0:$]
                     ##1 (txn_complete || txn_abandoned || bus_reset);
endproperty
assert property (p_token_ends_once);

// L2 -- AND NEVER BOTH AT ONCE. Completion and abandonment mean opposite
// things to the endpoint -- one advances the toggle, one discards state --
// so a cycle asserting both is not merely redundant, it is contradictory.
property p_not_both_endings;
  @(posedge clk) disable iff (!rst_n)
    !(txn_complete && txn_abandoned);
endproperty
assert property (p_not_both_endings);

// P1 -- A TIMEOUT ALWAYS ENDS AN ACTIVE TRANSACTION, ON THE NEXT CYCLE.
// Section 6's F1. Bounded to one cycle deliberately: an unbounded
// "eventually" cannot fail in simulation, and Chapter 10.3 section 7
// measured what that costs.
property p_timeout_ends_it;
  @(posedge clk) disable iff (!rst_n)
    (timeout && txn_active && !token_accept && !bus_reset) |=> !txn_active;
endproperty
assert property (p_timeout_ends_it);

// P2 -- A TOKEN ALWAYS RESTARTS, FROM ANY STATE. Section 6's F4. Note the
// antecedent has no state qualifier at all -- that absence IS the property.
property p_token_always_restarts;
  @(posedge clk) disable iff (!rst_n)
    (token_accept && !bus_reset) |=> (state == T_TOKEN);
endproperty
assert property (p_token_always_restarts);

// P3 -- AND SUPERSEDING ONE REPORTS AN ABANDONMENT. Without this, F4's
// silent drop and a correct supersede are indistinguishable.
property p_supersede_abandons;
  @(posedge clk) disable iff (!rst_n)
    (token_accept && txn_active && !bus_reset) |=> txn_abandoned;
endproperty
assert property (p_supersede_abandons);

// O1 -- AN OUT-OF-PHASE PACKET NEVER ADVANCES THE MACHINE. Section 3's
// first wrong answer, and section 6's F2. Stated as what must NOT change,
// because "acting on it" has several shapes and "not moving" has one.
property p_oop_does_not_advance;
  @(posedge clk) disable iff (!rst_n)
    ((state == T_TOKEN) && (hs_seen || hs_sent) && !token_accept
      && !timeout && !bus_reset) |=> (state == T_TOKEN);
endproperty
assert property (p_oop_does_not_advance);

// O2 -- AND IT IS REPORTED. Section 6's F3 -- the ONLY property that catches
// it, because F3 has no functional effect at all.
property p_oop_reported;
  @(posedge clk) disable iff (!rst_n)
    ((state == T_TOKEN) && (hs_seen || hs_sent) && !token_accept
      && !timeout && !bus_reset) |=> out_of_phase;
endproperty
assert property (p_oop_reported);

// I1 -- ISOCHRONOUS NEVER REACHES A HANDSHAKE STAGE. Chapter 12.3 section 2
// enforced from the sequencer's side, so that the two blocks cannot drift.
property p_iso_no_handshake_state;
  @(posedge clk) disable iff (!rst_n)
    (txn_active && token_is_iso)
      |-> (state != T_HS_SEND && state != T_HS_WAIT);
endproperty
assert property (p_iso_no_handshake_state);

L1 is the lifecycle law as a property, and it is worth contrasting with the run-level count §4 used. The count catches an imbalance; the property catches which transaction was imbalanced — and on a 1,295-cycle run with 180 transactions, the difference is between knowing something is wrong and knowing where.

P2's antecedent is bare, and that is the property. A token restarts the machine with no qualification about the current state is exactly §4's rule, and F4 is the mutation that adds a qualification. A property whose strength is in what it omits is unusual and worth recognising — the temptation is always to add the state check that makes it "more precise", which would make it satisfied by the defect.

O1 and O2 must both exist. O1 alone is satisfied by a machine that ignores everything; O2 alone by one that reports and also acts. §6's F2 violates O1 and F3 violates O2, and neither property catches the other's mutation.

8. Verification

This chapter's commit point is every transaction that started, ended — exactly once, and for a reason the endpoint could act on.

Stimulus. Clean transactions of all four shapes (OUT, IN, isochronous OUT, isochronous IN); transactions the host abandons, with silence and then a timeout; a handshake arriving during the token stage; a new token superseding an open transaction; a bus reset mid-transaction; 1,200 randomised events driving tokens, all four stage events and timeouts independently; and finally 30 forced timeouts and 10 idle cycles, to establish that the machine returns to rest.

Observation. The state every cycle, the three ending pulses, the out-of-phase pulse, a running count of accepted tokens against endings (§4's law), and the longest span any transaction stayed active — which is the measurement that catches a machine that never exits, independently of whether it ends things correctly.

Reference model. Deliberately none for the state itself. §4's conservation law and the liveness bound do the work instead, and both are stated in the protocol's terms rather than the design's. Chapter 11.3 §8 measured what a reference model sharing the design's assumptions is worth; for a sequencer, a second state machine would share them completely.

Coverage — crosses:

  • transaction shape (OUT / IN / iso-OUT / iso-IN) × ending (complete / timeout / supersede / bus reset)
  • each of the four stage events × each of the seven states — 28 cells, most of them out of phase
  • timeout arriving in each active state
  • a new token arriving in each state, T_DONE included
  • consecutive abandonments without an intervening completion

Negative cases with defined outcomes: no transaction ends twice; none ends zero times; complete and abandoned never coincide; an out-of-phase packet never advances the state; and no transaction remains active beyond a timeout.

9. Debugging: the Endpoint That Recovers Too Slowly

A bulk endpoint on a busy bus achieves far less throughput than expected. It never stalls and never reports errors. A trace shows the host frequently retrying, and every retry eventually succeeds. Throughput is roughly a third of what the bus should support.

What does every retry eventually succeeds tell you? That nothing is broken in the data path. The bytes are correct when they arrive. The cost is entirely in time.

Where does the time go? Each retry costs whatever the host waited before deciding to retry — and Chapter 12.5's timeout is short, so a few retries should cost very little. A third of the bus is not a few retries.

So what multiplies it? §6's F4 is the shape: a device that ignores the host's new token while waiting out its own stalled transaction loses every transaction issued during that wait, not just the one that failed. One disagreement costs several transactions.

How do you confirm it from the trace? Count. Compare the number of tokens the host issued to this endpoint against the number the device answered. If the device answered fewer, it was deaf for part of the time — and the length of each deaf window is the device's own timeout.

What if the device answered every token? Then it is not deafness, and the throughput loss is in the retry rate itself — a signal-integrity problem rather than a sequencing one, and a completely different investigation.

What is the single most useful device-side signal? §5's out_of_phase. It fires exactly when the device receives a packet belonging to a transaction it is not in, which is the direct observation of §2's divergence. A device that reports it turns this investigation into one measurement.

And §6's F3 is why many devices cannot do this. The flag costs a register and has no functional effect, so it is the first thing removed in a size review — and it is the only thing that would have made this failure diagnosable from the device side.

The signature to keep: throughput loss with no errors and successful retries is a recovery-latency problem — and the question is not why the retry happened but how long the device was unavailable afterwards.

10. Common Misconceptions

11. Reason It Through

A device's sequencer is being reviewed. A reviewer proposes adding a T_ERROR state entered whenever a packet arrives out of phase, exited on the next token. The argument: the machine currently ignores such packets, and ignoring an anomaly is how bugs hide.

Is the premise right? Partly. Ignoring anomalies silently is how bugs hide — which is why §5 already reports them on out_of_phase.

What would the new state add? It would make the machine stop making progress until a token arrives. Currently an out-of-phase packet leaves the state alone, so the transaction continues and can still complete.

Is stopping better or worse? Worse, and for a reason specific to this protocol. §2: an out-of-phase packet usually means the host has moved on. The transaction in progress on this device is already doomed. But not always — a spuriously-decoded packet, or another device's traffic that passed this device's filters, produces the same event on a transaction that is perfectly healthy.

So the proposal trades a certainty for a possibility. It guarantees the current transaction fails, in exchange for making an anomaly more visible — and the anomaly is already visible on a dedicated output.

Is there a version of the proposal that is right? Yes, and it is the useful part of the review: count them. An out-of-phase packet is a pulse; a rate of out-of-phase packets is diagnostic. A saturating counter costs a few bits and turns a transient into a trend, without changing behaviour at all.

What is the general principle? Do not let a diagnostic change behaviour. Chapter 10.5 §6 made the same point from the other direction — a flag that made coalescing visible without altering the data stream. A signal that reports is free to be wrong; a signal that acts is not, and conflating them means an over-sensitive detector becomes an availability problem.

And what should the reviewer be told? That the concern is right, the mechanism already exists, and the improvement is a counter rather than a state. Rejecting the proposal while accepting the argument is the correct outcome, and saying so explicitly is what keeps the next review useful.

12. Understanding Check

13. Summary

A transaction is three stages when nothing goes wrong, and something else otherwise. Isochronous has two; an interrupted transaction has one. And because USB has no error packet, every failure presents identically — the next stage does not happen — which is why a timeout is the only exit from a stalled stage, and why a sequencer cannot be driven by events alone.

The module's hardest idea is §2: the two ends maintain separate beliefs about one transaction, and nothing on the wire carries either. A lost handshake leaves the device certain it succeeded and the host certain it failed, both reasoning correctly.

And only the host can resolve it. The device usually notices first and can do nothing — every recovery mechanism is host-initiated. The device's job is to stay in a state from which the host's recovery works, which is what txn_abandoned exists for: not to report a problem, but to trigger the cleanup that makes the next attempt land somewhere consistent.

An out-of-phase packet is the one piece of direct evidence a device ever gets that the beliefs have diverged — and it is usually correct traffic for the host's belief, not a host error. It must be ignored and reported.

§4's conservation law — tokens = completed + abandoned + ended by bus reset — held exactly on the unmutated design at 180 = 47 + 132 + 1, with one documented exclusion and no second.

§6 measured five mutations over 1,295 cycles: removing the timeout left the machine stuck and costing one transaction per lost packet; accepting a handshake early ended transactions on evidence from other transactions; restarting only from idle produced the largest conservation gap, because one disagreement then costs several transactions; removing the out-of-phase report was functionally free and diagnostically fatal.

And the fifth changed nothing at all — which turned out to be information about the design rather than the bench:

T_DONE and T_IDLE are behaviourally identical, so the machine carries a state the specification does not require. That is redundancy, not coverage — and worth telling apart from Chapter 10.4's guard, which was merely covered and became observable once the clause dominating it was removed.

§8 adds a distinction the earlier modules had not needed: stimulus that is illegal as a sequence is not the same as stimulus that is illegal as a packet. The second needs a broken host; the first happens the moment a packet is lost. A sequencer must be verified against arbitrary event orders, because the expected order is what happens when nothing goes wrong — and nothing going wrong is not the case a sequencer exists for.

14. What Comes Next

Everything in this chapter treated timeout as an input that simply arrived. Chapter 12.5 is where it comes from — and it is not one number.

A device must respond within a bound; a host must wait at least a bound before giving up. Those are two different numbers with an ordering requirement between them, and the whole of USB's transaction reliability rests on that ordering holding across cable lengths, hub delays and a bit rate that varies by a factor of 320 from low speed to high.

The chapter closes Module 12 by making the arithmetic explicit — in bit times, which is the only unit in which the numbers are the same at every speed.

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.