Skip to content
VLSI Mentor

USB · Module 19

Remote Wakeup

The one exception to USB's single-master rule — a suspended device may drive the bus unasked, behind two independent fences that fail in completely different ways.

Every chapter of this track has rested on one fact: the host initiates everything.

Chapter 2.6 established it. Chapter 18.3 built an entire polled reporting mechanism because of it — a hub with news has to wait to be asked. Chapter 19.3's suspend detector exists because a device cannot even be told to sleep; it has to work it out by measuring silence.

Remote wakeup is the exception. A suspended device may drive the bus unasked, and the host will wake up.

1. What an Exception to a Single-Master Rule Costs

Allowing one device to speak first on a bus designed around exactly one initiator is not a small concession, and the specification fences it accordingly.

A wake event does nothing at all unless both of these hold:

FenceConditionWhat it prevents
armedthe host issued SetFeature(DEVICE_REMOTE_WAKEUP)a device waking a machine its owner put to sleep
suspendedthe bus is actually asleepK driven into a live transaction

Neither is redundant. A device may be armed on a running bus, and a device may be suspended having never been armed. Only the conjunction licenses driving the bus.

And the two refusals are different bugs. "The device tried to wake the host and was never armed" is a firmware defect. "The device tried to wake a bus that was already awake" is a state-tracking defect. A design that reports one number for both tells a debugger nothing, which is why the design carries two counters and mutation W6 — conflating them — dies 100 483 times.

2. Arming Does Not Survive a Bus Reset

SetFeature(DEVICE_REMOTE_WAKEUP) is feature selector 1 in Linux's ch9.h:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   #define USB_DEVICE_SELF_POWERED     0   /* (read only) */
   #define USB_DEVICE_REMOTE_WAKEUP    1   /* dev may initiate wakeup */

A bus reset clears it. The device returns to its default state and the arming goes with it, so the host must re-issue SetFeature after every reset.

That is not bookkeeping — it is consent. A device that kept the bit across a reset would be armed without the current host ever having agreed to it. And 19.3 §3 measured what arming costs: five times the suspended current, 2.5 mA instead of 500 µA, on every device that has it.

Mutation W3 keeps the arming across a reset and dies 132 595 times.

3. Two Places a Bus Reset Had To Be Made To Win

The design did not start with a bus reset outranking the wakeup, and the safety property found both gaps — one at a time.

First gap: a reset did not abort a drive in progress. A device that was legitimately armed and suspended, already driving K, kept driving it straight through a bus reset. A reset is the host taking the bus back, and a device driving K through one is fighting the host for the line.

Fixing that exposed the second: a reset arriving in the same cycle as a wake event still let the wakeup start. The device would come out of the reset already driving.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
          // A bus reset outranks the wake event as well as an in-progress
          // drive. Gating only the drive was not enough: a reset arriving in
          // the SAME cycle as a wake event would still let the wakeup start,
          // and the device would come out of the reset already driving.
          if (wake_event && !bus_reset) begin

Both are 18.2 §3's precedence rule in its device-side form: physical reality outranks anything the device was in the middle of. Mutations W4 and W5 are the two gaps, and they die 67 507 and 116 405 times.

Finding the second gap only after fixing the first is the normal shape of this work. The property was right both times; what changed was how much of the design it could see once the louder violation stopped masking the quieter one.

4. Once Started, the K Is Not Retracted

A wake event that disappears mid-signal does not stop the drive.

The host has already begun responding. Chapter 19.4 built the sequence it runs — a K, an end-of-packet, a return to idle — and a device that stopped driving partway leaves a resume half-finished on a bus with nobody else to complete it.

So the K runs for its programmed duration, and only a bus reset cuts it short.

The duration itself has a floor and a ceiling — 1 ms and 15 ms — and the design carries all three numbers: what it drives, and the two bounds it must stay inside. Mutation W7 drives the ceiling instead of the programmed duration: legal, five times longer than promised, and it dies 177 735 times.

5. The Sequence, Drawn

A sequence diagram with three participants: the host, the bus, and the device. First the host arms the device by issuing a SetFeature control transfer with the device remote wakeup selector, which is feature number one; the device records that it is armed. This step is essential, because a device that was never armed must stay silent no matter what happens to it. The host then stops transmitting, and after three milliseconds of continuous silence the device suspends itself, as built in chapter nineteen point three, cutting its draw to two and a half milliamps rather than five hundred microamps because being armed for wakeup costs standing current. Some time later the device has a reason to wake: a keypress, an arriving packet, a sensor reading. Because it is both armed and suspended, it drives the K state onto the bus unasked. This is the single exception to the rule that only the host initiates, and it is the only arrow in the entire track that the host did not solicit. The device holds K for its programmed duration, which lies between the specification's floor of one millisecond and its ceiling of fifteen. The host observes the signalling, wakes, and then runs its own resume sequence from chapter nineteen point four, driving K itself, then an end-of-packet, then returning the bus to idle. Normal traffic follows and the device retains the address and configuration it held before suspending. Finally the diagram notes that a bus reset at any point disarms the device and aborts any drive in progress, so the host must arm it again.The one arrow the host did not ask forHostBus (D+/D−)DeviceSetFeature(DEVICE_REMOTE_WAKEUP)— selector 1ARMED — and now costs 2.5 mA suspended, not 500 µAARMED — and nowcosts 2.5 mAsuspended, not 500…host goes quiet3 ms continuoussilence → SUSPENDED(19.3)a reason to wake:keypress, packet,sensordrives K UNASKED —the one exceptionhost observes thesignalling and wakeshost runs its ownresume sequence(19.4)K, then EOP, thenidleaddress andconfigurationretaineda bus reset disarmsand aborts any drive
Figure 1 — a device waking the host. The shaded messages are §1's two fences: the host must have armed the device, and the bus must actually be suspended. The arrow from device to bus is the only one in this entire track that the host did not ask for.

