USB · Module 19
Self Power
A self-powered device draws nothing from VBUS and must still watch it — a pull-up driven with VBUS absent pushes current back into a host that deliberately removed power.
Chapter 19.1 built a device that takes its power from the bus. Many devices do not. A printer, a powered hub, a desktop drive — each has its own supply, and the host's budget arithmetic skips them entirely.
They say so with one bit in the configuration descriptor. And the interesting question is not what that bit buys them. It is what they still owe the bus.
1. The Bit, and What It Means for the Host
bmAttributes bit 6 — Linux calls it USB_CONFIG_ATT_SELFPOWER:
/* from config descriptor bmAttributes */
#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */
#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */
#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */
#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */Bit 7 is worth a sentence of its own. It carries no information at all — it is a legacy constant that must be set, and a descriptor with it clear is malformed. It is the cheapest possible sanity check on a descriptor you have just parsed, and mutation S3 clearing it dies 56 396 times.
bMaxPower still means something for a self-powered device: it declares what the device draws from VBUS, which is not what it consumes. A well-behaved self-powered device declares nearly nothing there — its interface circuitry and no more.
2. What a Self-Powered Device Still Owes the Bus
It must keep watching VBUS.
A device signals its presence by pulling D+ up to its own 3.3 V through a resistor. A bus-powered device physically cannot do this with VBUS absent — it has no power to do it with. A self-powered device can.
And if it does, current flows out of the device, through the pull-up, down the data line, and into a host that has deliberately removed power.
The fix is one gate. The pull-up is driven only when VBUS is present, whatever the device's own logic wants — mutation S1, 11 637 errors.
3. Self-Powered Is Not a Constant
A device can have both supplies.
While its own supply is up it is self-powered. When that supply fails — unplugged brick, flat battery, a powered hub switched off at the wall — it is a bus-powered device, and it is subject to 19.1's one-unit-load floor like any other.
Its descriptor has not changed. The claim in bmAttributes is static; the state is not.
So GetStatus reports the state, not the claim — bit 0 of the device status, which Linux reads from the device rather than inferring from the descriptor. A dual-powered device running on the bus reports zero there while its descriptor still says self-powered, and the host needs that, because it changes whether the device counts against the port's budget (19.6).
descriptor bmAttributes bit 6 | GetStatus bit 0 | |
|---|---|---|
| What it is | a claim, fixed at design time | the state, right now |
| Changes at runtime? | no | yes |
| Host uses it for | knowing the device can self-power | budget arithmetic |
Mutation S2 reports the claim instead of the state and dies 19 637 times.
4. The Guard, Drawn
The gate has two inputs and one of them is not the device's business. pullup_request is what the device wants; vbus_present is whether there is anywhere for it to go. A device that consults only the first is the failure in §2.
5. The Hardware, Before Any Language
Everything is combinational except a counter kept for observability.
The guard is pullup_request && vbus_present and nothing else feeds it — not the local supply, not the configuration, not whether the device thinks it is ready. With no VBUS there is no host to attach to, and that is true regardless of anything happening inside the device.
guard_blocked is an output, and it is live. Chapter 18.3 §5 built a dead signal by accident — one whose condition could never occur — and it had to be replaced. This one fires whenever the device's own logic asked for the pull-up and the guard refused, which is a real condition reached 5818 times in the sweep. A guard whose activity cannot be measured is a guard nobody can prove is doing anything.
bus_draw_mA follows the actual source, not the claim: zero with no VBUS, bMaxPower scaled while genuinely self-powered, and 19.1's unit-load floor when running from the bus.
operational needs both a supply and VBUS. A self-powered device with its own supply up and no VBUS is powered but not operational — it has everything it needs except somebody to talk to.
6. Verilog-2005
// usb_selfpower_control -- a device that supplies itself, and what it still
// owes the bus.
//
// Chapter 19.1 built a device that takes its power from VBUS. Many devices
// do not: a printer, a powered hub, a desktop drive. They declare themselves
// SELF-POWERED with a bit in the configuration descriptor's bmAttributes,
// and the host's budget arithmetic (19.6) then skips them.
//
// The interesting question is what such a device still owes the bus, and the
// answer is: IT MUST KEEP WATCHING VBUS.
//
// THE FAILURE THIS MODULE EXISTS TO PREVENT
//
// A device signals its presence by pulling D+ (or D-) up to its own 3.3 V
// through a resistor. A BUS-powered device physically cannot do this with
// VBUS absent -- it has no power. A SELF-powered device can, and if it does,
// current flows out of the device, through the pull-up, down the data line,
// and into a host that has DELIBERATELY removed power.
//
// The consequences are all bad and none of them are obvious:
//
// * the host sees a device attached to a port it has powered off, so its
// model of the bus is wrong;
// * the host cannot complete a power-cycle of that port, because the port
// never actually goes quiet;
// * current flows into a rail that is supposed to be dead, which can hold
// up a supply rail the host was trying to bring down, and on a laptop
// can keep a controller partly alive across a sleep transition.
//
// This is "back-powering", and the fix is one gate: the pull-up is driven
// ONLY when VBUS is present, whatever the device's own logic wants.
//
// The second rule worth stating: SELF-POWERED IS NOT A CONSTANT. A device
// with both supplies is self-powered while its own supply is up and bus-
// powered when it is not, and GetStatus reports the state it is in NOW --
// not the claim its descriptor makes. Linux reads that bit from the device
// rather than from the descriptor for exactly this reason.
module usb_selfpower_control #(
parameter integer UNIT_LOAD_MA = 100 // what the bus grants before config
) (
input wire clk,
input wire rst_n,
input wire vbus_present,
input wire local_power_good, // the device's OWN supply is up
input wire cfg_self_powered, // bmAttributes bit 6, the CLAIM
input wire cfg_remote_wakeup, // bmAttributes bit 5
input wire [7:0] b_max_power, // units of 2 mA drawn FROM THE BUS
input wire pullup_request, // the device's logic wants to attach
output wire pullup_enable, // what actually drives the resistor
output wire status_self_powered,// GetStatus bit 0 -- the state NOW
output wire status_remote_wakeup,
output wire [7:0] bm_attributes, // the descriptor byte, assembled
output wire [15:0] bus_draw_mA, // what it takes from VBUS
output wire operational, // the device can actually function
output wire [1:0] power_source, // where the current comes from NOW
output wire guard_blocked, // the guard is blocking RIGHT NOW
output reg [31:0] blocked_count, // how often it has had to
output reg ever_blocked
);
// THE GUARD. One AND gate, and the entire point of the module: the device
// may want to attach, but with no VBUS there is nothing to attach TO, and
// driving the pull-up would push current into the host.
assign pullup_enable = pullup_request && vbus_present;
// Live, not dead: this asserts whenever the device's own logic asked for
// the pull-up and the guard refused. A design where this can never fire
// would be a design where the guard does nothing, and the testbench needs
// to prove the guard was actually exercised rather than assume it.
assign guard_blocked = pullup_request && !vbus_present;
// GetStatus reports the state the device is in NOW. A dual-powered device
// that has lost its own supply and is running from the bus reports zero
// here even though its descriptor claims self-powered -- and the host
// needs that, because it changes whether the device counts against the
// port's budget.
assign status_self_powered = cfg_self_powered && local_power_good;
assign status_remote_wakeup = cfg_remote_wakeup;
// bmAttributes, assembled. Bit 7 is reserved and MUST be set -- Linux
// names it USB_CONFIG_ATT_ONE. It carries no information; it is a legacy
// constant, and a descriptor with it clear is malformed.
assign bm_attributes = {1'b1, // bit 7: must be one
cfg_self_powered, // bit 6
cfg_remote_wakeup, // bit 5
5'b00000};
// What the device takes FROM THE BUS -- which is not what it consumes.
// A self-powered device running on its own supply draws only what
// bMaxPower declares, and a well-behaved one declares nearly nothing.
// Running on bus power instead, it is a bus-powered device and is held
// to chapter 19.1's one-unit-load floor until it is configured.
assign bus_draw_mA = !vbus_present ? 16'd0
: status_self_powered ? ({8'd0, b_max_power} << 1)
: UNIT_LOAD_MA[15:0];
// The same facts as a named source. Verilog-2005 has no enumerated type,
// so the encoding is stated as localparams -- which is the nearest thing
// available and still better than leaving a reader to reconstruct the
// source from two separate bits.
localparam [1:0] SRC_NONE = 2'd0, // no VBUS and no local supply
SRC_BUS = 2'd1, // drawing from VBUS (19.1's floor)
SRC_SELF = 2'd2; // running on its own supply
assign power_source = status_self_powered ? SRC_SELF
: vbus_present ? SRC_BUS
: SRC_NONE;
// A device is operational when it has power from SOMEWHERE and a bus to
// talk on. Note both terms: its own supply is not enough, because with no
// VBUS there is no host to talk to.
assign operational = vbus_present && (local_power_good || !cfg_self_powered);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
blocked_count <= 32'd0;
ever_blocked <= 1'b0;
end else if (guard_blocked) begin
blocked_count <= blocked_count + 32'd1;
ever_blocked <= 1'b1;
end
end
endmodulelocalparam [1:0] SRC_NONE / SRC_BUS / SRC_SELF is Verilog-2005's version of an enumerated type, and the reason it is here rather than only in the other two languages is §14: without it, the Verilog testbench had one fewer property to check than the SystemVerilog and VHDL ones, and a mutation scored 5373 lower in Verilog for a reason that had nothing to do with the design.
7. SystemVerilog
package usb_selfpower_pkg;
// Where the device's current is ACTUALLY coming from this instant, which
// is not the same as what its descriptor claims. A dual-powered device
// moves between SRC_SELF and SRC_BUS without its descriptor changing, and
// GetStatus reports this, not the claim.
typedef enum logic [1:0] {
SRC_NONE, // no VBUS and no local supply: the device is off
SRC_BUS, // drawing from VBUS -- subject to 19.1's unit-load floor
SRC_SELF // running on its own supply
} power_source_e;
endpackage
// usb_selfpower_control_sv -- a device that supplies itself, and what it still
// owes the bus.
//
// Chapter 19.1 built a device that takes its power from VBUS. Many devices
// do not: a printer, a powered hub, a desktop drive. They declare themselves
// SELF-POWERED with a bit in the configuration descriptor's bmAttributes,
// and the host's budget arithmetic (19.6) then skips them.
//
// The interesting question is what such a device still owes the bus, and the
// answer is: IT MUST KEEP WATCHING VBUS.
//
// THE FAILURE THIS MODULE EXISTS TO PREVENT
//
// A device signals its presence by pulling D+ (or D-) up to its own 3.3 V
// through a resistor. A BUS-powered device physically cannot do this with
// VBUS absent -- it has no power. A SELF-powered device can, and if it does,
// current flows out of the device, through the pull-up, down the data line,
// and into a host that has DELIBERATELY removed power.
//
// The consequences are all bad and none of them are obvious:
//
// * the host sees a device attached to a port it has powered off, so its
// model of the bus is wrong;
// * the host cannot complete a power-cycle of that port, because the port
// never actually goes quiet;
// * current flows into a rail that is supposed to be dead, which can hold
// up a supply rail the host was trying to bring down, and on a laptop
// can keep a controller partly alive across a sleep transition.
//
// This is "back-powering", and the fix is one gate: the pull-up is driven
// ONLY when VBUS is present, whatever the device's own logic wants.
//
// The second rule worth stating: SELF-POWERED IS NOT A CONSTANT. A device
// with both supplies is self-powered while its own supply is up and bus-
// powered when it is not, and GetStatus reports the state it is in NOW --
// not the claim its descriptor makes. Linux reads that bit from the device
// rather than from the descriptor for exactly this reason.
module usb_selfpower_control_sv
import usb_selfpower_pkg::*;
#(
parameter int unsigned UNIT_LOAD_MA = 100 // what the bus grants before config
) (
input logic clk,
input logic rst_n,
input logic vbus_present,
input logic local_power_good, // the device's OWN supply is up
input logic cfg_self_powered, // bmAttributes bit 6, the CLAIM
input logic cfg_remote_wakeup, // bmAttributes bit 5
input logic [7:0] b_max_power, // units of 2 mA drawn FROM THE BUS
input logic pullup_request, // the device's logic wants to attach
output logic pullup_enable, // what actually drives the resistor
output logic status_self_powered,// GetStatus bit 0 -- the state NOW
output logic status_remote_wakeup,
output logic [7:0] bm_attributes, // the descriptor byte, assembled
output logic [15:0] bus_draw_mA, // what it takes from VBUS
output logic operational, // the device can actually function
output power_source_e power_source, // where the current comes from NOW
output logic guard_blocked, // the guard is blocking RIGHT NOW
output logic [31:0] blocked_count, // how often it has had to
output logic ever_blocked
);
// THE GUARD. One AND gate, and the entire point of the module: the device
// may want to attach, but with no VBUS there is nothing to attach TO, and
// driving the pull-up would push current into the host.
assign pullup_enable = pullup_request && vbus_present;
// Live, not dead: this asserts whenever the device's own logic asked for
// the pull-up and the guard refused. A design where this can never fire
// would be a design where the guard does nothing, and the testbench needs
// to prove the guard was actually exercised rather than assume it.
assign guard_blocked = pullup_request && !vbus_present;
// GetStatus reports the state the device is in NOW. A dual-powered device
// that has lost its own supply and is running from the bus reports zero
// here even though its descriptor claims self-powered -- and the host
// needs that, because it changes whether the device counts against the
// port's budget.
assign status_self_powered = cfg_self_powered && local_power_good;
assign status_remote_wakeup = cfg_remote_wakeup;
// bmAttributes, assembled. Bit 7 is reserved and MUST be set -- Linux
// names it USB_CONFIG_ATT_ONE. It carries no information; it is a legacy
// constant, and a descriptor with it clear is malformed.
assign bm_attributes = {1'b1, // bit 7: must be one
cfg_self_powered, // bit 6
cfg_remote_wakeup, // bit 5
5'b00000};
// What the device takes FROM THE BUS -- which is not what it consumes.
// A self-powered device running on its own supply draws only what
// bMaxPower declares, and a well-behaved one declares nearly nothing.
// Running on bus power instead, it is a bus-powered device and is held
// to chapter 19.1's one-unit-load floor until it is configured.
assign bus_draw_mA = !vbus_present ? 16'd0
: status_self_powered ? (16'(b_max_power) << 1)
: 16'(UNIT_LOAD_MA);
// The same facts as a named source. A reader of a waveform can see
// SRC_SELF become SRC_BUS at the instant a local supply fails; two
// separate bits require them to reconstruct it.
always_comb begin
if (status_self_powered) power_source = SRC_SELF;
else if (vbus_present) power_source = SRC_BUS;
else power_source = SRC_NONE;
end
// A device is operational when it has power from SOMEWHERE and a bus to
// talk on. Note both terms: its own supply is not enough, because with no
// VBUS there is no host to talk to.
assign operational = vbus_present && (local_power_good || !cfg_self_powered);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
blocked_count <= '0;
ever_blocked <= 1'b0;
end else if (guard_blocked) begin
blocked_count <= blocked_count + 1;
ever_blocked <= 1'b1;
end
end
endmodulepower_source_e is worth more here than a typical enum, because the transition it names is invisible otherwise. A dual-powered device losing its supply changes status_self_powered from 1 to 0 — which a waveform reader must then combine with vbus_present to work out that the device is now bus-powered rather than off. SRC_SELF → SRC_BUS says it in one lane.
8. VHDL-2008
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package usb_selfpower_pkg is
-- Where the device's current is ACTUALLY coming from this instant, which
-- is not the same as what its descriptor claims. A dual-powered device
-- moves between SRC_SELF and SRC_BUS without its descriptor changing, and
-- GetStatus reports this, not the claim.
type power_source_t is (
SRC_NONE, -- no VBUS and no local supply: the device is off
SRC_BUS, -- drawing from VBUS -- subject to 19.1's unit-load floor
SRC_SELF -- running on its own supply
);
end package;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.usb_selfpower_pkg.all;
-- usb_selfpower_control_vhdl -- a device that supplies itself, and what it
-- still owes the bus.
--
-- Chapter 19.1 built a device that takes its power from VBUS. Many devices
-- do not: a printer, a powered hub, a desktop drive. They declare themselves
-- SELF-POWERED with a bit in the configuration descriptor's bmAttributes,
-- and the host's budget arithmetic (19.6) then skips them.
--
-- The interesting question is what such a device still owes the bus, and the
-- answer is: IT MUST KEEP WATCHING VBUS.
--
-- THE FAILURE THIS MODULE EXISTS TO PREVENT
--
-- A device signals its presence by pulling D+ (or D-) up to its own 3.3 V
-- through a resistor. A BUS-powered device physically cannot do this with
-- VBUS absent -- it has no power. A SELF-powered device can, and if it does,
-- current flows out of the device, through the pull-up, down the data line,
-- and into a host that has DELIBERATELY removed power.
--
-- The consequences are all bad and none of them are obvious:
--
-- * the host sees a device attached to a port it has powered off, so its
-- model of the bus is wrong;
-- * the host cannot complete a power-cycle of that port, because the port
-- never actually goes quiet;
-- * current flows into a rail that is supposed to be dead, which can hold
-- up a supply rail the host was trying to bring down, and on a laptop
-- can keep a controller partly alive across a sleep transition.
--
-- This is "back-powering", and the fix is one gate: the pull-up is driven
-- ONLY when VBUS is present, whatever the device's own logic wants.
--
-- The second rule worth stating: SELF-POWERED IS NOT A CONSTANT. A device
-- with both supplies is self-powered while its own supply is up and bus-
-- powered when it is not, and GetStatus reports the state it is in NOW --
-- not the claim its descriptor makes.
entity usb_selfpower_control_vhdl is
generic (
UNIT_LOAD_MA : positive := 100 -- what the bus grants before config
);
port (
clk : in std_logic;
rst_n : in std_logic;
vbus_present : in std_logic;
local_power_good : in std_logic; -- the device's OWN supply is up
cfg_self_powered : in std_logic; -- bmAttributes bit 6, the CLAIM
cfg_remote_wakeup : in std_logic; -- bmAttributes bit 5
b_max_power : in unsigned(7 downto 0);
pullup_request : in std_logic; -- the device's logic wants to attach
pullup_enable : out std_logic; -- what actually drives the resistor
status_self_powered : out std_logic; -- GetStatus bit 0 -- the state NOW
status_remote_wakeup : out std_logic;
bm_attributes : out std_logic_vector(7 downto 0);
bus_draw_mA : out unsigned(15 downto 0);
operational : out std_logic;
power_source : out power_source_t;
guard_blocked : out std_logic; -- the guard is blocking RIGHT NOW
blocked_count : out unsigned(31 downto 0);
ever_blocked : out std_logic
);
end entity;
architecture rtl of usb_selfpower_control_vhdl is
signal pu_i, blk_i, ssp_i : std_logic;
signal cnt_r : unsigned(31 downto 0) := (others => '0');
signal ever_r : std_logic := '0';
begin
-- THE GUARD. One AND gate, and the entire point of the module: the device
-- may want to attach, but with no VBUS there is nothing to attach TO, and
-- driving the pull-up would push current into the host.
pu_i <= pullup_request and vbus_present;
-- Live, not dead: this asserts whenever the device's own logic asked for
-- the pull-up and the guard refused. A design where this can never fire
-- would be a design where the guard does nothing, and the testbench needs
-- to prove the guard was actually exercised rather than assume it.
blk_i <= pullup_request and (not vbus_present);
-- GetStatus reports the state the device is in NOW. A dual-powered device
-- that has lost its own supply and is running from the bus reports zero
-- here even though its descriptor claims self-powered.
ssp_i <= cfg_self_powered and local_power_good;
pullup_enable <= pu_i;
guard_blocked <= blk_i;
status_self_powered <= ssp_i;
status_remote_wakeup <= cfg_remote_wakeup;
blocked_count <= cnt_r;
ever_blocked <= ever_r;
-- bmAttributes, assembled. Bit 7 is reserved and MUST be set -- Linux
-- names it USB_CONFIG_ATT_ONE. It carries no information; it is a legacy
-- constant, and a descriptor with it clear is malformed.
bm_attributes <= '1' & cfg_self_powered & cfg_remote_wakeup & "00000";
-- What the device takes FROM THE BUS -- which is not what it consumes.
-- A self-powered device running on its own supply draws only what
-- bMaxPower declares. Running on bus power instead, it is a bus-powered
-- device and is held to chapter 19.1's one-unit-load floor.
bus_draw_mA <= to_unsigned(0, 16) when vbus_present = '0' else
shift_left(resize(b_max_power, 16), 1) when ssp_i = '1' else
to_unsigned(UNIT_LOAD_MA, 16);
-- A device is operational when it has power from SOMEWHERE and a bus to
-- talk on. Note both terms: its own supply is not enough, because with no
-- VBUS there is no host to talk to.
operational <= '1' when (vbus_present = '1'
and (local_power_good = '1'
or cfg_self_powered = '0'))
else '0';
-- The same facts as a named source. A reader of a waveform can see
-- SRC_SELF become SRC_BUS at the instant a local supply fails; two
-- separate bits require them to reconstruct it.
power_source <= SRC_SELF when ssp_i = '1' else
SRC_BUS when vbus_present = '1' else
SRC_NONE;
process (clk, rst_n)
begin
if rst_n = '0' then
cnt_r <= (others => '0'); ever_r <= '0';
elsif rising_edge(clk) then
if blk_i = '1' then
cnt_r <= cnt_r + 1;
ever_r <= '1';
end if;
end if;
end process;
end architecture;The when / else chain for bus_draw_mA reads as the priority list it is, and VHDL requires every branch to produce the same type — so the to_unsigned(0, 16) is explicit rather than a bare 0 that Verilog would size by context.
9. The Testbench: 8192 Points, Exhaustively
Five control inputs and the whole bMaxPower field: 2⁵ × 256 = 8192 points, the entire domain.
for (st=0; st<32; st=st+1)
for (p=0; p<256; p=p+1) begin
vbus=st[0]; lpg=st[1]; cfg_sp=st[2]; cfg_rw=st[3]; pur=st[4];
bmp=p[7:0];
step;
n_exh = n_exh + 1;
endSix safety properties are checked at every point, and the first is the chapter:
// ---- SAFETY PROPERTIES, independent of the model ----
// 1. THE one that matters. No VBUS, no pull-up -- ever, for any
// combination of the device's own state. This is back-powering.
if (!vbus)
check(!pullup_enable,
"the device drove its pull-up with VBUS absent: BACK-POWERING");
// 2. With no VBUS a device takes nothing from the bus.
if (!vbus)
check(bus_draw_mA === 16'd0, "current drawn from an unpowered bus");
// 3. bmAttributes bit 7 is always set -- USB_CONFIG_ATT_ONE.
check(bm_attributes[7], "bmAttributes bit 7 must be one");
// 4. A device reporting self-powered really does have its own supply.
check(!status_self_powered || lpg,
"reported self-powered without a local supply");
// 5. An operational device always has VBUS.
check(!operational || vbus, "operational with no bus to talk on");Property 1 is checked against no model at all. It does not ask what the pull-up should be; it asserts that one particular combination is never produced, for any input. That is the right shape for a property whose violation damages the system rather than the data.
And the directed sequence walks the exact failure:
// a self-powered device, its own supply up, VBUS REMOVED, and its logic
// still asking to attach: the back-powering scenario exactly.
vbus=0; lpg=1; cfg_sp=1; cfg_rw=0; pur=1; bmp=8'd0; step;
check(!pullup_enable,
"with VBUS gone the pull-up stays down even though the device can drive it");
check(guard_blocked, "and the guard reports that it is blocking");
check(bus_draw_mA === 16'd0, "and nothing flows into the host");
// VBUS returns: the same device attaches immediately
vbus=1; step;
check(pullup_enable, "VBUS returns and the device attaches");
// the dual-powered case: its own supply fails while attached
lpg=0; step;
check(pullup_enable, "it stays attached -- VBUS is still there");
check(!status_self_powered,
"but now reports BUS-powered: the status bit is dynamic");
check(bus_draw_mA === 16'd100,
"and it falls back to the one-unit-load floor of 19.1");That last transition is §3 in three lines, and it is the one a directed test is genuinely better at than a sweep: the sweep visits the state, but only a sequence shows the change.
Measured reach:
exhaustive self-power sweep: 8192 of 8192 points verified
Verilog / SystemVerilog:
REACH: exhaustive=8192 guard-blocks=5818 operational=15565 dual-on-bus=3526
[Verilog] usb_selfpower_control: 0 errors — PASS
VHDL:
REACH: exhaustive=8192 guard-blocks=5836 operational=15646 dual-on-bus=3459
[VHDL] usb_selfpower_control_vhdl: 0 errors — PASSguard-blocks=5818 is the number that makes the rest meaningful. It says the guard actually refused a request 5818 times rather than sitting in a state where it never had to — the same role 18.1 §14's dirty-reset counter and 18.3 §10's late_event counter play.
10. Mutation Testing — Across All Three Languages
| Mutation | Verilog | SystemVerilog | VHDL | |
|---|---|---|---|---|
| S1 | the guard is removed — the device back-powers the host | 11637 | 11637 | 11673 |
| S2 | GetStatus reports the descriptor's claim, not the state | 19637 | 19637 | 19327 |
| S3 | bmAttributes bit 7 not set | 56396 | 56396 | 56396 |
| S4 | current drawn from a bus that is not powered | 18188 | 18188 | 18156 |
| S5 | operational without a bus to talk on | 14518 | 14518 | 14512 |
| S6 | a self-powered device is charged the unit load anyway | 6037 | 6037 | 6027 |
| S7 | guard_blocked reports the wrong condition | 3288 | 3288 | 3256 |
S3 scores identically in all three — 56 396 — and that is expected rather than suspicious. It is a purely combinational mutation of a value the exhaustive sweep determines completely, so all three benches check it the same number of times. Chapter 18.4 §13's duplicate detector is about two different mutations scoring alike, which is a different signal; identical counts for one mutation across languages mean the sweep is deterministic, which it is by construction.
S1 is the chapter and does not score highest, which is worth noting: back-powering is only observable when pullup_request is high and VBUS is absent, which is a quarter of the control space. The mutation with the worst real-world consequence is not the one with the biggest number, and reading the matrix as a ranking of severity would get this exactly backwards.
11. The Property the Verilog Bench Was Missing
The first run of this matrix had S2 at 14 264 in Verilog against 19 637 in SystemVerilog and 19 327 in VHDL — a 27 % shortfall in one language.
The cause was not the design. The SystemVerilog and VHDL versions expose power_source as a named type, and their testbenches check it:
// 6. The named source and the individual bits never disagree.
check(power_source === (e_sp ? 2'd2 : vbus ? 2'd1 : 2'd0),
"power_source names what the bits already say");The Verilog design had no such output, on the reasoning that Verilog-2005 has no enumerated type — so its bench had five properties where the others had six, and S2 corrupts exactly the signal that sixth property watches.
The fix was to give Verilog the output too, with a localparam encoding:
// The same facts as a named source. Verilog-2005 has no enumerated type,
// so the encoding is stated as localparams -- which is the nearest thing
// available and still better than leaving a reader to reconstruct the
// source from two separate bits.
localparam [1:0] SRC_NONE = 2'd0, // no VBUS and no local supply
SRC_BUS = 2'd1, // drawing from VBUS (19.1's floor)
SRC_SELF = 2'd2; // running on its own supplyS2 in Verilog went from 14 264 to 19 637, matching the other two exactly.
12. A UVM Environment for a Safety Guard
The guard is combinational, so what UVM adds is the ability to state the property as something that must never happen, and then generate stimulus specifically trying to make it happen.
// The adversarial agent. Its job is to make the device attach at the worst
// possible moment -- which is precisely what a real device's internal logic
// does by accident when a user yanks a cable.
class backpower_attack_seq extends uvm_sequence #(selfpower_item);
`uvm_object_utils(backpower_attack_seq)
task body();
selfpower_item it;
// Raise the pull-up request FIRST, with the local supply up, and only
// then remove VBUS. A device whose guard is level-sensitive passes;
// one that latched the request at attach time does not.
`uvm_do_with(it, { vbus_present == 1; local_power_good == 1;
pullup_request == 1; })
`uvm_do_with(it, { vbus_present == 0; local_power_good == 1;
pullup_request == 1; }) // the moment of truth
// and hold it there, because a guard that only blocks on the EDGE of
// VBUS loss would pass the cycle above and fail the ones after.
repeat (8)
`uvm_do_with(it, { vbus_present == 0; local_power_good == 1;
pullup_request == 1; })
endtask
endclass
class selfpower_scoreboard extends uvm_scoreboard;
`uvm_component_utils(selfpower_scoreboard)
function void write(selfpower_txn t);
// THE property. Not a comparison against an expected value -- an
// assertion that one combination is never produced, for any input.
// Its violation damages the host, not the data.
if (!t.vbus_present && t.pullup_enable)
`uvm_fatal("SELFPWR/BACKPOWER",
"pull-up driven with VBUS absent: current is flowing into the host")
// The status bit is dynamic. A device claiming self-power without a
// local supply is lying to the host's budget arithmetic (19.6).
if (t.status_self_powered && !t.local_power_good)
`uvm_error("SELFPWR/STATUS",
"reported self-powered with no local supply")
endfunction
endclass
covergroup selfpower_cg with function sample(
bit vbus, bit lpg, bit cfg_sp, bit pur);
// The bin that matters: the device WANTED to attach and VBUS was gone.
// A run where this reads zero has not tested the guard at all, however
// many points it swept.
cp_attack : coverpoint {pur, vbus} { bins wants_but_cannot = {2'b10}; }
// The dual-powered transition of section 3.
cp_dual : coverpoint {cfg_sp, lpg, vbus} {
bins self_powered = {3'b111};
bins fell_to_bus = {3'b101}; // claims self, supply down, VBUS up
}
x_attack : cross cp_attack, cp_dual;
endgroup13. Assertions
// THE property: no VBUS, no pull-up. Unconditional, and it needs no
// antecedent beyond the absence of VBUS itself.
property p_no_backpower;
@(posedge clk) disable iff (!rst_n)
(!vbus_present) |-> (!pullup_enable);
endproperty
a_no_backpower : assert property (p_no_backpower)
else $fatal(1, "pull-up driven with VBUS absent: BACK-POWERING");
// The status bit is the state, not the claim. Mutation S2.
property p_status_is_state;
@(posedge clk) disable iff (!rst_n)
status_self_powered == (cfg_self_powered && local_power_good);
endproperty
a_status_is_state : assert property (p_status_is_state);
// Nothing is drawn from a bus that is not powered. Mutation S4.
property p_no_draw_unpowered;
@(posedge clk) disable iff (!rst_n)
(!vbus_present) |-> (bus_draw_mA == '0);
endproperty
a_no_draw_unpowered : assert property (p_no_draw_unpowered);
// bmAttributes bit 7 is a constant. USB_CONFIG_ATT_ONE. Mutation S3.
property p_attr_bit7;
@(posedge clk) disable iff (!rst_n) bm_attributes[7];
endproperty
a_attr_bit7 : assert property (p_attr_bit7);p_no_backpower is a one-line proof obligation with no state and two signals, which makes it the single most formal-friendly property in Module 19: a prover settles it exhaustively in negligible time, for every parameterisation.
These were written but not simulated; Icarus supports no concurrent assertions.
14. Debugging: the Laptop That Will Not Sleep
The report: a laptop refuses to enter sleep, or wakes immediately. It happens only at one desk. Unplugging a USB hub fixes it — but the hub is not the device people suspect, because the hub works perfectly for data.
The procedure:
1. Establish that it is a port that should be off. During sleep the host removes VBUS from its ports. A port still showing an attached device at that point is the signature — something is holding the line up without the host's power.
2. Identify which attached device is self-powered. Only a self-powered device can do this (§2); a bus-powered one has nothing to do it with. A powered hub, a printer, a powered drive — the candidate list is short and it is exactly the list of things with their own power brick.
3. Confirm by removing the local supply, not the cable. Unplug the device's power brick and leave its USB cable connected. If the problem goes away, the pull-up was being driven from that supply. This is the decisive test and it takes ten seconds.
4. Distinguish it from remote wakeup. A device that legitimately wakes the host is doing so through 19.5's mechanism, which is enabled by an explicit feature the host sets. Back-powering is not a wakeup; it prevents the sleep from completing rather than ending one that did.
5. There is no software fix. The guard is in the device. A host can only stop offering the port — which is why the practical advice ends up being "use a different hub", and why the underlying defect so rarely gets attributed correctly.
15. Common Misconceptions
"A self-powered device doesn't care about VBUS." It must watch VBUS or it back-powers the host (§2). Mutation S1, 11 637 errors.
"A self-powered device draws nothing from the bus." It declares what it draws in bMaxPower (§1), and a dual-powered one falls back to full bus draw when its supply fails (§3).
"bmAttributes bit 6 tells the host the device is self-powered." It tells the host the device can be (§3). GetStatus bit 0 tells it what the device is, now.
"Bit 7 of bmAttributes is a self-powered flag." It is a legacy constant that must be set and carries no information (§1). Mutation S3, 56 396 errors.
"A device with its own supply is operational without VBUS." It is powered but not operational — there is no host to talk to (§5). Mutation S5, 14 518 errors.
"Back-powering just means a little leakage current." It means the host cannot power-cycle the port, its model of the bus is wrong, and a sleep transition can fail (§2, §14).
"The largest mutation count is the worst bug." S1 scores 11 637 — the lowest but one — and is the only mutation here that damages hardware (§10).
16. Exercises
1. §11 found a property one testbench could not check because one design lacked an output. Audit the other five outputs of this module and determine whether any is checked in fewer languages than the rest.
2. The guard is level-sensitive: pullup_request && vbus_present. Construct a plausible edge-sensitive implementation, say what it does wrong, and determine which of §10's mutations would catch it.
3. A dual-powered device loses its local supply while configured for 500 mA. Trace what 19.1's negotiation and this chapter's bus_draw_mA each report, and say whether they can disagree.
4. Write the SVA property that catches mutation S6 — charging a self-powered device the unit load — without referring to UNIT_LOAD_MA.
5. §12 uses uvm_fatal for back-powering and uvm_error for a wrong status bit. Define the rule that separates them, and classify every check in §9 under it.
6. A device is battery-powered (USB_CONFIG_ATT_BATTERY, bit 4). Determine what this module would have to add, and whether GetStatus bit 0 remains sufficient.
17. Summary
A self-powered device declares itself with bmAttributes bit 6 (§1), and bit 7 beside it is a legacy constant that must be set and means nothing (S3, 56 396 errors).
What it still owes the bus is to keep watching VBUS (§2). A pull-up driven with VBUS absent pushes current out of the device, down the data line, and into a host that deliberately removed power — so the host's model of the bus is wrong, its power-cycle of that port silently does not happen, and a sleep transition can fail. The fix is one AND gate (S1, 11 637 errors).
Self-powered is not a constant (§3). A dual-powered device is self-powered while its supply is up and bus-powered when it is not, and GetStatus reports the state rather than the claim — because the host's budget arithmetic depends on which it currently is (S2, 19 637 errors).
All three HDL implementations were simulated (§18) and seven mutations died in all three (§10), with the design verified exhaustively over all 8192 points (§9) — every control combination against the entire bMaxPower field.
The guard blocked 5818 times (§9), which is what makes the rest meaningful: a guard that never had to refuse anything has not been tested.
And one language's testbench was checking one property fewer than the other two (§11). S2 scored 14 264 in Verilog against 19 637 elsewhere, not because the Verilog design differed but because it lacked the power_source output the others exposed — so the comparison was measuring the benches. Adding a localparam encoding to the Verilog brought it to exactly 19 637. "This language doesn't have enums" is a reason to encode the state differently, not to omit it.
And the mutation with the worst consequences is not the one with the biggest number (§10) — reading the matrix as a severity ranking would invert this chapter entirely.
18. Tooling, Honestly
| Language | Design | Testbench | Analysed / compiled | Simulated | Mutations |
|---|---|---|---|---|---|
| Verilog-2005 | usb_selfpower_control | sp_v_tb.v | ✅ Icarus -g2005 | ✅ 0 errors, 8192/8192 | ✅ all seven |
| SystemVerilog | usb_selfpower_control_sv | sp_sv_tb.sv | ✅ Icarus -g2012 | ✅ 0 errors, 8192/8192 | ✅ all seven |
| VHDL-2008 | usb_selfpower_control_vhdl | sp_vhdl_tb.vhd | ✅ nvc 1.23.0 | ✅ 0 errors, 8192/8192 | ✅ all seven |
| UVM (§12) | — | — | ❌ no UVM-capable simulator here | ❌ | — |
| SVA (§13) | — | — | ❌ unsupported by Icarus | ❌ | — |
VHDL's randomised tail differs slightly (5836 guard blocks against 5818, 3459 dual-on-bus against 3526) because the three benches draw from different generators. The exhaustive 8192 points are identical by construction, and every mutation count agrees to within 2 %.
19. What Comes Next
Both chapters so far have assumed the bus is active — traffic flowing, a host issuing tokens, a device answering.
USB has no idle. The host sends a start-of-frame every millisecond whether or not there is anything to do, precisely so that devices can tell "nothing is happening" from "the bus is gone". Which raises the question the next chapter answers: what happens when the SOFs stop?
Chapter 19.3 — Suspend is about that, and the number at its centre is 3 ms. Three milliseconds of continuous bus idle and a device must begin suspending; within 10 ms it must have cut its draw to a trickle measured in microamps.
The word "continuous" is doing real work there, and it is where the chapter's RTL and its hardest bug both live.
Browse the full path on the USB tutorials index.
Continue learning
Related tutorials
- Related topic
Hub Power Management
A bus-powered hub gets 500 mA and must supply four ports that could each want 500 mA — so its ports are offered one unit load, and a device needing more is refused.
- Related topic
Bus Power
A device's current allowance changes exactly once during enumeration — and bMaxPower is counted in 2 mA units, not milliamps.
- Related topic
Hub Architecture
A hub is three devices in one package, and its repeater is deliberately asymmetric: downstream is a broadcast, upstream is a select of exactly one — and two talkers connects neither.
- Related topic
Port Management
Three uncoordinated sources move a hub port and collide in the same cycle — and the host, whose information is stale by construction, is the one that loses.
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.
