Skip to content
VLSI Mentor

USB · Module 19

Bus Power

A device's current allowance changes exactly once during enumeration — and bMaxPower is counted in 2 mA units, not milliamps.

Module 18 asked what a hub can supply. This module asks what a device may take, and they are not the same question with the answer written from the other side.

A hub's budget is a property of the hub. A device's allowance is a property of a negotiation — it changes during enumeration, it changes exactly once, and until it changes the device is entitled to far less than it asked for.

1. The Allowance Changes Exactly Once

A device attaches. It is not yet configured. How much current may it draw?

One unit load. 100 mA. However much it declared in its descriptor, however much the port has spare.

Then the host reads its descriptors, decides the port can supply what the device wants, and issues SetConfiguration. Now — and not before — the device may draw what it declared.

before SetConfigurationafter
Allowanceone unit loadbMaxPower, scaled
Depends on the descriptor?noyes
Depends on the port's spare capacity?noyes

And the floor is a floor, not a default. A device that never gets configured stays there permanently.

2. bMaxPower Is Not Milliamps

The descriptor field is one byte, and it counts in units, not milliamps — and the unit depends on the speed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
   unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
   return c->desc.bMaxPower * mul;

A USB 2.0 device asking for 500 mA writes 250. A SuperSpeed device asking for 900 mA writes 113 — because 900 is not a multiple of 8, so it writes 113 and gets 904, or 112 and gets 896.

Speedunitone byte expressesfull load
USB 2.02 mA0 – 510 mA500 mA (250)
SuperSpeed8 mA0 – 2040 mA900 mA (113 → 904 mA)

The 2 mA unit is why 500 mA is the USB 2.0 ceiling in practice: one byte times 2 reaches 510, so the field can express the full load and almost nothing beyond it. The scaling is not an encoding convenience — it is what sets the maximum a device can even ask for.

Mutation B2 applies the 2 mA scale to SuperSpeed too and dies 35 897 times: every SuperSpeed device would request a quarter of what it meant.

3. What "Detected But Doesn't Work" Actually Is

SetConfiguration can fail, and the failure has a very specific shape.

If the port cannot supply what the device declared, the host does not configure it. The device is enumerated, addressed, visible in the device list, and stuck at one unit load.

This is the hardware behind the most common USB support complaint there is. The device shows up. It has a name. It has a serial number. It does nothing — because a configuration that was never accepted means an allowance that was never granted, and the device is sitting at 100 mA waiting for permission that is not coming.

config_refused is a sticky output for exactly this reason. Without it, a refused configuration and a device that was never asked look identical from outside: both unconfigured, both at one unit load, both silent.

4. Losing VBUS Is Not a Transition to Negotiate

A configured device that loses VBUS does not stay configured.

This looks like defensive coding and is not. A device that retained configured across a power loss would, on the next attach, resume drawing its full allowance immediately — before the host had read a descriptor, before it had issued anything, before it knew what had just been plugged in. The one-unit-load floor of §1 would be bypassed entirely by unplugging and replugging.

Mutation B5 removes the VBUS term from the deconfigure condition and dies 7100 times.

SetConfiguration(0) does the same thing deliberately: it returns the device to the Address state, and the allowance drops back to one unit load with it (mutation B6, 1708 errors).

5. The Negotiation, Drawn

A block diagram of the bus power negotiation, arranged in four rows. At the top, VBUS appears when the device is attached to a port, and the port's hub declares how much current it offers, which comes from the hub power budget of module eighteen. In the second row on the left, the device's descriptor supplies bMaxPower, a single byte counted in two milliamp units for USB 2.0 and eight milliamp units for SuperSpeed. On the right of that row is the pre-configuration allowance, fixed at one unit load of one hundred milliamps for USB 2.0 or one hundred and fifty for SuperSpeed; this applies from attach onwards regardless of what the device declared. In the third row on the left, bMaxPower is scaled into an actual milliamp request, and in the centre of that row the host compares that scaled request against what the port offers, using an inclusive test so a request that exactly consumes the offer is legal. If the request fits, SetConfiguration succeeds and the device moves to the configured state in the bottom row, where its allowance becomes the full scaled request. If the request does not fit, SetConfiguration is refused, a sticky refusal flag is raised, and the device remains at one unit load: enumerated, addressed, visible to software, and unable to operate. Losing VBUS or a SetConfiguration of zero returns the device from the configured state back to the one unit load state.VBUS + port offerwhat the hub said it supplies (18.5)bMaxPowerone byte, in 2 mA or 8 mA unitsOne unit load100 mA / 150 mA — from attach onwardScale to mAx2, or x8 on SuperSpeedrequest ≤ offer ?inclusive — exactly consuming it islegalConfiguredallowance = the scaled requestVBUS lost / SetConfig(0)allowance returns to one unit loadRefusedstays at one unit load — unusableofferattachunitsmAfitstoo bigrevert12
Figure 1 — a device's current allowance through enumeration. The shaded path is the one that matters: the allowance is one unit load from attach until SetConfiguration succeeds, and a SetConfiguration that fails leaves the device on that same path permanently — enumerated, addressed, and unusable.