Count the arrows out of Device that the host did not solicit. There is exactly one, and this chapter is the fence around it.

6. Verilog-2005

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// usb_remote_wakeup -- the one exception to the single-master rule, and the
// two independent fences around it.
//
// Every chapter of this track has rested on one fact: THE HOST INITIATES
// EVERYTHING. Chapter 2.6 established it, chapter 18.3 built an entire
// polled reporting mechanism because of it, and chapter 19.3's suspend
// detector exists because a device cannot even be TOLD to sleep -- it has to
// work it out by measuring silence.
//
// Remote wakeup is the exception. A suspended device may drive the bus
// unasked, and the host will wake up.
//
// That is a large thing to allow on a single-master bus, and it is fenced
// accordingly. A wake event does nothing at all unless BOTH of these hold:
//
//   1. THE DEVICE IS ARMED. The host must have issued
//      SetFeature(DEVICE_REMOTE_WAKEUP) -- feature selector 1 in Linux's
//      ch9.h -- and a device that was never armed must stay silent no
//      matter what happens to it. This is not a formality: an unarmed
//      device that wakes the bus wakes a machine its owner deliberately
//      put to sleep, and does it from inside a closed bag.
//
//   2. THE BUS IS SUSPENDED. Driving K on a running bus is not a wakeup,
//      it is corruption -- it collides with whatever transaction the host
//      was conducting. There is nothing to wake, so there is nothing to do.
//
// The two refusals are DIFFERENT and both are counted, because "the device
// tried to wake the host and was not armed" and "the device tried to wake a
// bus that was already awake" are different bugs with different fixes.
//
// ARMING DOES NOT SURVIVE A BUS RESET
//
// A bus reset returns the device to its default state, and the arming goes
// with it. The host must re-issue SetFeature after every reset. A device
// that kept the bit across a reset would be armed without the current host
// ever having agreed to it -- and chapter 19.3 measured what arming costs:
// five times the suspended current, on every device that has it.
module usb_remote_wakeup #(
  parameter integer K_HOLD_TICKS = 5,   // how long this device drives K
  parameter integer K_MIN_TICKS  = 1,   // the specification's floor
  parameter integer K_MAX_TICKS  = 15   // the specification's ceiling
) (
  input  wire        clk,
  input  wire        rst_n,

  // --- the host, through a control transfer ---
  input  wire        set_feature_rw,    // SetFeature(DEVICE_REMOTE_WAKEUP)
  input  wire        clear_feature_rw,
  input  wire        bus_reset,

  // --- the device's own state ---
  input  wire        suspended,         // from chapter 19.3
  input  wire        wake_event,        // a keypress, a packet, a sensor

  output reg         rw_enabled,        // the armed bit -- GetStatus bit 1
  output wire        drive_k,           // the device driving the bus UNASKED
  output reg  [15:0] k_ticks,
  output wire        wakeup_active,
  output wire [1:0]  wake_outcome,      // what happened to this attempt

  output reg  [31:0] wakeups_issued,
  output reg  [31:0] refused_not_armed,
  output reg  [31:0] refused_not_suspended
);
  localparam [1:0] W_IDLE = 2'd0, W_DRIVE = 2'd1;

  reg [1:0] state;

  assign drive_k       = (state == W_DRIVE);
  assign wakeup_active = (state == W_DRIVE);

  // What this cycle's wake attempt came to. A combinational decode of the
  // same conditions the sequential block uses -- not a second opinion.
  // Verilog-2005 has no enumerated type, so the encoding is localparams:
  // chapters 19.2 and 19.3 both measured what happens when one language's
  // design exposes less than the others, and the answer both times was that
  // the tri-HDL comparison stopped measuring the designs.
  localparam [1:0] WK_ISSUED          = 2'd0,
                   WK_REFUSED_UNARMED = 2'd1,
                   WK_REFUSED_AWAKE   = 2'd2,
                   WK_NONE            = 2'd3;
  assign wake_outcome = (!wake_event || bus_reset) ? WK_NONE
                      : (state != W_IDLE)         ? WK_NONE
                      : (!rw_enabled)             ? WK_REFUSED_UNARMED
                      : (!suspended)              ? WK_REFUSED_AWAKE
                                                  : WK_ISSUED;

  // THE GATE is the pair of conditions below, tested SEPARATELY rather than
  // as one conjunction. A combined `rw_enabled && suspended` would be
  // shorter and would lose the thing that matters: "not armed" and "the bus
  // is awake" are different refusals with different fixes, and a design that
  // reports one number for both tells a debugger nothing.

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      rw_enabled            <= 1'b0;
      state                 <= W_IDLE;
      k_ticks               <= 16'd0;
      wakeups_issued        <= 32'd0;
      refused_not_armed     <= 32'd0;
      refused_not_suspended <= 32'd0;
    end else begin
      // ---- the armed bit ----
      if (bus_reset) begin
        // A reset returns the device to its default state, and the arming
        // goes with it. The host must arm again.
        rw_enabled <= 1'b0;
      end else if (clear_feature_rw) begin
        rw_enabled <= 1'b0;
      end else if (set_feature_rw) begin
        rw_enabled <= 1'b1;
      end

      // ---- the wakeup itself ----
      case (state)
        W_IDLE: begin
          // A bus reset outranks the wake event as well as an in-progress
          // drive. Gating only the drive was not enough: a reset arriving in
          // the SAME cycle as a wake event would still let the wakeup start,
          // and the device would come out of the reset already driving.
          if (wake_event && !bus_reset) begin
            if (!rw_enabled)
              // Not armed. The device stays silent, and the attempt is
              // RECORDED -- a device trying to wake a host it was never
              // permitted to wake is a defect, and an unrecorded refusal
              // looks exactly like an event that never happened.
              refused_not_armed <= refused_not_armed + 32'd1;
            else if (!suspended)
              // Armed, but the bus is awake. Nothing to wake.
              refused_not_suspended <= refused_not_suspended + 32'd1;
            else begin
              state          <= W_DRIVE;
              k_ticks        <= 16'd0;
              wakeups_issued <= wakeups_issued + 32'd1;
            end
          end
        end

        W_DRIVE: begin
          // A BUS RESET ABORTS THE DRIVE, immediately and unconditionally.
          // A reset is the host taking the bus back, and a device driving K
          // through one is fighting the host for the line. This is chapter
          // 18.2's precedence rule in its device-side form: physical
          // reality outranks anything the device was in the middle of.
          if (bus_reset) begin
            state   <= W_IDLE;
            k_ticks <= 16'd0;
          end
          // Otherwise the K is driven for its full duration. A wake event
          // that disappears mid-signal does NOT retract the wakeup: the
          // host has already begun responding, and a device that stopped
          // driving early would leave the resume half-finished.
          else if (k_ticks + 16'd1 >= K_HOLD_TICKS[15:0]) begin
            k_ticks <= k_ticks + 16'd1;
            state   <= W_IDLE;
          end else begin
            k_ticks <= k_ticks + 16'd1;
          end
        end

        default: state <= W_IDLE;
      endcase
    end
  end
endmodule

The two refusal conditions are tested separately rather than as one rw_enabled && suspended. The combined form is shorter and loses the only thing a debugger needs — which is why an earlier draft that declared exactly that wire had to delete it: it was never used, and using it would have cost the distinction.

7. SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
package usb_wakeup_pkg;
  // Why a wake attempt did or did not reach the bus. Three outcomes, and
  // collapsing the two refusals into one "refused" loses the only thing a
  // debugger needs: an unarmed device is a firmware bug, and a device
  // signalling on a running bus is a state-tracking bug.
  typedef enum logic [1:0] {
    WK_ISSUED,          // armed and suspended: the bus was driven
    WK_REFUSED_UNARMED, // the host never armed this device
    WK_REFUSED_AWAKE,   // armed, but there was nothing to wake
    WK_NONE             // no wake event this cycle
  } wake_outcome_e;
endpackage

// usb_remote_wakeup_sv -- the one exception to the single-master rule, and the
// two independent fences around it.
//
// Every chapter of this track has rested on one fact: THE HOST INITIATES
// EVERYTHING. Chapter 2.6 established it, chapter 18.3 built an entire
// polled reporting mechanism because of it, and chapter 19.3's suspend
// detector exists because a device cannot even be TOLD to sleep -- it has to
// work it out by measuring silence.
//
// Remote wakeup is the exception. A suspended device may drive the bus
// unasked, and the host will wake up.
//
// That is a large thing to allow on a single-master bus, and it is fenced
// accordingly. A wake event does nothing at all unless BOTH of these hold:
//
//   1. THE DEVICE IS ARMED. The host must have issued
//      SetFeature(DEVICE_REMOTE_WAKEUP) -- feature selector 1 in Linux's
//      ch9.h -- and a device that was never armed must stay silent no
//      matter what happens to it. This is not a formality: an unarmed
//      device that wakes the bus wakes a machine its owner deliberately
//      put to sleep, and does it from inside a closed bag.
//
//   2. THE BUS IS SUSPENDED. Driving K on a running bus is not a wakeup,
//      it is corruption -- it collides with whatever transaction the host
//      was conducting. There is nothing to wake, so there is nothing to do.
//
// The two refusals are DIFFERENT and both are counted, because "the device
// tried to wake the host and was not armed" and "the device tried to wake a
// bus that was already awake" are different bugs with different fixes.
//
// ARMING DOES NOT SURVIVE A BUS RESET
//
// A bus reset returns the device to its default state, and the arming goes
// with it. The host must re-issue SetFeature after every reset. A device
// that kept the bit across a reset would be armed without the current host
// ever having agreed to it -- and chapter 19.3 measured what arming costs:
// five times the suspended current, on every device that has it.
module usb_remote_wakeup_sv
  import usb_wakeup_pkg::*;
#(
  parameter int unsigned K_HOLD_TICKS = 5,  // how long this device drives K
  parameter int unsigned K_MIN_TICKS  = 1,  // the specification's floor
  parameter int unsigned K_MAX_TICKS  = 15  // the specification's ceiling
) (
  input  logic       clk,
  input  logic       rst_n,

  // --- the host, through a control transfer ---
  input  logic       set_feature_rw,    // SetFeature(DEVICE_REMOTE_WAKEUP)
  input  logic       clear_feature_rw,
  input  logic       bus_reset,

  // --- the device's own state ---
  input  logic       suspended,         // from chapter 19.3
  input  logic       wake_event,        // a keypress, a packet, a sensor

  output logic        rw_enabled,       // the armed bit -- GetStatus bit 1
  output logic        drive_k,          // the device driving the bus UNASKED
  output logic [15:0] k_ticks,
  output logic        wakeup_active,
  output wake_outcome_e wake_outcome,   // what happened to this attempt

  output logic [31:0] wakeups_issued,
  output logic [31:0] refused_not_armed,
  output logic [31:0] refused_not_suspended
);
  initial begin
    if (K_HOLD_TICKS < K_MIN_TICKS || K_HOLD_TICKS > K_MAX_TICKS)
      $fatal(1, "K_HOLD_TICKS=%0d is outside the permitted range %0d..%0d",
             K_HOLD_TICKS, K_MIN_TICKS, K_MAX_TICKS);
  end

  localparam logic [1:0] W_IDLE = 2'd0, W_DRIVE = 2'd1;

  logic [1:0] state;

  // What this cycle's wake attempt came to. A combinational decode of the
  // same conditions the sequential block uses -- not a second opinion.
  always_comb begin
    if      (!wake_event || bus_reset) wake_outcome = WK_NONE;
    else if (state != W_IDLE)          wake_outcome = WK_NONE;
    else if (!rw_enabled)              wake_outcome = WK_REFUSED_UNARMED;
    else if (!suspended)               wake_outcome = WK_REFUSED_AWAKE;
    else                               wake_outcome = WK_ISSUED;
  end

  assign drive_k       = (state == W_DRIVE);
  assign wakeup_active = (state == W_DRIVE);

  // THE GATE is the pair of conditions below, tested SEPARATELY rather than
  // as one conjunction. A combined `rw_enabled && suspended` would be
  // shorter and would lose the thing that matters: "not armed" and "the bus
  // is awake" are different refusals with different fixes, and a design that
  // reports one number for both tells a debugger nothing.

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      rw_enabled            <= 1'b0;
      state                 <= W_IDLE;
      k_ticks               <= 16'd0;
      wakeups_issued        <= '0;
      refused_not_armed     <= '0;
      refused_not_suspended <= '0;
    end else begin
      // ---- the armed bit ----
      if (bus_reset) begin
        // A reset returns the device to its default state, and the arming
        // goes with it. The host must arm again.
        rw_enabled <= 1'b0;
      end else if (clear_feature_rw) begin
        rw_enabled <= 1'b0;
      end else if (set_feature_rw) begin
        rw_enabled <= 1'b1;
      end

      // ---- the wakeup itself ----
      case (state)
        W_IDLE: begin
          // A bus reset outranks the wake event as well as an in-progress
          // drive. Gating only the drive was not enough: a reset arriving in
          // the SAME cycle as a wake event would still let the wakeup start,
          // and the device would come out of the reset already driving.
          if (wake_event && !bus_reset) begin
            if (!rw_enabled)
              // Not armed. The device stays silent, and the attempt is
              // RECORDED -- a device trying to wake a host it was never
              // permitted to wake is a defect, and an unrecorded refusal
              // looks exactly like an event that never happened.
              refused_not_armed <= refused_not_armed + 1;
            else if (!suspended)
              // Armed, but the bus is awake. Nothing to wake.
              refused_not_suspended <= refused_not_suspended + 1;
            else begin
              state          <= W_DRIVE;
              k_ticks        <= 16'd0;
              wakeups_issued <= wakeups_issued + 1;
            end
          end
        end

        W_DRIVE: begin
          // A BUS RESET ABORTS THE DRIVE, immediately and unconditionally.
          // A reset is the host taking the bus back, and a device driving K
          // through one is fighting the host for the line. This is chapter
          // 18.2's precedence rule in its device-side form: physical
          // reality outranks anything the device was in the middle of.
          if (bus_reset) begin
            state   <= W_IDLE;
            k_ticks <= 16'd0;
          end
          // Otherwise the K is driven for its full duration. A wake event
          // that disappears mid-signal does NOT retract the wakeup: the
          // host has already begun responding, and a device that stopped
          // driving early would leave the resume half-finished.
          else if (k_ticks + 16'd1 >= 16'(K_HOLD_TICKS)) begin
            k_ticks <= k_ticks + 16'd1;
            state   <= W_IDLE;
          end else begin
            k_ticks <= k_ticks + 16'd1;
          end
        end

        default: state <= W_IDLE;
      endcase
    end
  end
