Skip to content
VLSI Mentor

USB · Module 19

Resume

The specification says drive resume for at least 20 ms; Linux drives 40 and says why — a design can be standard-compliant and still wrong for the devices it must work with.

Chapter 19.3 put a device to sleep by measuring silence. This wakes it up, and the first thing to notice is that the wakeup is not a packet.

A suspended device is not listening for tokens. It is running on 500 µA with most of itself powered down, and there is no address to send anything to. So the host does not transmit a message — it holds an electrical state on the data lines long enough that a device running on microamps cannot miss it.

1. Three Phases, and They Are Not Interchangeable

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   1. DRIVE K    the host holds the opposite of the idle state.
                 This is the wakeup itself.
   2. EOP        SE0 for two bit times — a low-speed end-of-packet.
                 This says the resume signalling has ENDED.
   3. IDLE (J)   the bus returns to idle and normal traffic resumes.

The middle phase is the one people leave out, and mutation R2 — skipping it — dies 46 657 times.

Without the EOP the device cannot tell a finished resume from one still in progress. K is a level, not an edge: a device that sees K and then sees K removed has learned nothing about whether the host is done, because the host might simply be between symbols. The EOP is an explicit, unambiguous "that was the end."

And exactly one phase is driven at a time. The three outputs are decodes of one phase register, not three independent enables — because two enables asserted at once is not a protocol error, it is a short on a real bus. Mutation R5 asserts J during the EOP and dies 9024 times against a property that counts drivers rather than comparing values.

2. Twenty Milliseconds, or Forty

The specification requires the host to drive resume for at least 20 ms, and gives no upper bound.

Linux drives it for 40, and the comment explaining why is unusually candid:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   (b) Some (many) devices actually need more than 20 ms of resume
   signalling, and while we can argue that's against the USB
   Specification, we don't have control over which devices a certification
   laboratory will be using for certification. If CertLab uses a device
   which was tested against Windows and that happens to have relaxed
   resume signalling rules, we might fall into situations where we fail
   interoperability and electrical tests.

   #define USB_RESUME_TIMEOUT  40 /* ms */

This is why the design has two parameters and not one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  parameter integer RESUME_TICKS   = 40,  // what this host drives (ms)
  parameter integer SPEC_MIN_TICKS = 20,  // the specification's floor (ms)

RESUME_TICKS is what we do. SPEC_MIN_TICKS is what we must not go below. A design that hard-codes one number cannot express the difference, and the difference is the entire chapter.

Mutation R1 drives only the specification minimum. It is spec-conformant — and it dies 71 214 times, because the testbench is checking what the design promised rather than what the standard permits.

A design can be simultaneously standard-compliant and wrong for the population it has to work with. The only way to keep that visible in RTL is to carry both numbers.

3. What a Truncated Resume Costs

A resume cut short below the floor is a protocol violation, and the design reports it.

It has to, because nothing else will. The device that fails to wake stays asleep. It does not report an error — it is not awake to report one. The host, having driven what it thought was a resume, sees a device that has stopped responding and concludes the device has gone away.

spec_violation is sticky and violations counts. Mutation R4 removes the reporting and dies 469 343 times — the largest count in Module 19 — because once the model expects a violation the design never records, every subsequent tick disagrees.

And the EOP is the tell. Whatever else happens, the end-of-packet must never be reached with K held for less than the full duration, because a device seeing a complete-looking resume will act on it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // 2. The EOP is only ever reached after a full-length K. A resume
      //    whose K was short must never reach the end-of-packet, because
      //    the device would treat a too-short wakeup as a complete one.
      if (drive_se0)
        check(k_ticks >= RT,
              "the EOP was reached without holding K for the full duration");

4. A Resume in Progress Is Not Restarted

A second start_resume arriving mid-sequence is ignored.

The reason is a livelock rather than a correctness subtlety. Restarting resets the K counter — so a host that retriggered every millisecond would drive K forever and never reach the EOP that ends it. The bus would sit in permanent resume signalling, which is indistinguishable from a stuck driver.

Mutation R3 makes a retrigger restart the count and dies 126 131 times, with the sweep in §7 supplying 71 456 retriggers inside the K phase to catch it with.

5. The Sequence, Drawn

A sequence diagram with three participants: the host, the bus, and the suspended device. The device begins suspended, drawing five hundred microamps and not listening for tokens, because a suspended device has powered down most of itself. The host decides to wake the bus. It drives the K state onto the data lines; this is the wakeup itself, and it is an electrical level rather than a packet, because there is no address to send a packet to. The host holds K for the full duration it promised, which is forty milliseconds in the Linux implementation even though the specification's floor is twenty; the comment in the kernel explains that many shipped devices need more than the legal minimum and that a certification laboratory may use one of them. The device observes the K state and begins waking. The host then drives a single-ended zero for two bit times, a low-speed end-of-packet, which tells the device that the resume signalling has ended rather than merely paused. Without it the device cannot distinguish a completed resume from one still in progress. The host then returns the bus to its idle J state and normal traffic resumes. The device, now awake, restores its full current allowance and retains the address and configuration it held before suspending, so no re-enumeration is needed.Waking a device that is not listening for packetsHostBus (D+/D−)Suspended devicesuspended: 500 µA,not listening fortokensdrive K — the wakeupis a LEVEL, not apacketK observed: beginwakinghold K: 40 msdriven, 20 ms is thefloorEOP — SE0 for twobit timesthe resume hasENDED, not pausedreturn to idle (J)full allowancerestoredaddress andconfigurationretainednormal trafficresumes
Figure 1 — a host-driven resume. The shaded message is §2: the host holds K for what it promised rather than for the specification's floor, and the two are different numbers on purpose. The end-of-packet that follows is what tells the device the resume is over.