6. The Hardware, Before Any Language

Two combinational quantities and one bit of state.

requested_mA is bMaxPower scaled, widened before the shift so the product cannot wrap: 255 × 8 is 2040, which does not fit eight bits.

request_fits is inclusive — the same convention as 16.4, 17.4, 17.5 and 18.5.

allowance_mA selects between the unit load and the request on one bit: configured.

The unit load follows the speed, not the request — a SuperSpeed device gets a larger floor as well as a larger ceiling (mutation B7, 44 184 errors).

Precedence on the state bit: detach or VBUS loss outranks SetConfiguration(0), which outranks SetConfiguration. Physical first, as in 18.2 §3.

7. Verilog-2005

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// usb_bus_power_negotiate -- what a bus-powered device may draw, and when.
//
// Module 18 asked what a HUB can supply. This asks what a DEVICE may take,
// and the two are different questions with different answers at different
// times.
//
// The rule that surprises people is that a device's allowance CHANGES
// during enumeration, and it changes exactly once:
//
//   BEFORE SetConfiguration   one unit load. Always. However much the
//                             device declared it wants, however much the
//                             port has spare. The host has not yet agreed
//                             to anything, and a device that helped itself
//                             to 500 mA while merely attached could brown
//                             out a port before anyone had read a
//                             descriptor from it.
//
//   AFTER SetConfiguration    bMaxPower, scaled -- and only if the host
//                             accepted the configuration. If the port
//                             cannot supply it, SetConfiguration FAILS and
//                             the device stays at one unit load, addressed
//                             and enumerated and unusable.
//
// bMaxPower is stored in UNITS, not milliamps, and the unit depends on the
// speed: SuperSpeed counts in 8 mA, everything else in 2 mA. Linux:
//
//     /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
//     unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
//     return c->desc.bMaxPower * mul;
//
// An 8-bit field times 2 is 510 mA, which is why a USB 2.0 device can ask
// for the full 500 mA in one byte and not a milliamp more.
module usb_bus_power_negotiate #(
  parameter integer UNIT_LOAD_HS = 100,   // one unit load, USB 2.0, mA
  parameter integer UNIT_LOAD_SS = 150    // one unit load, SuperSpeed, mA
) (
  input  wire        clk,
  input  wire        rst_n,

  input  wire        vbus_present,
  input  wire        superspeed,       // scales BOTH the unit load and bMaxPower
  input  wire [7:0]  b_max_power,      // the descriptor field, in UNITS
  input  wire [15:0] port_offers_mA,   // what the hub said this port supplies

  input  wire        set_config,       // host issues SetConfiguration
  input  wire        set_config_zero,  // SetConfiguration(0) -- deconfigure
  input  wire        detach,

  output wire [15:0] unit_load_mA,
  output wire [15:0] requested_mA,     // bMaxPower scaled into milliamps
  output wire        request_fits,     // the port can supply the request
  output reg         configured,
  output wire [15:0] allowance_mA,     // what the device may draw RIGHT NOW
  output reg         config_refused,   // sticky: a SetConfiguration failed
  output reg  [31:0] refusal_count
);
  // The unit load is a property of the SPEED, not of the request. A
  // SuperSpeed device gets a bigger floor as well as a bigger ceiling.
  assign unit_load_mA = superspeed ? UNIT_LOAD_SS[15:0] : UNIT_LOAD_HS[15:0];

  // bMaxPower is in units. Widened to 16 bits BEFORE the multiply so the
  // product cannot wrap: 255 * 8 is 2040, which does not fit 8 bits and
  // does fit 16.
  assign requested_mA = superspeed ? ({8'd0, b_max_power} << 3)   // x8
                                   : ({8'd0, b_max_power} << 1);  // x2

  // Inclusive, as everywhere else in this track: a request that exactly
  // consumes what the port offers is legal.
  assign request_fits = (requested_mA <= port_offers_mA);

  // THE POINT OF THE MODULE. Before configuration the device is entitled to
  // one unit load and nothing else, whatever it declared. After a SUCCESSFUL
  // SetConfiguration it is entitled to what it declared.
  assign allowance_mA = configured ? requested_mA : unit_load_mA;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      configured     <= 1'b0;
      config_refused <= 1'b0;
      refusal_count  <= 32'd0;
    end else if (detach || !vbus_present) begin
      // Losing VBUS is not a state transition to negotiate, it is the end
      // of the conversation. A device that stayed "configured" across a
      // detach would resume drawing its full allowance on the next attach
      // before the host had agreed to anything.
      configured     <= 1'b0;
      config_refused <= 1'b0;
    end else if (set_config_zero) begin
      // SetConfiguration(0) returns the device to the Address state, and
      // with it to one unit load.
      configured     <= 1'b0;
    end else if (set_config) begin
      if (request_fits) begin
        configured     <= 1'b1;
        config_refused <= 1'b0;
      end else begin
        // The request does not fit. The device is NOT configured, and it
        // stays at one unit load -- enumerated, addressed, visible in the
        // device list, and unusable. This is what "the device is detected
        // but doesn't work" looks like from inside the hardware.
        configured     <= 1'b0;
        config_refused <= 1'b1;
        refusal_count  <= refusal_count + 32'd1;
      end
    end
  end
endmodule

allowance_mA is one line and it is the whole module. Everything else computes the two candidate values; that line chooses between them, and the choice is the specification.

8. SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
package usb_devpower_pkg;
  // What a device is entitled to draw, as a named state rather than a flag.
  // The distinction is not cosmetic: the two states differ by up to a factor
  // of five, and "configured" is the only word that licenses the larger one.
  typedef enum logic [1:0] {
    PWR_UNPOWERED,     // no VBUS -- the device may draw nothing at all
    PWR_ONE_UNIT,      // attached or addressed: ONE unit load, no more
    PWR_CONFIGURED     // SetConfiguration succeeded: bMaxPower, scaled
  } dev_power_e;
endpackage

// usb_bus_power_negotiate_sv -- what a bus-powered device may draw, and when.
//
// Module 18 asked what a HUB can supply. This asks what a DEVICE may take,
// and the two are different questions with different answers at different
// times.
//
// The rule that surprises people is that a device's allowance CHANGES
// during enumeration, and it changes exactly once:
//
//   BEFORE SetConfiguration   one unit load. Always. However much the
//                             device declared it wants, however much the
//                             port has spare. The host has not yet agreed
//                             to anything, and a device that helped itself
//                             to 500 mA while merely attached could brown
//                             out a port before anyone had read a
//                             descriptor from it.
//
//   AFTER SetConfiguration    bMaxPower, scaled -- and only if the host
//                             accepted the configuration. If the port
//                             cannot supply it, SetConfiguration FAILS and
//                             the device stays at one unit load, addressed
//                             and enumerated and unusable.
//
// bMaxPower is stored in UNITS, not milliamps, and the unit depends on the
// speed: SuperSpeed counts in 8 mA, everything else in 2 mA. Linux:
//
//     /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
//     unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
//     return c->desc.bMaxPower * mul;
module usb_bus_power_negotiate_sv
  import usb_devpower_pkg::*;
#(
  parameter int unsigned UNIT_LOAD_HS = 100,
  parameter int unsigned UNIT_LOAD_SS = 150
) (
  input  logic        clk,
  input  logic        rst_n,

  input  logic        vbus_present,
  input  logic        superspeed,
  input  logic [7:0]  b_max_power,
  input  logic [15:0] port_offers_mA,

  input  logic        set_config,
  input  logic        set_config_zero,
  input  logic        detach,

  output logic [15:0] unit_load_mA,
  output logic [15:0] requested_mA,
  output logic        request_fits,
  output logic        configured,
  output logic [15:0] allowance_mA,
  output dev_power_e  power_state,
  output logic        config_refused,
  output logic [31:0] refusal_count
);
  initial begin
    if (UNIT_LOAD_HS < 1 || UNIT_LOAD_SS < 1)
      $fatal(1, "a unit load of zero entitles a device to nothing");
    // 255 units x 8 mA is 2040, which must fit the arithmetic below.
    if (UNIT_LOAD_SS > 2040)
      $fatal(1, "UNIT_LOAD_SS=%0d exceeds what bMaxPower can express",
             UNIT_LOAD_SS);
  end

  // The unit load is a property of the SPEED, not of the request. A
  // SuperSpeed device gets a bigger floor as well as a bigger ceiling.
  assign unit_load_mA = superspeed ? 16'(UNIT_LOAD_SS) : 16'(UNIT_LOAD_HS);

  // bMaxPower is in units. Widened to 16 bits BEFORE the multiply so the
  // product cannot wrap: 255 * 8 is 2040, which does not fit 8 bits and
  // does fit 16.
  assign requested_mA = superspeed ? (16'(b_max_power) << 3)
                                   : (16'(b_max_power) << 1);

  // Inclusive, as everywhere else in this track: a request that exactly
  // consumes what the port offers is legal.
  assign request_fits = (requested_mA <= port_offers_mA);

  // THE POINT OF THE MODULE. Before configuration the device is entitled to
  // one unit load and nothing else, whatever it declared.
  assign allowance_mA = configured ? requested_mA : unit_load_mA;

  // The same fact as a named state. `configured` is a bit a reader must
  // interpret; PWR_ONE_UNIT says what the device may actually do.
  always_comb begin
    if      (!vbus_present) power_state = PWR_UNPOWERED;
    else if (configured)    power_state = PWR_CONFIGURED;
    else                    power_state = PWR_ONE_UNIT;
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      configured <= 1'b0; config_refused <= 1'b0; refusal_count <= '0;
    end else if (detach || !vbus_present) begin
      // Losing VBUS is not a state transition to negotiate, it is the end
      // of the conversation. A device that stayed "configured" across a
      // detach would resume drawing its full allowance on the next attach
      // before the host had agreed to anything.
      configured <= 1'b0; config_refused <= 1'b0;
    end else if (set_config_zero) begin
      // SetConfiguration(0) returns the device to the Address state, and
      // with it to one unit load.
      configured <= 1'b0;
    end else if (set_config) begin
      if (request_fits) begin
        configured <= 1'b1; config_refused <= 1'b0;
      end else begin
        // The request does not fit. The device is NOT configured, and it
        // stays at one unit load -- enumerated, addressed, visible in the
        // device list, and unusable. This is what "the device is detected
        // but doesn't work" looks like from inside the hardware.
        configured <= 1'b0; config_refused <= 1'b1;
        refusal_count <= refusal_count + 1;
      end
    end
  end
endmodule

dev_power_e gives the three cases names, and the middle one is the chapter: PWR_ONE_UNIT is not "not yet configured", it is a specific entitlement with a specific number. A waveform showing configured = 0 tells a reader nothing about what the device may draw; a waveform showing PWR_ONE_UNIT tells them exactly.

9. VHDL-2008

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

package usb_devpower_pkg is
  -- What a device is entitled to draw, as a named state rather than a flag.
  -- The distinction is not cosmetic: the two states differ by up to a factor
  -- of five, and "configured" is the only word that licenses the larger one.
  type dev_power_t is (
    PWR_UNPOWERED,     -- no VBUS -- the device may draw nothing at all
    PWR_ONE_UNIT,      -- attached or addressed: ONE unit load, no more
    PWR_CONFIGURED     -- SetConfiguration succeeded: bMaxPower, scaled
  );
end package;

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

-- usb_bus_power_negotiate_vhdl -- what a device may draw, and when.
--
-- Module 18 asked what a HUB can supply. This asks what a DEVICE may take,
-- and the two are different questions with different answers at different
-- times.
--
-- The rule that surprises people is that a device's allowance CHANGES
-- during enumeration, and it changes exactly once:
--
--   BEFORE SetConfiguration   one unit load. Always. However much the
--                             device declared it wants, however much the
--                             port has spare. The host has not yet agreed
--                             to anything, and a device that helped itself
--                             to 500 mA while merely attached could brown
--                             out a port before anyone had read a
--                             descriptor from it.
--
--   AFTER SetConfiguration    bMaxPower, scaled -- and only if the host
--                             accepted the configuration. If the port
--                             cannot supply it, SetConfiguration FAILS and
--                             the device stays at one unit load, addressed
--                             and enumerated and unusable.
--
-- bMaxPower is stored in UNITS, not milliamps, and the unit depends on the
-- speed: SuperSpeed counts in 8 mA, everything else in 2 mA. Linux:
--
--     /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
--     unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
--     return c->desc.bMaxPower * mul;
entity usb_bus_power_negotiate_vhdl is
  generic (
    UNIT_LOAD_HS : positive := 100;
    UNIT_LOAD_SS : positive := 150
  );
  port (
    clk             : in  std_logic;
    rst_n           : in  std_logic;

    vbus_present    : in  std_logic;
    superspeed      : in  std_logic;
    b_max_power     : in  unsigned(7 downto 0);
    port_offers_mA  : in  unsigned(15 downto 0);

    set_config      : in  std_logic;
    set_config_zero : in  std_logic;
    detach          : in  std_logic;

    unit_load_mA    : out unsigned(15 downto 0);
    requested_mA    : out unsigned(15 downto 0);
    request_fits    : out std_logic;
    configured      : out std_logic;
    allowance_mA    : out unsigned(15 downto 0);
    power_state     : out dev_power_t;
    config_refused  : out std_logic;
    refusal_count   : out unsigned(31 downto 0)
  );
end entity;

architecture rtl of usb_bus_power_negotiate_vhdl is
  signal ul_i    : unsigned(15 downto 0);
  signal req_i   : unsigned(15 downto 0);
  signal fits_i  : std_logic;
  signal cfg_r   : std_logic := '0';
  signal ref_r   : std_logic := '0';
  signal cnt_r   : unsigned(31 downto 0) := (others => '0');
begin
  assert UNIT_LOAD_SS <= 2040
    report "UNIT_LOAD_SS exceeds what bMaxPower can express"
    severity failure;

  -- The unit load is a property of the SPEED, not of the request. A
  -- SuperSpeed device gets a bigger floor as well as a bigger ceiling.
  ul_i <= to_unsigned(UNIT_LOAD_SS, 16) when superspeed = '1'
          else to_unsigned(UNIT_LOAD_HS, 16);

  -- bMaxPower is in units. Resized to 16 bits BEFORE the shift so the
  -- product cannot wrap: 255 * 8 is 2040, which does not fit 8 bits and
  -- does fit 16. Written as a shift rather than a multiply because
  -- numeric_std's "unsigned * natural" returns a result as wide as both
  -- operands together, which is a runtime length error rather than a
  -- truncation (chapter 18.4 section 9).
  req_i <= shift_left(resize(b_max_power, 16), 3) when superspeed = '1'
           else shift_left(resize(b_max_power, 16), 1);

  -- Inclusive, as everywhere else in this track: a request that exactly
  -- consumes what the port offers is legal.
  fits_i <= '1' when req_i <= port_offers_mA else '0';

  unit_load_mA   <= ul_i;
  requested_mA   <= req_i;
  request_fits   <= fits_i;
  configured     <= cfg_r;
  config_refused <= ref_r;
  refusal_count  <= cnt_r;

  -- THE POINT OF THE MODULE. Before configuration the device is entitled to
  -- one unit load and nothing else, whatever it declared.
  allowance_mA <= req_i when cfg_r = '1' else ul_i;

  -- The same fact as a named state. `configured` is a bit a reader must
  -- interpret; PWR_ONE_UNIT says what the device may actually do.
  power_state <= PWR_UNPOWERED  when vbus_present = '0' else
                 PWR_CONFIGURED when cfg_r = '1'        else
                 PWR_ONE_UNIT;

  process (clk, rst_n)
  begin
    if rst_n = '0' then
      cfg_r <= '0'; ref_r <= '0'; cnt_r <= (others => '0');
    elsif rising_edge(clk) then
      if detach = '1' or vbus_present = '0' then
        -- Losing VBUS is not a state transition to negotiate, it is the end
        -- of the conversation. A device that stayed "configured" across a
        -- detach would resume drawing its full allowance on the next attach
        -- before the host had agreed to anything.
        cfg_r <= '0'; ref_r <= '0';
      elsif set_config_zero = '1' then
        -- SetConfiguration(0) returns the device to the Address state, and
        -- with it to one unit load.
        cfg_r <= '0';
      elsif set_config = '1' then
        if fits_i = '1' then
          cfg_r <= '1'; ref_r <= '0';
        else
          -- The request does not fit. The device is NOT configured, and it
          -- stays at one unit load -- enumerated, addressed, visible in the
          -- device list, and unusable. This is what "the device is detected
          -- but doesn't work" looks like from inside the hardware.
          cfg_r <= '0'; ref_r <= '1';
          cnt_r <= cnt_r + 1;
        end if;
      end if;
    end if;
  end process;
end architecture;

shift_left(resize(...), 3) rather than * 8 is Chapter 18.4 §9's lesson applied before it could bite: numeric_std's unsigned * natural returns a result as wide as both operands together, which is a runtime length error rather than a truncation. The shift states both the intent and the width.

10. The Testbench: 5632 Scenarios, Exhaustively

The decision has three inputs: the entire bMaxPower field (256 values), the speed (2), and what the port offers.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    for (s=0; s<2; s=s+1)
     for (p=0; p<256; p=p+1)
      for (o=0; o<11; o=o+1) begin
        hard_reset;
        ss=s[0]; bmp=p[7:0]; #1;
        offers = offer_of(o, requested_mA); #1;
        check_comb;                       // unconfigured allowance
        if (offers == requested_mA) n_exact = n_exact + 1;
        step(1, 0, 0, 1);                 // the host tries SetConfiguration
        n_exh = n_exh + 1;
      end

Three safety properties are checked at every point, and none comes from the model:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // ---- SAFETY PROPERTIES, independent of the model ----
      // 1. THE one that matters: a configured device never has an allowance
      //    larger than its port offered it.
      if (configured)
        check(allowance_mA <= offers,
              "a configured device may draw more than its port supplies");
      // 2. An unconfigured device draws exactly one unit load, never more.
      if (!configured)
        check(allowance_mA === e_ul,
              "an unconfigured device's allowance is not one unit load");
      // 3. Being configured implies the request fitted.
      check(!configured || request_fits,
            "the device is configured with a request that does not fit");

And the model multiplies where the design shifts — b * m against << 1 / << 3. Same answer, different arithmetic, so a mistake in one is not automatically a mistake in the other.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  exhaustive bus-power sweep: 5632 of 5632 scenarios verified

  REACH: exhaustive=5632 configured=2860 refused=2772 offer-exactly-request=522
  [Verilog] usb_bus_power_negotiate: 0 errors — PASS
  [SystemVerilog] usb_bus_power_negotiate_sv: 0 errors — PASS
  [VHDL] usb_bus_power_negotiate_vhdl: 0 errors — PASS

11. The Boundary That Was Reached Eight Times

The first version of this sweep had 4096 points and eight fixed port offers: 0, 100, 150, 250, 500, 510, 900 and 2040 mA — chosen because each is a real boundary in the specification.

Mutation B3 — the fit test made exclusive — died 212 times in Verilog and 82 in VHDL.

B3 differs from the correct design only when the request exactly equals the offer. With bMaxPower sweeping 0–255 and eight fixed offers, that happens when bMaxPower × 2 or × 8 lands precisely on one of those eight numbers:

Speedoffers hit exactlybMaxPower
USB 2.0 (×2)0, 100, 150, 250, 500, 5100, 50, 75, 125, 250, 255
SuperSpeed (×8)0, 20400, 255

Eight points out of 4096. Everything else in the sweep was strictly above or strictly below the line, where inclusive and exclusive agree.

The fix is to compute three of the offers from the request rather than fixing them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Offer modes 8, 9 and 10 are computed FROM the request: exactly the
  // request, one milliamp under, one over. Without them the sweep lands on
  // the inclusive boundary only where a fixed offer happens to equal
  // bMaxPower x 2 -- eight times in 4096 points, which is not enough to
  // distinguish an inclusive bound from an exclusive one.
  function [15:0] offer_of(input integer mode, input [15:0] req);
    begin
      case (mode)
        8:       offer_of = req;
        9:       offer_of = (req == 16'd0) ? 16'd0 : (req - 16'd1);
        10:      offer_of = req + 16'd1;
        default: offer_of = OFF[mode];
      endcase
    end
  endfunction

Exact-boundary hits went from 8 to 522, and B3 from 212 to 2782 (VHDL: 82 → 2652).

12. Mutation Testing — Across All Three Languages

MutationVerilogSystemVerilogVHDL
B1no one-unit-load floor — the device draws what it declared522995229953179
B2SuperSpeed scaled by 2 as well358973589739602
B3the fit test becomes exclusive278227822652
B4a refused SetConfiguration configures anyway259022590228560
B5losing VBUS does not deconfigure710071007439
B6SetConfiguration(0) does not deconfigure170817081434
B7the unit load ignores the speed441844418447260

B1 is §1 as a one-line change and the largest count: it removes the floor entirely, so every unconfigured device draws whatever it felt like declaring.

B4 is the dangerous one in practice. It configures a device whose request the port cannot meet — the hardware equivalent of a host that ignores its own budget check. It dies 25 902 times, and the check that kills it is safety property 1: a configured device never has an allowance larger than its port offered it.

B6 scores lowest (1708) because SetConfiguration(0) is issued only in the randomised phase — the exhaustive sweep configures once and stops. That is a legitimate rarity rather than a hole: the mutation is reached thousands of times and dies every time.

13. A UVM Environment for Enumeration Power

The negotiation is a protocol over time — attach, read descriptors, configure, maybe fail — which is what sequences are for.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class enum_power_item extends uvm_sequence_item;
  `uvm_object_utils(enum_power_item)
  rand bit [7:0]  b_max_power;
  rand bit        superspeed;
  rand bit [15:0] port_offers_mA;

  // Bias HARD toward the boundary. Section 11 measured what uniform
  // stimulus does here: the inclusive bound is reached by coincidence,
  // eight times in four thousand.
  constraint c_boundary {
    port_offers_mA dist {
      (superspeed ? b_max_power*8 : b_max_power*2)       := 10,  // exactly
      (superspeed ? b_max_power*8 : b_max_power*2) - 1   := 5,   // just under
      (superspeed ? b_max_power*8 : b_max_power*2) + 1   := 5,   // just over
      [0:2040]                                            := 3   // anywhere
    };
  }
endclass

// The whole enumeration, as a sequence. The POINT is the window between
// attach and SetConfiguration, where the device is entitled to one unit
// load and a badly-behaved device would take more.
class enumerate_seq extends uvm_sequence #(enum_power_item);
  `uvm_object_utils(enumerate_seq)
  rand int unsigned descriptor_delay;
  constraint c_delay { descriptor_delay inside {[1:20]}; }

  task body();
    enum_power_item it;
    `uvm_do_with(it, { })                 // attach: VBUS appears
    repeat (descriptor_delay) @(posedge cfg.vif.clk);   // host reads descriptors
    // THE window. Everything above happens while the device is entitled to
    // exactly one unit load, and the scoreboard checks that continuously.
    `uvm_do_with(it, { })                 // SetConfiguration
  endtask
endclass

The scoreboard's central check is a temporal one, and it is the reason this block is worth a UVM environment rather than a directed test:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class bus_power_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(bus_power_scoreboard)
  local bit m_configured;   // its OWN copy (chapter 17.3)

  function void write(power_txn t);
    int unsigned unit_load = t.superspeed ? 150 : 100;
    int unsigned request   = t.b_max_power * (t.superspeed ? 8 : 2);

    // THE property, and it is about a WINDOW rather than an instant: from
    // attach until SetConfiguration succeeds, the allowance is one unit
    // load. A device that exceeded it even for one cycle could brown out a
    // port before the host knew what was attached.
    if (!m_configured && t.allowance_mA != unit_load)
      `uvm_error("PWR/FLOOR", $sformatf(
        "unconfigured device allowed %0d mA; one unit load is %0d",
        t.allowance_mA, unit_load))

    // A configured device never exceeds what its port offered.
    if (m_configured && t.allowance_mA > t.port_offers_mA)
      `uvm_error("PWR/OVER", $sformatf(
        "configured at %0d mA from a port offering %0d",
        t.allowance_mA, t.port_offers_mA))

    if (t.set_config) m_configured = (request <= t.port_offers_mA);
    if (t.set_config_zero || !t.vbus_present) m_configured = 0;
  endfunction
endclass

covergroup bus_power_cg with function sample(
    int unsigned request, int unsigned offer, bit ss, bit configured);
  cp_speed : coverpoint ss;
  // The bin section 11 is about. Without deriving the offer from the
  // request, this bin reads 8 out of 4096.
  cp_rel : coverpoint (request == offer ? 0 : request < offer ? 1 : 2) {
    bins exactly_fits = {0};
    bins fits         = {1};
    bins too_big      = {2};
  }
  x_speed_rel : cross cp_speed, cp_rel;
endgroup

14. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // THE property: an unconfigured device draws exactly one unit load.
  property p_unconfigured_floor;
    @(posedge clk) disable iff (!rst_n)
      (!configured) |-> (allowance_mA == unit_load_mA);
  endproperty
  a_unconfigured_floor : assert property (p_unconfigured_floor)
    else $error("an unconfigured device's allowance is not one unit load");

  // A configured device never exceeds what its port offered.
  property p_within_offer;
    @(posedge clk) disable iff (!rst_n)
      configured |-> (allowance_mA <= port_offers_mA);
  endproperty
  a_within_offer : assert property (p_within_offer);

  // Configuration only ever follows a request that fitted. Mutation B4.
  property p_config_implies_fit;
    @(posedge clk) disable iff (!rst_n)
      ($rose(configured)) |-> $past(request_fits);
  endproperty
  a_config_implies_fit : assert property (p_config_implies_fit);

  // Losing VBUS deconfigures, next cycle, unconditionally. Mutation B5.
  property p_vbus_deconfigures;
    @(posedge clk) disable iff (!rst_n)
      (!vbus_present) |=> (!configured);
  endproperty
  a_vbus_deconfigures : assert property (p_vbus_deconfigures);

p_unconfigured_floor is the one to prove formally — it is a pure combinational implication, and a prover settles it for every bMaxPower and every offer at once, including the parameterisations no testbench instantiated.

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

15. Debugging: the Device That Enumerates and Does Nothing

The report: a USB microphone appears in the device list with its correct name. It is never usable. On another machine it works. On a powered hub it works.

The procedure:

1. Check whether it is configured, not whether it is detected. Those are different states (§3), and every OS tool shows the second more prominently than the first. An enumerated, addressed, unconfigured device is the signature.

2. Read bMaxPower and remember it is in units. A device declaring 250 wants 500 mA, not 250. This is the single commonest misreading of a descriptor dump (§2).

3. Compare against what the port offers, which for a bus-powered hub is one unit load (18.5 §1). A 500 mA device on a bus-powered hub is refused with the budget completely empty — the per-port offer is the binding constraint, not the budget.

4. Explain "it works on another machine." A different host, or a root port rather than a hub, offers a full load. The device did not change and neither did its descriptor; the offer did.

5. If it fails on a powered hub too, check config_refused against the refusal count. A device that is refused repeatedly is being retried by the host; a device refused once and left alone has been given up on.

16. Common Misconceptions

"A device can draw its bMaxPower as soon as it is plugged in." One unit load until SetConfiguration succeeds (§1). Mutation B1, 52 299 errors.

"bMaxPower is in milliamps." It is in 2 mA units — 8 mA on SuperSpeed (§2). A device wanting 500 mA writes 250.

"A USB 2.0 device could ask for 1 A if it wanted." One byte × 2 mA tops out at 510 mA (§2). The encoding sets the ceiling.

"SetConfiguration always succeeds." It fails when the port cannot supply the request, and the device stays enumerated and unusable (§3).

"An unconfigured device is one the host has not got to yet." It may be one the host refused (§3) — which is why config_refused is a separate, sticky output.

"A configured device stays configured until told otherwise." Losing VBUS deconfigures it (§4), and must, or the floor could be bypassed by replugging.

"The unit load is always 100 mA." 150 mA on SuperSpeed (§2, §6). Mutation B7, 44 184 errors.

"4096 points covering the whole descriptor field is a thorough test." It reached the inclusive boundary eight times (§11).

17. Exercises

1. A SuperSpeed device needs exactly 900 mA. Determine what it must write in bMaxPower, what it will actually be granted, and whether any byte value yields exactly 900.

2. §11 derived three offers from the request. Identify every other relational comparison in this design and say which of them the fixed-offer sweep tested adequately and which it did not.

3. Mutation B6 scores lowest at 1708. Construct the smallest directed test that would kill it, and say why the exhaustive sweep does not contain one.

4. Write the SVA property that catches B2 — the wrong SuperSpeed multiplier — without referring to the constant 8.

5. Chapter 18.5 computed what a hub may supply; this chapter computes what a device may take. Determine what happens when the two disagree — a hub offering 100 mA to a device configured for 500 — and which module is responsible for preventing it.

6. The design deconfigures on !vbus_present in the same branch as detach. Determine whether they could be separated, what would have to change, and whether any mutation in §12 would notice.

18. Summary

A device's allowance changes exactly once (§1): one unit load from attach until SetConfiguration succeeds, then bMaxPower scaled. The floor exists because at attach time the host has not read a descriptor and has agreed to nothing.

bMaxPower is in units, not milliamps (§2) — 2 mA normally, 8 mA on SuperSpeed — and one byte times 2 is why 510 mA is the USB 2.0 ceiling.

SetConfiguration can fail (§3), and the result is a device that is enumerated, addressed, listed, and unusable. That is the hardware behind "it's detected but it doesn't work."

Losing VBUS deconfigures (§4), and must: otherwise the one-unit-load floor could be bypassed by unplugging and replugging.

All three HDL implementations were simulated (§19) and seven mutations died in all three (§12), with the negotiation verified exhaustively over all 5632 attach-and-configure scenarios (§10) — the entire 256-value descriptor field, both speeds, eleven offers.

And the inclusive boundary was reached eight times in the first 4096-point sweep (§11). Eight fixed offers, each a real specification boundary, and bMaxPower × 2 landed on one of them only by coincidence. Deriving three offers from the request took exact hits from 8 to 522 and mutation B3 from 212 to 2782.

To test a boundary you must land on it, and fixed stimulus values land on a relational boundary only by accident — 18.5 §12's lesson in a different disguise.

19. Tooling, Honestly

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

The three languages report identical reach (5632 / 2860 / 2772 / 522) because the exhaustive sweep is deterministic; only the randomised tail differs, and it is checked rather than counted.

VHDL's counts run consistently higher than the other two on B1, B2, B4, B5 and B7 — by roughly 5–10 % — because its randomised phase draws from a different generator and reaches the configured state slightly more often. The exhaustive portion is identical by construction.

20. What Comes Next

This chapter assumed the device takes its power from the bus. Many do not.

A printer, a powered hub, a desktop drive — each has its own supply and draws nothing meaningful from VBUS. Their configuration descriptor says so, with a bit Linux names USB_CONFIG_ATT_SELFPOWER, and the host's budget arithmetic skips them entirely.

Chapter 19.2 — Self Power is about those devices, and the interesting thing is what they still owe the bus. A self-powered device must keep watching VBUS even though it does not draw from it — and a device that ignores this does something genuinely destructive: it drives its pull-up, and current, back into a host that has deliberately removed power.

That failure has a name, a mechanism, and a one-line fix in RTL, and it is where the next chapter starts.

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.