endmodule

wake_outcome_e names the four cases, and WK_NONE is deliberately one of them: no wake event this cycle is not the same as a wake event that was refused, and a design with three states would conflate them.

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_wakeup_pkg is
  -- Why a wake attempt did or did not reach the bus. Three outcomes, and
  -- collapsing the two refusals into one "refused" loses the only thing a
  -- debugger needs: an unarmed device is a firmware bug, and a device
  -- signalling on a running bus is a state-tracking bug.
  type wake_outcome_t is (
    WK_ISSUED,          -- armed and suspended: the bus was driven
    WK_REFUSED_UNARMED, -- the host never armed this device
    WK_REFUSED_AWAKE,   -- armed, but there was nothing to wake
    WK_NONE             -- no wake event this cycle
  );
end package;

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

-- usb_remote_wakeup_vhdl -- the one exception to the single-master rule, and
-- the two independent fences around it.
--
-- Every chapter of this track has rested on one fact: THE HOST INITIATES
-- EVERYTHING. Chapter 2.6 established it, chapter 18.3 built an entire
-- polled reporting mechanism because of it, and chapter 19.3's suspend
-- detector exists because a device cannot even be TOLD to sleep.
--
-- Remote wakeup is the exception. A suspended device may drive the bus
-- unasked, and the host will wake up.
--
-- That is a large thing to allow on a single-master bus, and it is fenced
-- accordingly. A wake event does nothing at all unless BOTH of these hold:
--
--   1. THE DEVICE IS ARMED. The host must have issued
--      SetFeature(DEVICE_REMOTE_WAKEUP) -- feature selector 1 in Linux's
--      ch9.h -- and a device that was never armed must stay silent. An
--      unarmed device that wakes the bus wakes a machine its owner
--      deliberately put to sleep, from inside a closed bag.
--
--   2. THE BUS IS SUSPENDED. Driving K on a running bus is not a wakeup,
--      it is corruption -- it collides with whatever transaction the host
--      was conducting.
--
-- The two refusals are DIFFERENT and both are counted, because "not armed"
-- and "the bus was already awake" are different bugs with different fixes.
--
-- ARMING DOES NOT SURVIVE A BUS RESET, and a bus reset outranks both an
-- in-progress drive and a wake event arriving in the same cycle.
entity usb_remote_wakeup_vhdl is
  generic (
    K_HOLD_TICKS : positive := 5;   -- how long this device drives K
    K_MIN_TICKS  : positive := 1;   -- the specification's floor
    K_MAX_TICKS  : positive := 15   -- the specification's ceiling
  );
  port (
    clk                   : in  std_logic;
    rst_n                 : in  std_logic;

    set_feature_rw        : in  std_logic;
    clear_feature_rw      : in  std_logic;
    bus_reset             : in  std_logic;

    suspended             : in  std_logic;
    wake_event            : in  std_logic;

    rw_enabled            : out std_logic;
    drive_k               : out std_logic;
    k_ticks               : out unsigned(15 downto 0);
    wakeup_active         : out std_logic;
    wake_outcome          : out wake_outcome_t;

    wakeups_issued        : out unsigned(31 downto 0);
    refused_not_armed     : out unsigned(31 downto 0);
    refused_not_suspended : out unsigned(31 downto 0)
  );