Nothing in that diagram is a packet until the last arrow. Everything before it is a voltage held on two wires for a length of time.

6. Verilog-2005

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// usb_resume_sequencer -- waking a suspended device, and the twenty
// milliseconds that turned into forty.
//
// Chapter 19.3 put a device to sleep by measuring silence. This wakes it,
// and the waking is not a packet -- there is nothing to address a packet to,
// because a suspended device is not listening for tokens. It is an
// ELECTRICAL signal held on the data lines long enough that a device running
// on microamps cannot miss it.
//
// The sequence has three phases and they are not interchangeable:
//
//   1. DRIVE K   the host holds the opposite of the idle state on the bus.
//                This is the wakeup itself, and it must be held for a long
//                time by bus standards -- see the note below.
//   2. EOP       a low-speed end-of-packet: SE0 for two bit times, which
//                tells the device the resume signalling has ENDED and normal
//                traffic is about to start. Without it the device cannot
//                tell a finished resume from a resume still in progress.
//   3. IDLE (J)  the bus returns to its idle state and traffic resumes.
//
// TWENTY MILLISECONDS, OR FORTY
//
// The specification requires the host to drive resume for AT LEAST 20 ms and
// gives no upper bound. Linux drives it for 40, and the comment explaining
// why is worth reading in full because it is unusually candid:
//
//     (b) Some (many) devices actually need more than 20 ms of resume
//     signalling, and while we can argue that's against the USB
//     Specification, we don't have control over which devices a
//     certification laboratory will be using for certification. If CertLab
//     uses a device which was tested against Windows and that happens to
//     have relaxed resume signalling rules, we might fall into situations
//     where we fail interoperability and electrical tests.
//
//     #define USB_RESUME_TIMEOUT  40 /* ms */
//
// So RESUME_TICKS is what this host actually drives and SPEC_MIN_TICKS is
// the floor the specification sets. They are separate parameters ON PURPOSE:
// a design that hard-codes one number cannot express the difference between
// "what we do" and "what we must not go below", and this block's whole
// reason for existing is that those are different numbers.
//
// A resume cut short below SPEC_MIN_TICKS is a protocol violation, and it is
// reported rather than silently tolerated -- because the device it fails to
// wake will simply stay asleep, and nothing else in the system will say why.
module usb_resume_sequencer #(
  parameter integer RESUME_TICKS   = 40,  // what this host drives (ms)
  parameter integer SPEC_MIN_TICKS = 20,  // the specification's floor (ms)
  parameter integer EOP_TICKS      = 2    // SE0 for two bit times
) (
  input  wire        clk,
  input  wire        rst_n,

  input  wire        start_resume,   // the host decides to wake the bus
  input  wire        abort,          // something cut the sequence short

  output reg  [1:0]  phase,
  output wire        drive_k,        // the wakeup signal itself
  output wire        drive_se0,      // the end-of-packet
  output wire        drive_j,        // idle -- normal traffic may follow
  output reg  [15:0] k_ticks,        // how long K has been held

  output reg         resume_complete,// one-tick pulse: the bus is awake
  output reg         spec_violation, // sticky: a resume below the floor
  output reg  [31:0] resumes_done,
  output reg  [31:0] violations
);
  localparam [1:0] R_IDLE = 2'd0, R_K = 2'd1, R_EOP = 2'd2, R_DONE = 2'd3;

  // The three drive states are mutually exclusive by construction: they are
  // decodes of one phase register, not three independent enables. Three
  // enables would admit the one state that must never occur -- two of them
  // asserted at once, which on a real bus is a short.
  assign drive_k   = (phase == R_K);
  assign drive_se0 = (phase == R_EOP);
  assign drive_j   = (phase == R_IDLE) || (phase == R_DONE);

  reg [15:0] eop_ticks;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      phase           <= R_IDLE;
      k_ticks         <= 16'd0;
      eop_ticks       <= 16'd0;
      resume_complete <= 1'b0;
      spec_violation  <= 1'b0;
      resumes_done    <= 32'd0;
      violations      <= 32'd0;
    end else begin
      resume_complete <= 1'b0;

      case (phase)
        R_IDLE: begin
          if (start_resume && !abort) begin
            phase     <= R_K;
            k_ticks   <= 16'd0;
            eop_ticks <= 16'd0;
          end
        end

        R_K: begin
          if (abort) begin
            // Cut short. If K was held for less than the specification's
            // floor, this is a violation and it is RECORDED -- the device
            // that fails to wake will not report anything, so if this
            // block does not, nothing does.
            if (k_ticks < SPEC_MIN_TICKS[15:0]) begin
              spec_violation <= 1'b1;
              violations     <= violations + 32'd1;
            end
            phase <= R_IDLE;
          end else if (k_ticks + 16'd1 >= RESUME_TICKS[15:0]) begin
            k_ticks <= k_ticks + 16'd1;
            phase   <= R_EOP;
          end else begin
            // A start arriving mid-sequence is IGNORED. A resume already in
            // progress must not be restarted: restarting resets k_ticks, and
            // a host that retriggered every millisecond would drive K
            // forever without ever reaching the EOP that ends it.
            k_ticks <= k_ticks + 16'd1;
          end
        end

        R_EOP: begin
          if (abort) begin
            // Aborting during the EOP is not a spec violation on the K
            // duration -- K was already held long enough -- but the resume
            // did not complete, so no completion is announced.
            phase <= R_IDLE;
          end else if (eop_ticks + 16'd1 >= EOP_TICKS[15:0]) begin
            phase <= R_DONE;
          end else begin
            eop_ticks <= eop_ticks + 16'd1;
          end
        end

        R_DONE: begin
          resume_complete <= 1'b1;
          resumes_done    <= resumes_done + 32'd1;
          phase           <= R_IDLE;
        end

        default: phase <= R_IDLE;
      endcase
    end
  end
endmodule

drive_k, drive_se0 and drive_j are decodes, not registers. That is what makes §1's electrical property true by construction rather than by care — there is no sequence of events that can assert two of them, because they are three mutually exclusive comparisons against one value.

7. SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
package usb_resume_pkg;
  // The three phases of a resume, plus the one-tick completion. They are
  // not interchangeable and they are not independent enables: exactly one
  // is driven at a time, because two at once is a short on a real bus.
  typedef enum logic [1:0] {
    R_IDLE,   // driving J -- normal traffic may flow
    R_K,      // driving K -- this IS the wakeup
    R_EOP,    // driving SE0 -- the resume has ended
    R_DONE    // one tick: announce completion, return to idle
  } resume_phase_e;
endpackage

// usb_resume_sequencer_sv -- waking a suspended device, and the twenty
// milliseconds that turned into forty.
//
// Chapter 19.3 put a device to sleep by measuring silence. This wakes it,
// and the waking is not a packet -- there is nothing to address a packet to,
// because a suspended device is not listening for tokens. It is an
// ELECTRICAL signal held on the data lines long enough that a device running
// on microamps cannot miss it.
//
// The sequence has three phases and they are not interchangeable:
//
//   1. DRIVE K   the host holds the opposite of the idle state on the bus.
//                This is the wakeup itself, and it must be held for a long
//                time by bus standards -- see the note below.
//   2. EOP       a low-speed end-of-packet: SE0 for two bit times, which
//                tells the device the resume signalling has ENDED and normal
//                traffic is about to start. Without it the device cannot
//                tell a finished resume from a resume still in progress.
//   3. IDLE (J)  the bus returns to its idle state and traffic resumes.
//
// TWENTY MILLISECONDS, OR FORTY
//
// The specification requires the host to drive resume for AT LEAST 20 ms and
// gives no upper bound. Linux drives it for 40, and the comment explaining
// why is worth reading in full because it is unusually candid:
//
//     (b) Some (many) devices actually need more than 20 ms of resume
//     signalling, and while we can argue that's against the USB
//     Specification, we don't have control over which devices a
//     certification laboratory will be using for certification. If CertLab
//     uses a device which was tested against Windows and that happens to
//     have relaxed resume signalling rules, we might fall into situations
//     where we fail interoperability and electrical tests.
//
//     #define USB_RESUME_TIMEOUT  40 /* ms */
//
// So RESUME_TICKS is what this host actually drives and SPEC_MIN_TICKS is
// the floor the specification sets. They are separate parameters ON PURPOSE:
// a design that hard-codes one number cannot express the difference between
// "what we do" and "what we must not go below", and this block's whole
// reason for existing is that those are different numbers.
//
// A resume cut short below SPEC_MIN_TICKS is a protocol violation, and it is
// reported rather than silently tolerated -- because the device it fails to
// wake will simply stay asleep, and nothing else in the system will say why.
module usb_resume_sequencer_sv
  import usb_resume_pkg::*;
#(
  parameter int unsigned RESUME_TICKS   = 40,  // what this host drives (ms)
  parameter int unsigned SPEC_MIN_TICKS = 20,  // the spec's floor (ms)
  parameter int unsigned EOP_TICKS      = 2    // SE0 for two bit times
) (
  input  logic        clk,
  input  logic        rst_n,

  input  logic        start_resume,   // the host decides to wake the bus
  input  logic        abort,          // something cut the sequence short

  output resume_phase_e phase,
  output logic        drive_k,        // the wakeup signal itself
  output logic        drive_se0,      // the end-of-packet
  output logic        drive_j,        // idle -- normal traffic may follow
  output logic [15:0] k_ticks,        // how long K has been held

  output logic        resume_complete,// one-tick pulse: the bus is awake
  output logic        spec_violation, // sticky: a resume below the floor
  output logic [31:0] resumes_done,
  output logic [31:0] violations
);
  initial begin
    if (SPEC_MIN_TICKS > RESUME_TICKS)
      $fatal(1, "RESUME_TICKS=%0d is below the specification floor of %0d",
             RESUME_TICKS, SPEC_MIN_TICKS);
    if (EOP_TICKS < 1)
      $fatal(1, "an end-of-packet with no duration does not end anything");
  end

  // The three drive states are mutually exclusive by construction: they are
  // decodes of one phase register, not three independent enables. Three
  // enables would admit the one state that must never occur -- two of them
  // asserted at once, which on a real bus is a short.
  assign drive_k   = (phase == R_K);
  assign drive_se0 = (phase == R_EOP);
  assign drive_j   = (phase == R_IDLE) || (phase == R_DONE);

  logic [15:0] eop_ticks;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      phase           <= R_IDLE;
      k_ticks         <= 16'd0;
      eop_ticks       <= 16'd0;
      resume_complete <= 1'b0;
      spec_violation  <= 1'b0;
      resumes_done    <= 32'd0;
      violations      <= 32'd0;
    end else begin
      resume_complete <= 1'b0;

      case (phase)
        R_IDLE: begin
          if (start_resume && !abort) begin
            phase     <= R_K;
            k_ticks   <= 16'd0;
            eop_ticks <= 16'd0;
          end
        end

        R_K: begin
          if (abort) begin
            // Cut short. If K was held for less than the specification's
            // floor, this is a violation and it is RECORDED -- the device
            // that fails to wake will not report anything, so if this
            // block does not, nothing does.
            if (k_ticks < 16'(SPEC_MIN_TICKS)) begin
              spec_violation <= 1'b1;
              violations     <= violations + 32'd1;
            end
            phase <= R_IDLE;
          end else if (k_ticks + 16'd1 >= 16'(RESUME_TICKS)) begin
            k_ticks <= k_ticks + 16'd1;
            phase   <= R_EOP;
          end else begin
            // A start arriving mid-sequence is IGNORED. A resume already in
            // progress must not be restarted: restarting resets k_ticks, and
            // a host that retriggered every millisecond would drive K
            // forever without ever reaching the EOP that ends it.
            k_ticks <= k_ticks + 16'd1;
          end
        end

        R_EOP: begin
          if (abort) begin
            // Aborting during the EOP is not a spec violation on the K
            // duration -- K was already held long enough -- but the resume
            // did not complete, so no completion is announced.
            phase <= R_IDLE;
          end else if (eop_ticks + 16'd1 >= 16'(EOP_TICKS)) begin
            phase <= R_DONE;
          end else begin
            eop_ticks <= eop_ticks + 16'd1;
          end
        end

        R_DONE: begin
          resume_complete <= 1'b1;
          resumes_done    <= resumes_done + 32'd1;
          phase           <= R_IDLE;
        end

        default: phase <= R_IDLE;
      endcase
    end
  end
endmodule

The elaboration guard is the one worth having here: SPEC_MIN_TICKS > RESUME_TICKS is a parameterisation that says we drive less than the standard requires, which is not a configuration anyone should be able to build by accident.

8. VHDL-2008

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package usb_resume_pkg is
  -- The three phases of a resume, plus the one-tick completion. They are
  -- not interchangeable and they are not independent enables: exactly one
  -- is driven at a time, because two at once is a short on a real bus.
  type resume_phase_t is (
    R_IDLE,   -- driving J -- normal traffic may flow
    R_K,      -- driving K -- this IS the wakeup
    R_EOP,    -- driving SE0 -- the resume has ended
    R_DONE    -- one tick: announce completion, return to idle
  );
end package;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.usb_resume_pkg.all;

-- usb_resume_sequencer_vhdl -- waking a suspended device, and the twenty
-- milliseconds that turned into forty.
--
-- Chapter 19.3 put a device to sleep by measuring silence. This wakes it,
-- and the waking is not a packet -- there is nothing to address a packet to,
-- because a suspended device is not listening for tokens. It is an
-- ELECTRICAL signal held on the data lines long enough that a device running
-- on microamps cannot miss it.
--
-- The sequence has three phases and they are not interchangeable:
--
--   1. DRIVE K   the host holds the opposite of the idle state on the bus.
--   2. EOP       a low-speed end-of-packet: SE0 for two bit times, which
--                tells the device the resume signalling has ENDED.
--   3. IDLE (J)  the bus returns to its idle state and traffic resumes.
--
-- TWENTY MILLISECONDS, OR FORTY
--
-- The specification requires the host to drive resume for AT LEAST 20 ms and
-- gives no upper bound. Linux drives it for 40, and the comment explaining
-- why is worth reading in full because it is unusually candid:
--
--     (b) Some (many) devices actually need more than 20 ms of resume
--     signalling, and while we can argue that's against the USB
--     Specification, we don't have control over which devices a
--     certification laboratory will be using for certification.
--
--     #define USB_RESUME_TIMEOUT  40 /* ms */
--
-- So RESUME_TICKS is what this host actually drives and SPEC_MIN_TICKS is
-- the floor the specification sets. They are separate generics ON PURPOSE:
-- a design that hard-codes one number cannot express the difference between
-- "what we do" and "what we must not go below".
entity usb_resume_sequencer_vhdl is
  generic (
    RESUME_TICKS   : positive := 40;  -- what this host drives (ms)
    SPEC_MIN_TICKS : positive := 20;  -- the specification's floor (ms)
    EOP_TICKS      : positive := 2    -- SE0 for two bit times
  );
  port (
    clk             : in  std_logic;
    rst_n           : in  std_logic;

    start_resume    : in  std_logic;
    abort_in        : in  std_logic;

    phase           : out resume_phase_t;
    drive_k         : out std_logic;
    drive_se0       : out std_logic;
    drive_j         : out std_logic;
    k_ticks         : out unsigned(15 downto 0);

    resume_complete : out std_logic;
    spec_violation  : out std_logic;
    resumes_done    : out unsigned(31 downto 0);
    violations      : out unsigned(31 downto 0)
  );
end entity;

architecture rtl of usb_resume_sequencer_vhdl is
  signal ph_r    : resume_phase_t := R_IDLE;
  signal k_r     : unsigned(15 downto 0) := (others => '0');
  signal eop_r   : unsigned(15 downto 0) := (others => '0');
  signal done_r  : std_logic := '0';
  signal sviol_r : std_logic := '0';
  signal rcnt_r  : unsigned(31 downto 0) := (others => '0');
  signal vcnt_r  : unsigned(31 downto 0) := (others => '0');
begin
  assert SPEC_MIN_TICKS <= RESUME_TICKS
    report "RESUME_TICKS is below the specification floor" severity failure;

  -- The three drive states are mutually exclusive by construction: they are
  -- decodes of one phase signal, not three independent enables. Three
  -- enables would admit the one state that must never occur -- two of them
  -- asserted at once, which on a real bus is a short.
  drive_k   <= '1' when ph_r = R_K   else '0';
  drive_se0 <= '1' when ph_r = R_EOP else '0';
  drive_j   <= '1' when (ph_r = R_IDLE or ph_r = R_DONE) else '0';

  phase           <= ph_r;
  k_ticks         <= k_r;
  resume_complete <= done_r;
  spec_violation  <= sviol_r;
  resumes_done    <= rcnt_r;
  violations      <= vcnt_r;

  process (clk, rst_n)
  begin
    if rst_n = '0' then
      ph_r <= R_IDLE; k_r <= (others => '0'); eop_r <= (others => '0');
      done_r <= '0'; sviol_r <= '0';
      rcnt_r <= (others => '0'); vcnt_r <= (others => '0');
    elsif rising_edge(clk) then
      done_r <= '0';

      case ph_r is
        when R_IDLE =>
          if start_resume = '1' and abort_in = '0' then
            ph_r <= R_K; k_r <= (others => '0'); eop_r <= (others => '0');
          end if;

        when R_K =>
          if abort_in = '1' then
            -- Cut short. If K was held for less than the specification's
            -- floor, this is a violation and it is RECORDED -- the device
            -- that fails to wake will not report anything, so if this
            -- block does not, nothing does.
            if k_r < to_unsigned(SPEC_MIN_TICKS, 16) then
              sviol_r <= '1';
              vcnt_r  <= vcnt_r + 1;
            end if;
            ph_r <= R_IDLE;
          elsif k_r + 1 >= to_unsigned(RESUME_TICKS, 16) then
            k_r  <= k_r + 1;
            ph_r <= R_EOP;
          else
            -- A start arriving mid-sequence is IGNORED. A resume already in
            -- progress must not be restarted: restarting resets k_ticks, and
            -- a host that retriggered every millisecond would drive K
            -- forever without ever reaching the EOP that ends it.
            k_r <= k_r + 1;
          end if;

        when R_EOP =>
          if abort_in = '1' then
            -- Aborting during the EOP is not a spec violation on the K
            -- duration -- K was already held long enough -- but the resume
            -- did not complete, so no completion is announced.
            ph_r <= R_IDLE;
          elsif eop_r + 1 >= to_unsigned(EOP_TICKS, 16) then
            ph_r <= R_DONE;
          else
            eop_r <= eop_r + 1;
          end if;

        when R_DONE =>
          done_r <= '1';
          rcnt_r <= rcnt_r + 1;
          ph_r   <= R_IDLE;
      end case;
    end if;
  end process;
end architecture;

abort_in rather than abort — VHDL reserves abort in no standard sense, but the surrounding if … then syntax makes a bare abort read as a statement. A rename forced by readability rather than by the language.

And the case ph_r is over an enumerated type needs no when others: the four states are the whole type, so VHDL rejects an incomplete case at analysis time. The Verilog needs a default because [1:0] has four values and names four — but a two-bit register with three named states would silently latch on the fourth.

9. The Waveform

K, then the end-of-packet, then idle — and never two at once

10 cycles
A waveform of the resume sequencer over ten ticks, with the K duration reduced from forty to four and the end-of-packet from two bit times to two ticks so that an entire resume fits in one figure. At tick zero the sequencer is idle and driving the J state. At tick one the host asserts start resume, still while idle and driving J. From tick two through tick five the sequencer is in the K phase and driving K: the K tick counter reads zero, one, two and three across those four ticks. At tick six the full K duration has been reached, the counter reads four, and the sequencer has moved to the end-of-packet phase, driving a single-ended zero. It holds that at tick seven as well, for the two ticks the end-of-packet requires. At tick eight the sequencer has moved to the done phase and is driving J again. At tick nine the completion pulse fires for exactly one tick and the sequencer returns to idle. Throughout all ten ticks exactly one of the three drive signals is asserted at any instant, which matters because two drivers asserted together would be an electrical short on a real bus rather than merely a protocol error.host starts the resumehost starts the resumeK — the wakeup, held for its full durationK — the wakeup, held forits full durationEOP: the resume has ENDEDEOP: the resume has ENDEDcompletion, exactly one tickcompletion, exactly onetickclkstartphaseIDLEIDLEKKKKEOPEOPDONEIDLEdrive_kdrive_se0drive_jk_ticks0001234444resume_donet0t1t2t3t4t5t6t7t8t9
Figure 2 — ten ticks from the simulator, with the K duration reduced to 4 and the EOP to 2 so a whole resume fits. Exactly one drive signal is asserted at every tick; that is the property, and it holds by construction rather than by checking.

Add the three drive rows at any tick and the answer is always 1. That is §1's electrical property, and it is worth seeing as a picture because the failure it prevents — two drivers fighting — does not look like a protocol bug in a trace. It looks like a damaged transceiver.

10. The Testbench: Every Start-and-Abort Interleaving

The sequencer has two asynchronous inputs — wake the bus and something interrupted it — and the interesting behaviour is entirely in how they interleave.

So the sweep enumerates every interleaving. Every combination of start and abort events across an 8-tick window: 2⁸ start patterns × 2⁸ abort patterns = 65 536 scenarios, 524 288 ticks.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // Every combination of start and abort events over an 8-tick window:
    // 2^8 start patterns x 2^8 abort patterns = 65536 scenarios, 524288
    // ticks. This covers every possible interleaving of "wake the bus" and
    // "something interrupted it", including every retrigger mid-sequence
    // and every abort at every point in the sequence.
    for (sp=0; sp<(1<<W); sp=sp+1)
     for (ap=0; ap<(1<<W); ap=ap+1) begin
       hard_reset;
       for (t=0; t<W; t=t+1) tick(sp[t], ap[t]);
       n_scen = n_scen + 1;
     end

Three properties are checked against no model:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // ---- SAFETY PROPERTIES, independent of the model ----
      // 1. THE electrical one: never two drive states at once. On a real
      //    bus that is a short, not a protocol error.
      check((drive_k + drive_se0 + drive_j) == 1,
            "two drive states asserted at once: that is a bus short");
      // 2. The EOP is only ever reached after a full-length K. A resume
      //    whose K was short must never reach the end-of-packet, because
      //    the device would treat a too-short wakeup as a complete one.
      if (drive_se0)
        check(k_ticks >= RT,
              "the EOP was reached without holding K for the full duration");
      // 3. And therefore never below the specification's floor either.
      if (drive_se0)
        check(k_ticks >= SMIN,
              "the EOP was reached with K held below the spec minimum");

Property 1 counts drivers rather than comparing them to expected values — which is the right shape for a constraint whose violation is electrical rather than logical.

Measured reach:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  exhaustive start/abort pattern sweep: 65536 of 65536 scenarios verified

  Verilog / SystemVerilog:
  REACH: scenarios=65536 completions=749 violations=205
         aborts-in-K=71248 retriggers-in-K=71456
  [Verilog] usb_resume_sequencer: 0 errors — PASS

  VHDL:
  REACH: scenarios=65536 completions=775 violations=173
         aborts-in-K=71219 retriggers-in-K=71508
  [VHDL] usb_resume_sequencer_vhdl: 0 errors — PASS

11. The Fifth Check to Accuse Correct Hardware

The directed section failed on first run — three errors — and the design was right again.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    for (t=0; t<EOPT-1; t=t+1) tick(1'b0, 1'b0);
    check(drive_j, "and the EOP is followed by idle");      // FAILED

EOPT ticks are spent in the EOP phase, not EOPT-1: the first tick enters it and the last leaves it. After EOPT-1 ticks the sequencer was still driving SE0 — exactly as it should have been — and the check asserted the bus was already idle.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // EOPT ticks are spent IN the EOP phase -- the first enters it and the
    // last leaves it. An earlier version of this loop ran EOPT-1 and then
    // asserted the bus was idle, which failed against a correct design:
    // the sequencer was still driving SE0, exactly as it should have been.
    for (t=0; t<EOPT; t=t+1) tick(1'b0, 1'b0);

The exhaustive sweep had already passed all 65 536 scenarios while this was broken, because the sweep never asserts where the sequencer should be after a hand-counted number of ticks — it compares against a model that counts the same way the design does.

This is the fifth testbench check in Modules 18 and 19 to fire against correct hardware, after 18.2 §12 (a missing qualifier), 18.2 §14 (a missing exception), 18.3 §10 (the wrong instant), and 18.5 §13 (the wrong one of three rules). All five were hand-written directed checks; none was a model comparison. Directed checks encode a human's arithmetic, and that is exactly what makes them worth having and what makes them wrong.

12. Mutation Testing — Across All Three Languages

MutationVerilogSystemVerilogVHDL
R1drive only the specification minimum, not what was promised712147121471545
R2the EOP is skipped entirely466574665746415
R3a start mid-K restarts the sequence126131126131126730
R4a truncated resume is not reported469343469343469585
R5J is driven during the EOP — two drivers at once902490249102
R6the EOP completes in one tick regardless of EOP_TICKS299562995629945
R7the completion becomes a level rather than a pulse611616116161230

R4 is the largest count in Module 19 at 469 343, and the reason is structural rather than semantic: spec_violation is sticky, so once the model expects it and the design never sets it, every subsequent tick of the run disagrees. A sticky output makes its own mutation loud.

R5 is the smallest at 9024 and is the only one whose failure is electrical. It is reached only during the EOP phase, which is two ticks out of every eight-tick resume — rare, and caught every single time, because the property that catches it is checked unconditionally rather than only when something looks wrong.

R1 is the chapter. It produces a host that is standard-compliant and fails the devices §2 exists to accommodate — and it is caught only because the design carries both numbers and the testbench checks against the promise rather than the standard.

13. A UVM Environment for an Interleaving Problem

The value here is generating the interleavings a directed test would not think of, and §10 is the measurement of how many there are.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class resume_event_item extends uvm_sequence_item;
  `uvm_object_utils(resume_event_item)
  rand bit start_resume;
  rand bit abort;

  // Aborts are rare in the field and central to this block, so the
  // generator over-weights them relative to reality. Realistic rates would
  // reach section 12's R4 and R5 a handful of times per run.
  constraint c_rates {
    start_resume dist { 1 := 1, 0 := 5 };
    abort        dist { 1 := 1, 0 := 9 };
  }
endclass

// Aborting at EVERY point in the sequence, deliberately, one point per
// iteration -- the directed complement to the random interleaving above.
class abort_walk_seq extends uvm_sequence #(resume_event_item);
  `uvm_object_utils(abort_walk_seq)
  rand int unsigned abort_at;
  constraint c_at { abort_at inside {[0:RESUME_TICKS+EOP_TICKS+1]}; }

  task body();
    resume_event_item it;
    `uvm_do_with(it, { start_resume == 1; abort == 0; })
    for (int i = 0; i < abort_at; i++)
      `uvm_do_with(it, { start_resume == 0; abort == 0; })
    // THE point: whether this abort is a spec violation depends entirely on
    // how far K had got, and that is the one thing the design must get
    // right about aborts.
    `uvm_do_with(it, { start_resume == 0; abort == 1; })
  endtask
endclass

class resume_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(resume_scoreboard)

  function void write(resume_txn t);
    // THE electrical property. Counted, not compared -- its violation is a
    // short, and there is no "expected value" that makes two drivers OK.
    int unsigned drivers = t.drive_k + t.drive_se0 + t.drive_j;
    if (drivers != 1)
      `uvm_fatal("RESUME/SHORT", $sformatf(
        "%0d drive signals asserted at once", drivers))

    // A device seeing the EOP treats the resume as complete. It must
    // therefore never follow a K that was too short.
    if (t.drive_se0 && t.k_ticks < RESUME_TICKS)
      `uvm_error("RESUME/SHORT_K", $sformatf(
        "EOP after only %0d ticks of K (need %0d)", t.k_ticks, RESUME_TICKS))

    // And the promise, which is stricter than the standard.
    if (t.drive_se0 && t.k_ticks < SPEC_MIN_TICKS)
      `uvm_error("RESUME/SUBSPEC", "K held below the specification floor")
  endfunction
endclass

covergroup resume_cg with function sample(
    int unsigned phase, int unsigned k_at_abort, bit retrigger);
  // Where in the sequence the abort landed. The bins either side of the
  // specification floor are the ones that decide whether it was a
  // violation, and they are what section 12's R4 lives in.
  cp_abort_point : coverpoint k_at_abort {
    bins below_floor = {[0:SPEC_MIN_TICKS-1]};
    bins at_floor    = {SPEC_MIN_TICKS};
    bins above_floor = {[SPEC_MIN_TICKS+1:RESUME_TICKS]};
  }
  // A start arriving mid-sequence. Section 4's livelock lives here.
  cp_retrigger : coverpoint retrigger { bins mid_sequence = {1}; }
  cp_phase : coverpoint phase;
  x_abort_phase : cross cp_abort_point, cp_phase, cp_retrigger;
endgroup

14. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // THE electrical property: exactly one driver, always.
  property p_one_driver;
    @(posedge clk) disable iff (!rst_n)
      $countones({drive_k, drive_se0, drive_j}) == 1;
  endproperty
  a_one_driver : assert property (p_one_driver)
    else $fatal(1, "two drive states asserted at once: bus short");

  // The EOP never follows a short K. Mutations R1 and R2 together.
  property p_eop_after_full_k;
    @(posedge clk) disable iff (!rst_n)
      drive_se0 |-> (k_ticks >= RESUME_TICKS);
  endproperty
  a_eop_after_full_k : assert property (p_eop_after_full_k);

  // A resume in progress is never restarted. Mutation R3.
  property p_no_restart;
    @(posedge clk) disable iff (!rst_n)
      (drive_k && start_resume && !abort) |=> (k_ticks == $past(k_ticks) + 1);
  endproperty
  a_no_restart : assert property (p_no_restart)
    else $error("a start mid-K restarted the sequence");

  // The completion is a pulse. Mutation R7.
  property p_completion_is_a_pulse;
    @(posedge clk) disable iff (!rst_n)
      resume_complete |=> !resume_complete;
  endproperty
  a_completion_is_a_pulse : assert property (p_completion_is_a_pulse);

p_one_driver is the property to prove formally: it is a combinational relation over three outputs of a four-state machine, so a prover settles it exhaustively and, unlike simulation, settles it for every parameterisation.

These were written but not simulated; Icarus supports no concurrent assertions.

15. Debugging: the Device That Wakes On One Machine and Not Another

The report: a device resumes correctly from suspend on one host and not on another. Same device, same cable, same OS version. On the failing host it stays asleep and is eventually reported as disconnected.

The procedure:

1. Measure the K duration on both hosts. This is a scope measurement, not a software one — the resume is an electrical level, and nothing in the software stack reports how long it was held.

2. Compare against 20 ms and against 40. A host driving 21 ms is standard-compliant. A device needing 30 will not wake, and neither party is reporting an error: the host drove a legal resume, the device never saw enough of one.

3. Recognise that this is §2 from the other side. Linux's 40 ms exists because this failure is common enough to affect certification. A host that drives the minimum is the failing host here, and it is the compliant one.

4. Check whether the EOP is present. A resume with no end-of-packet (mutation R2) leaves the device unable to tell the signalling ended. Some devices tolerate it; the ones that do not look identical to the duration failure from software.

5. Distinguish from a device that never armed. Chapter 19.5 covers device-initiated wakeup, which fails differently: there the device never signals at all, rather than signalling and being ignored.

16. Common Misconceptions

"Resume is a packet." It is an electrical level held on the data lines (§1) — a suspended device is not listening for packets.

"The K signalling is the whole resume." The EOP is what tells the device the signalling ended (§1). Mutation R2, 46 657 errors.

"20 ms is the resume duration." It is the minimum (§2). Linux drives 40, because many shipped devices need more than the legal floor.

"Driving the specification minimum is correct." It is compliant and it fails devices — and can fail certification (§2, §15). Mutation R1, 71 214 errors.

"A host can retrigger a resume to make it longer." A retrigger is ignored; making it restart creates a livelock in which the EOP is never reached (§4). Mutation R3, 126 131 errors.

"An aborted resume is harmless — just retry." The device that failed to wake reports nothing, so an unreported truncation is invisible (§3). Mutation R4, 469 343 errors.

"Two drive enables asserted together is a protocol error." It is a short (§1). Mutation R5 — and uvm_fatal, not uvm_error.

17. Exercises

1. §2 carries two parameters where most designs carry one. Identify every other place in Module 19 where "what we do" and "what we must not go below" are different numbers, and say which of them the RTL distinguishes.

2. §10 sweeps 4⁸ interleavings of two inputs over 8 ticks. Compute the window needed to cover every distinct behaviour of a RESUME_TICKS = N, EOP_TICKS = M sequencer, and the scenario count that implies.

3. Mutation R5 dies 9024 times, the fewest here, and is the only electrical failure. Explain why a low count and a severe consequence are uncorrelated, using R4 as the contrast.

4. Write the SVA property that catches R6 — an EOP that completes in one tick — without referring to EOP_TICKS.

5. §11 lists five directed checks across two modules that accused correct hardware, and notes none was a model comparison. Characterise what directed checks encode that model comparisons do not, and say why that makes them both valuable and error-prone.

6. A host drives resume for 40 ms to a device that needs 20. Determine what that costs, and whether any device is harmed by the longer signal.

18. Summary

A resume is not a packet (§1). It is an electrical level held long enough that a device running on 500 µA cannot miss it, followed by an end-of-packet that says the signalling has ended rather than paused, followed by idle. Exactly one driver is asserted at a time, and that holds by construction because the three outputs are decodes of one register.

The specification says at least 20 ms. Linux drives 40 (§2), and says plainly that many shipped devices need more than the legal minimum and that a certification laboratory may hand you one. So the design carries two numbers — what it drives and what it must not go below — because a design with one cannot express the difference.

Mutation R1 drives the specification minimum, is fully compliant, and dies 71 214 times against a testbench checking the promise rather than the standard. A design can be standard-compliant and wrong for the population it must work with.

A truncated resume is reported (§3) because nothing else will — the device that fails to wake is not awake to complain. Mutation R4, 469 343 errors, the largest in Module 19.

A resume in progress is not restarted (§4), or a host retriggering every millisecond would drive K forever and never reach the EOP that ends it.

All three HDL implementations were simulated (§19) and seven mutations died in all three (§12), with the sequencer verified over every one of 65 536 start-and-abort interleavings — 2⁸ × 2⁸ patterns, 524 288 ticks, supplying 71 248 aborts and 71 456 retriggers inside the K phase (§10).

And the directed section accused correct hardware for the fifth time in two modules (§11), miscounting the EOP by one tick. All five such failures were hand-written directed checks; none was a model comparison — which is precisely what directed checks are for and why they go wrong.

19. Tooling, Honestly

LanguageDesignTestbenchAnalysed / compiledSimulatedMutations
Verilog-2005usb_resume_sequencerre_v_tb.v✅ Icarus -g2005✅ 0 errors, 65536/65536✅ all seven
SystemVerilogusb_resume_sequencer_svre_sv_tb.sv✅ Icarus -g2012✅ 0 errors, 65536/65536✅ all seven
VHDL-2008usb_resume_sequencer_vhdlre_vhdl_tb.vhd✅ nvc 1.23.0✅ 0 errors, 65536/65536✅ all seven
UVM (§13)——❌ no UVM-capable simulator here❌—
SVA (§14)——❌ unsupported by Icarus❌—

The durations in simulation are 4, 3 and 2 ticks against defaults of 40, 20 and 2 ms, so a whole resume fits inside the swept window. The design refers to each number exactly once, which is what makes that substitution safe.

VHDL's randomised tail differs (775 completions against 749, 173 violations against 205) because the three benches draw from different generators. The 65 536 exhaustive scenarios are identical by construction, and every mutation count agrees to within 1 %.

20. What Comes Next

This chapter's resume was host-driven throughout. The host decided to wake the bus, and everything followed from that decision.

Chapter 19.5 — Remote Wakeup inverts it, and in doing so breaks the rule the entire track has rested on: a suspended device may drive the bus unasked, and the host will wake up.

Chapter 2.6 established that only the host initiates. Chapter 18.3 built an entire polled reporting mechanism because of it. Remote wakeup is the one exception, and what it costs to have an exception to a single-master rule — in hardware, in standing current, and in the number of things that can go wrong — is the next chapter.

The fences around it are worth previewing: a wake event does nothing unless the device was explicitly armed by the host and the bus is actually suspended, and those two conditions fail in completely different ways.

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.