end entity;

architecture rtl of usb_remote_wakeup_vhdl is
  type wk_state_t is (W_IDLE, W_DRIVE);

  signal st_r    : wk_state_t := W_IDLE;
  signal arm_r   : std_logic := '0';
  signal k_r     : unsigned(15 downto 0) := (others => '0');
  signal iss_r   : unsigned(31 downto 0) := (others => '0');
  signal rna_r   : unsigned(31 downto 0) := (others => '0');
  signal rns_r   : unsigned(31 downto 0) := (others => '0');
begin
  assert K_HOLD_TICKS >= K_MIN_TICKS and K_HOLD_TICKS <= K_MAX_TICKS
    report "K_HOLD_TICKS is outside the permitted range" severity failure;

  drive_k       <= '1' when st_r = W_DRIVE else '0';
  wakeup_active <= '1' when st_r = W_DRIVE else '0';
  rw_enabled    <= arm_r;
  k_ticks       <= k_r;

  wakeups_issued        <= iss_r;
  refused_not_armed     <= rna_r;
  refused_not_suspended <= rns_r;

  -- What this cycle's wake attempt came to. A combinational decode of the
  -- same conditions the sequential process uses -- not a second opinion.
  wake_outcome <= WK_NONE            when (wake_event = '0'
                                           or bus_reset = '1') else
                  WK_NONE            when st_r /= W_IDLE       else
                  WK_REFUSED_UNARMED when arm_r = '0'          else
                  WK_REFUSED_AWAKE   when suspended = '0'      else
                  WK_ISSUED;

  process (clk, rst_n)
  begin
    if rst_n = '0' then
      arm_r <= '0'; st_r <= W_IDLE; k_r <= (others => '0');
      iss_r <= (others => '0'); rna_r <= (others => '0');
      rns_r <= (others => '0');
    elsif rising_edge(clk) then
      -- ---- the armed bit ----
      if bus_reset = '1' then
        -- A reset returns the device to its default state, and the arming
        -- goes with it. The host must arm again.
        arm_r <= '0';
      elsif clear_feature_rw = '1' then
        arm_r <= '0';
      elsif set_feature_rw = '1' then
        arm_r <= '1';
      end if;

      -- ---- the wakeup itself ----
      case st_r is
        when W_IDLE =>
          -- A bus reset outranks the wake event as well as an in-progress
          -- drive. Gating only the drive is not enough: a reset arriving in
          -- the SAME cycle as a wake event would still let the wakeup start,
          -- and the device would come out of the reset already driving.
          if wake_event = '1' and bus_reset = '0' then
            if arm_r = '0' then
              -- Not armed. The device stays silent, and the attempt is
              -- RECORDED -- an unrecorded refusal looks exactly like an
              -- event that never happened.
              rna_r <= rna_r + 1;
            elsif suspended = '0' then
              -- Armed, but the bus is awake. Nothing to wake.
              rns_r <= rns_r + 1;
            else
              st_r  <= W_DRIVE;
              k_r   <= (others => '0');
              iss_r <= iss_r + 1;
            end if;
          end if;

        when W_DRIVE =>
          -- A BUS RESET ABORTS THE DRIVE, immediately and unconditionally.
          -- A reset is the host taking the bus back, and a device driving K
          -- through one is fighting the host for the line.
          if bus_reset = '1' then
            st_r <= W_IDLE; k_r <= (others => '0');
          -- Otherwise the K is driven for its full duration. A wake event
          -- that disappears mid-signal does NOT retract the wakeup: the
          -- host has already begun responding, and a device that stopped
          -- driving early would leave the resume half-finished.
          elsif k_r + 1 >= to_unsigned(K_HOLD_TICKS, 16) then
            k_r  <= k_r + 1;
            st_r <= W_IDLE;
          else
            k_r <= k_r + 1;
          end if;
      end case;
    end if;
  end process;
end architecture;

type wk_state_t is (W_IDLE, W_DRIVE); is declared in the architecture, not the package — it is internal, nothing outside needs it, and VHDL lets that distinction be expressed. The Verilog's localparam encoding is visible to anything that includes the file.

9. The Waveform

The same wake event, three different outcomes

10 cycles
A waveform of the remote wakeup block over ten ticks, with the K hold duration reduced from five to three so the whole story fits. At tick zero the bus is suspended and a wake event arrives, but the device has never been armed; the outcome output reads refused-unarmed and the device drives nothing. At tick one the host issues SetFeature with the remote wakeup selector, and the unarmed refusal counter has incremented to one. At tick two the device is armed, but the bus is awake rather than suspended, and a wake event arrives; the outcome reads refused-awake, a different refusal for a different reason, and the device again drives nothing. At tick three the bus suspends and the awake-refusal counter reads one. At tick four the device is both armed and suspended and a wake event arrives; the outcome reads issued. From tick five through tick seven the device drives the K state onto the bus unasked, with its K tick counter reading zero, one and two, and the issued counter now reading one. At tick eight a bus reset arrives: the drive stops immediately and the device is disarmed. At tick nine an identical wake event on a suspended bus is refused once more for being unarmed, because the arming did not survive the reset.unarmed → refusedunarmed → refusedarmed but bus awake → refusedarmed but bus awake →refusedarmed AND suspended → issuedarmed AND suspended →issuedbus reset: drive aborted, disarmedbus reset: drive aborted,disarmedclkset_featurebus_resetsuspendedwake_eventrw_enableddrive_koutcomeUNARM—AWAKE—ISSUE————UNARMt0t1t2t3t4t5t6t7t8t9
Figure 2 — ten ticks from the simulator, with the K duration reduced to 3. All three outcomes appear: refused for being unarmed, refused because the bus was awake, and issued. Cycle 8's bus reset disarms the device, so cycle 9's identical wake event is refused again.

Ticks 0, 2 and 4 carry an identical wake_event and produce three different answers. That is §1 in one picture: the event is never the thing that decides.

And ticks 8 and 9 are §2. The reset disarms, so an event that would have been issued at tick 4 is refused at tick 9 — same device, same bus state, same event.

10. The Testbench: 384 Transitions and 4096 Interleavings

Two exhaustive sweeps, because the block has two different kinds of domain.

The first is over state and input, jointly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // 12 distinct positions (armed or not, idle or partway through K) x all
    // 32 input combinations = 384 transitions: the entire one-step domain.
    for (pos=0; pos<12; pos=pos+1)
     for (ic=0; ic<32; ic=ic+1) begin
       goto(pos);
       tick(ic[0], ic[1], ic[2], ic[3], ic[4]);
       n_trans = n_trans + 1;
     end

Twelve positions — armed or not, crossed with idle or partway through a K of each possible length — against all 32 combinations of the five control inputs. That is every one-step transition the design has.

The second is temporal, over the two inputs that §1's gate is built from:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // Every interleaving of "the device has a reason to wake" and "the bus
    // is suspended" across a 6-tick window: 2^6 x 2^6 = 4096 scenarios.
    // These are the two conditions section 2's gate is built from, and the
    // whole point is that neither alone is sufficient.

Four properties are checked against no model, and the first is stated more carefully than it first was:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // 1. THE one that matters, stated precisely. A wakeup must never
      //    START unless the device was armed -- that is the property that
      //    stops a device waking a machine its owner put to sleep. Note it
      //    is about the START: a device that was legitimately armed and is
      //    then disarmed MID-SIGNAL keeps driving, because the host has
      //    already begun responding and a half-finished resume is worse
      //    than a completed one.

Measured reach:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  exhaustive transition sweep: 384 of 384 transitions verified
  exhaustive wake/suspend interleaving sweep: 4096 of 4096 verified

  Verilog / SystemVerilog:
  REACH: transitions=384 interleavings=4096 issued=5597
         refused-unarmed=3397 refused-awake=4313 driving-ticks=21395
  [Verilog] usb_remote_wakeup: 0 errors — PASS

  VHDL:
  REACH: transitions=384 interleavings=4096 issued=5568
         refused-unarmed=3321 refused-awake=4345 driving-ticks=21316
  [VHDL] usb_remote_wakeup_vhdl: 0 errors — PASS

All three outcomes are reached thousands of times each, which is what makes the matrix below meaningful: 5597 issued, 3397 refused for being unarmed, 4313 refused because the bus was awake.

11. Mutation Testing — Across All Three Languages

MutationVerilogSystemVerilogVHDL
W1the arming fence is gone — an unarmed device wakes the host146900146900144884
W2the suspended fence is gone — K driven onto a running bus170744170744170288
W3a bus reset does not disarm132595132595130272
W4a bus reset does not abort a drive in progress675076750762572
W5a bus reset does not block the start of a wakeup116405116405114036
W6the two refusals are conflated into one counter100483100483100555
W7K is driven for the ceiling, not the programmed duration177735177735178876

W4 and W5 are §3's two gaps, now permanent tests. They score differently — 67 507 against 116 405 — because they are reached differently: a reset during a drive needs the drive to be in progress, which is rarer than a reset coinciding with a wake event.

W7 scores highest at 177 735 and is the most benign: driving K for 15 ms instead of 5 is legal. It wastes time and standing current and wakes the host perfectly well. The largest number in the table belongs to the mutation with the mildest consequence, which is the same inversion 19.2 §10 found.

Every mutation here is reached by the exhaustive sweeps rather than by luck, and none needed a stimulus change — the second chapter in Module 19 where that is true, and for the same reason: the sweeps enumerate an interaction rather than a single decision.

12. A UVM Environment for an Exception

The interesting stimulus here is adversarial: a device that tries to wake the host at every moment it is not supposed to.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The attack sequence. Its job is to try a wakeup at every point where the
// device is NOT entitled to one -- which is exactly what a firmware bug
// does by accident, and exactly what a malicious device would do on purpose.
class unarmed_wake_attack_seq extends uvm_sequence #(wakeup_item);
  `uvm_object_utils(unarmed_wake_attack_seq)

  task body();
    wakeup_item it;
    // Never arm. Then try to wake from every bus state there is.
    foreach_state: for (int s = 0; s < 4; s++) begin
      `uvm_do_with(it, { set_feature_rw == 0; clear_feature_rw == 0;
                         suspended == s[0]; bus_reset == s[1];
                         wake_event == 1; })
    end
  endtask
endclass

// The RACE sequence: a bus reset arriving in the same cycle as a wake
// event. Section 3 found this by hand; here it is deliberate.
class reset_wake_race_seq extends uvm_sequence #(wakeup_item);
  `uvm_object_utils(reset_wake_race_seq)
  rand int unsigned offset;      // where in the K the reset lands
  constraint c_off { offset inside {[0:K_HOLD_TICKS]}; }

  task body();
    wakeup_item it;
    `uvm_do_with(it, { set_feature_rw == 1; })                 // arm
    `uvm_do_with(it, { suspended == 1; wake_event == 1; })     // start a K
    repeat (offset)
      `uvm_do_with(it, { suspended == 1; wake_event == 0; })
    // THE race. offset == 0 is the same-cycle case that section 3's second
    // gap lived in; every other offset is the in-progress case of the first.
    `uvm_do_with(it, { bus_reset == 1; wake_event == 1; })
  endtask
endclass

class wakeup_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(wakeup_scoreboard)
  local bit m_armed, m_was_driving;

  function void write(wakeup_txn t);
    // THE property, and the severity is deliberate: a device that wakes a
    // host it was never permitted to wake is not a protocol error with a
    // retry path. It is a machine coming out of a bag hot.
    if (!m_was_driving && t.drive_k && !m_armed)
      `uvm_fatal("WAKE/UNARMED",
        "a device that was never armed drove the bus")

    // A bus reset outranks everything the device was doing.
    if (t.bus_reset && t.drive_k)
      `uvm_error("WAKE/RESET",
        "the device kept driving K through a bus reset")

    // The two refusals stay distinguishable.
    if (t.wake_event && !t.drive_k) begin
      if (!m_armed && t.outcome != WK_REFUSED_UNARMED)
        `uvm_error("WAKE/REASON", "an unarmed refusal reported as something else")
      if (m_armed && !t.suspended && t.outcome != WK_REFUSED_AWAKE)
        `uvm_error("WAKE/REASON", "an awake-bus refusal reported as something else")
    end

    if (t.bus_reset || t.clear_feature_rw) m_armed = 0;
    else if (t.set_feature_rw)             m_armed = 1;
    m_was_driving = t.drive_k;
  endfunction
endclass

covergroup wakeup_cg with function sample(
    bit armed, bit suspended, bit wake, bit reset, bit driving);
  // The 2x2 that section 1 is entirely about. Three of these four must
  // produce nothing, and a run that misses any of them has not tested the
  // gate -- it has tested one side of it.
  x_fences : cross
    coverpoint armed     { bins no = {0}; bins yes = {1}; },
    coverpoint suspended { bins no = {0}; bins yes = {1}; }
    iff (wake);

  // Section 3's two gaps, as bins. cp_reset_start is the same-cycle race
  // and cp_reset_mid is the reset landing inside a drive.
  cp_reset_start : coverpoint {reset, wake, driving} { bins race = {3'b110}; }
  cp_reset_mid   : coverpoint {reset, driving}       { bins mid  = {2'b11}; }
endgroup

13. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // THE property: a wakeup never STARTS unarmed. On the edge, because that
  // is the only instant where "was it allowed to begin?" is meaningful.
  property p_no_unarmed_start;
    @(posedge clk) disable iff (!rst_n)
      $rose(drive_k) |-> $past(rw_enabled);
  endproperty
  a_no_unarmed_start : assert property (p_no_unarmed_start)
    else $fatal(1, "a device that was never armed drove the bus");

  // And never on a bus that is awake.
  property p_no_start_while_awake;
    @(posedge clk) disable iff (!rst_n)
      $rose(drive_k) |-> $past(suspended);
  endproperty
  a_no_start_while_awake : assert property (p_no_start_while_awake);

  // A bus reset outranks both the drive and the start. Mutations W4 and W5.
  property p_reset_wins;
    @(posedge clk) disable iff (!rst_n)
      bus_reset |=> !drive_k;
  endproperty
  a_reset_wins : assert property (p_reset_wins)
    else $error("a bus reset did not stop the device driving");

  // Arming does not survive a reset. Mutation W3.
  property p_reset_disarms;
    @(posedge clk) disable iff (!rst_n)
      bus_reset |=> !rw_enabled;
  endproperty
  a_reset_disarms : assert property (p_reset_disarms);

p_reset_wins covers both of §3's gaps in one line — it does not care whether the drive was starting or continuing, only that a reset ends it. That is the property I should have written first, and writing it as two separate design conditions is what made finding them sequential rather than simultaneous.

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

14. Debugging: the Laptop That Wakes in the Bag

The report: a laptop wakes from sleep on its own, in a bag, repeatedly, and flattens its battery. It happens with one particular peripheral attached and not otherwise.

The procedure:

1. Establish that it is a wakeup and not a failure to sleep. Chapter 19.2 §14's back-powering prevents sleep; this ends one. The distinction is whether the machine ever went to sleep, and every OS logs that separately from what woke it.

2. Read what the OS says woke it. Most name the device. If a USB device is named, the question becomes whether it was entitled to.

3. Check whether the host armed it. GetStatus bit 1 reports the armed state, and OS tooling generally exposes it as a per-device wakeup-enabled flag. A device waking the host while that flag is clear is mutation W1 in the field — the device is signalling without permission.

4. Distinguish it from a device that was armed and is over-eager. An armed device waking on spurious input is a sensitivity problem in the device's own wake logic. An unarmed device waking at all is a conformance failure, and the fixes are completely different: one is tuning, the other is a firmware bug.

5. Confirm the disarm-across-reset behaviour. A device that stays armed across a bus reset (mutation W3) will be armed after a reconnect the host never re-armed it through — which presents exactly as "it only does this after it has been unplugged and replugged."

6. The workaround is real and worth knowing. Clearing the wakeup-enabled flag for that device is ClearFeature(DEVICE_REMOTE_WAKEUP), and it is exposed by most systems. It disables a legitimate capability to work around a device that misuses it, which is the right trade until the firmware is fixed.

15. Common Misconceptions

"USB devices can interrupt the host." One mechanism can, under two conditions, after explicit permission (§1). Everything else in the protocol is polled.

"Remote wakeup is always available." The host must arm it with SetFeature(DEVICE_REMOTE_WAKEUP) (§1, §2). Mutation W1, 146 900 errors.

"Arming is a one-time configuration." It is cleared by every bus reset (§2). Mutation W3, 132 595 errors.

"Enabling remote wakeup is free." It costs five times the suspended current on every armed device (19.3 §3) — 2.5 mA instead of 500 µA.

"A device can signal a wakeup whenever it has something to report." Only while the bus is suspended (§1). On a running bus it is a collision. Mutation W2, 170 744 errors.

"A wake event disappearing should stop the signalling." The host has already begun responding; stopping leaves a half-finished resume (§4).

"A bus reset only needs to clear the armed bit." It must also abort a drive in progress and block a start in the same cycle (§3). Mutations W4 and W5.

"One 'wake refused' counter is enough." Unarmed and bus-awake are different bugs with different fixes (§1). Mutation W6, 100 483 errors.

16. Exercises

1. §10 found a property that was slightly too strong and hid two defects. Construct the general rule for when "X must never happen" should be rewritten as "X must never start", and apply it to the properties in 19.2 §13.

2. §3's two gaps were found sequentially. Determine whether a single property — bus_reset |=> !drive_k — would have found both at once, and say why the design was written as two conditions rather than one.

3. W7 drives K for 15 ms instead of 5 and scores highest in the matrix. Compute the extra energy that costs on a device suspended at 2.5 mA, and say whether it is ever observable.

4. Write the SVA property that catches W6 — conflated refusal counters — without referring to either counter by name.

5. A hub has four armed devices below it. Using 18.5's budget and 19.3's suspend currents, determine whether a bus-powered hub can support them all in suspend, and what happens if it cannot.

6. The Linux kernel exposes usb_wakeup_enabled_descendants, which counts armed devices at or below a node. Determine what a hub must do differently when that count is non-zero, and which chapter of this module the answer belongs to.

17. Summary

Remote wakeup is the single exception to USB's single-master rule (§1), and it is fenced by two independent conditions: the host must have armed the device, and the bus must actually be suspended. Neither alone is sufficient, and the two refusals are different bugs that the design reports separately (W6, 100 483 errors).

An unarmed device that wakes the host wakes a machine its owner deliberately put to sleep (§1) — the failure behind every laptop that comes out of a bag hot. Mutation W1, 146 900 errors, and uvm_fatal rather than uvm_error in §12.

Arming does not survive a bus reset (§2). It is consent, not bookkeeping: a device that kept the bit would be armed without the current host having agreed — and arming costs five times the suspended current on every device that has it.

A bus reset had to be made to win in two separate places (§3): aborting a drive in progress, and blocking a wakeup that would otherwise start in the same cycle. The second was found only after the first was fixed, because the louder violation was masking it.

All three HDL implementations were simulated (§18) and seven mutations died in all three (§11), verified over 384 one-step transitions — twelve positions against all 32 input combinations — and 4096 interleavings of the two conditions the gate is built from (§10).

The central property was initially too strong (§10). "An unarmed device never drives the bus" fired on a device that was legitimately armed when it started and disarmed mid-signal, which §4 says is correct. Rewriting it to check the start is what let it see the two real gaps underneath. A property that is slightly too strong does not merely produce noise — it hides what is beneath it.

And the largest count belongs to the mildest mutation again (§11): W7 drives K for the specification's ceiling instead of the programmed duration, which is legal, wasteful, and works.

18. Tooling, Honestly

LanguageDesignTestbenchAnalysed / compiledSimulatedMutations
Verilog-2005usb_remote_wakeuprw_v_tb.v✅ Icarus -g2005✅ 0 errors, 384 + 4096✅ all seven
SystemVerilogusb_remote_wakeup_svrw_sv_tb.sv✅ Icarus -g2012✅ 0 errors, 384 + 4096✅ all seven
VHDL-2008usb_remote_wakeup_vhdlrw_vhdl_tb.vhd✅ nvc 1.23.0✅ 0 errors, 384 + 4096✅ all seven
UVM (§12)——❌ no UVM-capable simulator here❌—
SVA (§13)——❌ unsupported by Icarus❌—

The wake_outcome output was added to all three designs at once, rather than to SystemVerilog and VHDL first and Verilog later. Chapters 19.2 §11 and 19.3 §13 each cost a round of mutation re-measurement to discover that one design exposing less than the others makes the tri-HDL comparison measure the testbenches. Doing it up front here is that lesson applied rather than repeated.

VHDL's randomised tail differs (5568 issued against 5597) because the three benches draw from different generators. The 384 transitions and 4096 interleavings are identical by construction.

19. What Comes Next

Five chapters have been about one device, or one port, or one link.

Chapter 19.6 — Power Budgeting is about all of them at once. The host walks its entire tree, subtracts each device's declared draw from the hub above it, and decides what it can still afford — and the arithmetic has a term this module has not needed yet: a device that is enumerated but not configured still costs one unit load, because it is entitled to one whether it is using it or not.

Linux does this walk in a loop with a saturating floor and a warning, and the edge cases are exactly where 19.1's per-device rule and 18.5's per-hub rule meet. Where the two disagree is the last thing this module has to settle.

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.