USB · Module 15
The Polling Model
The host runs a periodic schedule the device cannot see, so device hardware must count frames rather than trust a period — built in Verilog, SystemVerilog and VHDL with a schedule model that disagrees independently.
Module 14 priced Bulk, a transfer type that promises nothing about time. Interrupt promises bounded latency — and a promise about time has to be implemented by something that counts.
Chapter 10.4 established what an interrupt endpoint is: polled, not interrupting, with a declared service interval and a device-side obligation to always have an answer. It stopped at the endpoint.
This module starts at the schedule — the structure the host builds, walks once per frame, and never transmits.
1. The Device Cannot See the Schedule
A device declares bInterval in its endpoint descriptor and then never hears about it again.
Nothing on the wire carries a schedule. There is no your next poll is in 4 ms packet, no way to ask when the next one is due, and no acknowledgement that the host accepted the interval at all. Chapter 10.4 §3 established that the interval is a ceiling the host may beat, so even the value the device asked for is not a promise about what it will get.
The device declares a requirement, the host builds a schedule, and the only evidence the device ever receives that the schedule exists is a token arriving.
Which sets this chapter's engineering problem. The schedule is real, it is periodic, and it is entirely on the host side — so what does a device have to build?
Two things, and they are not the same:
| Lives where | Chapter | |
|---|---|---|
| Have an answer ready | the endpoint's state | 10.4 — the sticky pending flag |
| Know when service is due | a periodic counter | this chapter |
A device controller needs the second whenever it must do work in anticipation of a poll rather than in response to one — sampling a sensor, scanning a key matrix, or arming a buffer. And a host controller needs it always, because building the schedule is its whole job.
2. What the Host's Schedule Actually Is
A periodic schedule is a list of endpoints, each with an interval and a position.
Once per frame the host walks the list and services every endpoint whose interval has elapsed. Chapter 11.4 §3's frame number is the clock the walk runs on.
Which makes the schedule a very simple data structure and a hard placement problem:
| Interval | Serviced in frames | Load |
|---|---|---|
| 1 | every frame | heavy, unavoidable |
| 4 | 0, 4, 8, 12 … | ¼ of a frame's periodic budget |
| 8 | 0, 8, 16 … | ⅛ |
| 32 | 0, 32, 64 … | 1⁄32 |
And the placement problem is what §3 is about: two endpoints with the same interval can be serviced in the same frame or in different frames, and the choice changes the peak load without changing the average.
3. Phase: Why Two Endpoints With the Same Interval Must Not Share a Frame
Four endpoints, each with an interval of 4 frames. Each needs service once every four frames, so the average load is one endpoint per frame.
Placed naively — all at position 0:
| Frame | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| Endpoints serviced | 4 | 0 | 0 | 0 | 4 | 0 | 0 | 0 |
Peak load is four transactions in one frame and zero in the next three.
Placed with a phase offset — positions 0, 1, 2, 3:
| Frame | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| Endpoints serviced | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
Same average, quarter the peak.
4. The Hardware, Before Any Language
One endpoint's position in a periodic schedule is a down-counter clocked by the frame boundary.
| Element | Purpose |
|---|---|
A counter, ⌈log₂ INTERVAL⌉ bits | frames remaining until service |
Load value INTERVAL − 1 | reload at terminal count |
Initial value PHASE | §3's spread |
| A terminal-count pulse | service is due now |
| An armed flag | the schedule exists at all |
Three decisions in that table are the whole design, and each is a mutation in §9:
The counter advances on the frame boundary, not on the clock. A controller runs at tens of megahertz and a frame is 1 ms — counting clocks would need a 17-bit counter and would drift against the host, because the two oscillators are independent. Chapter 11.4 §4's rule: count the event both ends observe.
The reload is INTERVAL − 1, not INTERVAL. The terminal count is a frame, so counting INTERVAL more after it gives a period of INTERVAL + 1. This is the classic off-by-one, and §10 measures how each language's testbench finds it.
Disable reloads the phase. An endpoint that is disabled and re-enabled must rejoin the schedule at its assigned position, not wherever its counter happened to stop — otherwise Chapter 8.5's reconfiguration silently destroys §3's spread.
5. Verilog
// ─────────────────────────────────────────────────────────────────────────
// usb_poll_scheduler — SIMPLIFIED SYNTHESIZABLE TEACHING RTL
//
// Models section 4: one endpoint's position in a periodic schedule.
//
// NOT MODELLED: the host's schedule as a whole (this is one entry); the
// bandwidth admission check (Chapter 16.4); the transaction the poll
// launches (Module 12); the endpoint's data (Chapter 10.4's pending flag);
// and the frame counter itself (Chapter 11.4 — frame_tick arrives here).
// ─────────────────────────────────────────────────────────────────────────
module usb_poll_scheduler #(
parameter INTERVAL = 8, // frames between services
parameter PHASE = 0, // section 3: starting offset, < INTERVAL
parameter CNT_W = 8
)(
input wire clk,
input wire rst_n,
input wire bus_reset,
input wire ep_enabled,
input wire frame_tick, // Chapter 11.4: one per frame
output wire poll_due, // single-cycle: service is due now
output wire [CNT_W-1:0] frames_left,
output wire armed
);
reg [CNT_W-1:0] cnt;
reg armed_r;
reg due_r;
assign frames_left = cnt;
assign armed = armed_r;
assign poll_due = due_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= PHASE[CNT_W-1:0];
armed_r <= 1'b0;
due_r <= 1'b0;
end else if (bus_reset || !ep_enabled) begin
// Section 4: rejoin at the ASSIGNED position, not wherever we stopped.
cnt <= PHASE[CNT_W-1:0];
armed_r <= 1'b0;
due_r <= 1'b0;
end else begin
armed_r <= 1'b1;
due_r <= 1'b0; // a pulse, never a level
if (frame_tick) begin // section 4: the FRAME, not the clock
if (cnt == {CNT_W{1'b0}}) begin
due_r <= 1'b1;
// INTERVAL-1, because the terminal count is itself a frame.
cnt <= INTERVAL[CNT_W-1:0] - {{(CNT_W-1){1'b0}}, 1'b1};
end else begin
cnt <= cnt - {{(CNT_W-1){1'b0}}, 1'b1};
end
end
end
end
endmoduleContract. Purpose: report when one endpoint's service is due. Inputs: frame boundary, enable, two resets. Outputs: a single-cycle due pulse, the remaining count, an armed flag. State: CNT_W bits of counter plus two flags. Hardware: a down-counter with a load mux and a zero comparator. Reset: counter to PHASE, disarmed — on hard reset, bus reset and disable alike. Priority: reset/disable beats the frame tick. Boundary: INTERVAL = 1 polls every frame; PHASE = 0 starts due on the first frame. Assumptions: frame_tick is one cycle per frame and PHASE < INTERVAL. Omissions: in the header. Invariants: §11.
6. SystemVerilog
The same hardware. What changes is that two things the Verilog leaves as conventions become checked.
// Same hardware as section 5. SIMPLIFIED SYNTHESIZABLE TEACHING RTL.
module usb_poll_scheduler_sv #(
parameter int unsigned INTERVAL = 8,
parameter int unsigned PHASE = 0,
parameter int unsigned CNT_W = 8
)(
input logic clk,
input logic rst_n,
input logic bus_reset,
input logic ep_enabled,
input logic frame_tick,
output logic poll_due,
output logic [CNT_W-1:0] frames_left,
output logic armed
);
// BUILD-TIME CHECK — Chapter 12.5 section 7's rule: a relationship between
// parameters is decided before a cycle runs, so it is checked where it is
// decided. The Verilog version cannot express this; the constraint is real
// in both.
initial begin
if (INTERVAL == 0)
$fatal(1, "usb_poll_scheduler_sv: INTERVAL must be non-zero");
if (PHASE >= INTERVAL)
$fatal(1, {"usb_poll_scheduler_sv: PHASE (%0d) must be < INTERVAL (%0d) ",
"-- a larger phase is a longer first period, not a spread."},
PHASE, INTERVAL);
end
logic [CNT_W-1:0] cnt_q;
logic armed_q, due_q;
assign frames_left = cnt_q;
assign armed = armed_q;
assign poll_due = due_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_q <= CNT_W'(PHASE); armed_q <= 1'b0; due_q <= 1'b0;
end else if (bus_reset || !ep_enabled) begin
cnt_q <= CNT_W'(PHASE); armed_q <= 1'b0; due_q <= 1'b0;
end else begin
armed_q <= 1'b1;
due_q <= 1'b0;
if (frame_tick) begin
if (cnt_q == '0) begin
due_q <= 1'b1;
cnt_q <= CNT_W'(INTERVAL - 1);
end else begin
cnt_q <= cnt_q - 1'b1;
end
end
end
end
endmoduleWhat the language bought, concretely and not decoratively:
always_ffmakes this is a register a statement the tool checks rather than a convention a reader infers.CNT_W'(PHASE)is an explicit width cast; the VerilogPHASE[CNT_W-1:0]is a slice that silently truncates a too-large parameter.$fatalat elaboration turns §4'sPHASE < INTERVALassumption from a comment into a build failure.'0is width-agnostic, so changingCNT_Wcannot leave a stale literal behind.
7. VHDL
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity usb_poll_scheduler is
generic (
INTERVAL : positive := 8;
PHASE : natural := 0;
CNT_W : positive := 8
);
port (
clk : in std_logic;
rst_n : in std_logic;
bus_reset : in std_logic;
ep_enabled : in std_logic;
frame_tick : in std_logic;
poll_due : out std_logic;
frames_left : out std_logic_vector(CNT_W-1 downto 0);
armed : out std_logic
);
end entity usb_poll_scheduler;
architecture rtl of usb_poll_scheduler is
-- The same build-time constraint as section 6, as an elaboration assertion.
constant PHASE_OK : boolean := (PHASE < INTERVAL);
signal cnt : unsigned(CNT_W-1 downto 0);
signal armed_r : std_logic;
signal due_r : std_logic;
begin
assert PHASE_OK
report "usb_poll_scheduler: PHASE must be < INTERVAL -- a larger phase " &
"is a longer first period, not a spread."
severity failure;
-- Explicit conversions: the counter is arithmetic, the port is a bit vector.
frames_left <= std_logic_vector(cnt);
armed <= armed_r;
poll_due <= due_r;
schedule : process (clk, rst_n)
begin
if rst_n = '0' then
cnt <= to_unsigned(PHASE, CNT_W);
armed_r <= '0';
due_r <= '0';
elsif rising_edge(clk) then
if bus_reset = '1' or ep_enabled = '0' then
cnt <= to_unsigned(PHASE, CNT_W);
armed_r <= '0';
due_r <= '0';
else
armed_r <= '1';
due_r <= '0';
if frame_tick = '1' then
if cnt = to_unsigned(0, CNT_W) then
due_r <= '1';
cnt <= to_unsigned(INTERVAL - 1, CNT_W);
else
cnt <= cnt - 1;
end if;
end if;
end if;
end if;
end process schedule;
end architecture rtl;8. Comparing the Three
The hardware is identical — one counter, one comparator, one load mux, two flags. What differs is what the language makes explicit.
| Concern | Verilog | SystemVerilog | VHDL |
|---|---|---|---|
| Register intent | always @(posedge) — convention | always_ff — checked | process(clk, rst_n) + rising_edge |
| Counter type | reg [N-1:0], arithmetic implied | logic [N-1:0] | unsigned — arithmetic is in the type |
| Port ↔ counter | same object | same object | explicit conversion both ways |
| Width of a constant | slice: PHASE[N-1:0] | cast: CNT_W'(PHASE) | to_unsigned(PHASE, CNT_W) |
| Parameter constraint | comment only | $fatal at elaboration | assert … severity failure |
| Zero literal | {CNT_W{1'b0}} | '0 | to_unsigned(0, CNT_W) |
| Async reset | in the sensitivity list | in the sensitivity list | in the sensitivity list |
Two observations are worth more than the table.
VHDL forces the type distinction the other two leave implicit. frames_left is a std_logic_vector because it is a port, and cnt is unsigned because it is counted — and the conversion between them is written down. In Verilog they are the same object and the arithmetic meaning is in the reader's head.
And both SystemVerilog and VHDL can refuse to build on a bad parameter, where Verilog cannot. That is the same constraint from §4 enforced at the only moment it can be — and §10's P2 mutation is what it would otherwise cost.
9. The Testbenches
All three testbenches apply the same scenario, which is what makes §10's comparison meaningful: reset, the phase, steady state, a single-cycle pulse check, disable/re-enable, a bus reset, and frames with no tick.
The Verilog testbench checks the gap between polls:
gap = 0;
for (i = 0; i < INTERVAL*6; i = i + 1) begin
frame;
gap = gap + 1;
if (poll_due) begin
check(gap == INTERVAL, "polls must be exactly INTERVAL frames apart");
polls = polls + 1;
gap = 0;
end
endThe SystemVerilog testbench checks every frame against an independent model of the schedule — not a second down-counter:
// ARCHITECTURALLY INDEPENDENT: it counts frames since the schedule started
// and predicts a poll whenever (frames - PHASE) is a multiple of INTERVAL.
// That is the SPECIFICATION of a periodic schedule, so it cannot reproduce
// an off-by-one in the DUT's reload.
int unsigned frames_since_start = 0;
function automatic logic expect_poll();
return (frames_since_start >= PHASE)
&& (((frames_since_start - PHASE) % INTERVAL) == 0);
endfunction
for (i = 0; i < INTERVAL*8; i++) begin
frame();
chk(poll_due == expect_poll(),
$sformatf("poll at frame %0d disagreed with the schedule model",
frames_since_start));
frames_since_start++;
endThe VHDL testbench uses assert with the same scenario:
-- steady state: one poll every INTERVAL frames
gap := 0;
for i in 0 to INTERVAL*6 - 1 loop
frame;
gap := gap + 1;
if poll_due = '1' then
chk(gap = INTERVAL, "polls must be exactly INTERVAL frames apart");
polls := polls + 1;
gap := 0;
end if;
end loop;
chk(polls = 7, "six further polls expected in INTERVAL*6 frames");10. Mutation Testing — The Same Defect in Two Languages
Five mutations, each applied identically to the Verilog and the SystemVerilog, and run against each language's own testbench.
| Mutation | What it does | Verilog TB | SystemVerilog TB |
|---|---|---|---|
P1 reload INTERVAL not INTERVAL−1 | period becomes INTERVAL + 1 | 6 errors | 14 errors |
P2 ignore PHASE, always start at 0 | §3's spread destroyed | 8 errors | 21 errors |
| P3 count clocks, not frames | drifts against the host | 5 errors | 18 errors |
P4 poll_due never cleared | a level, not a pulse | 70 errors | 75 errors |
| P5 disable does not disarm | a disabled endpoint keeps polling | 4 errors | 4 errors |
Every mutation is caught in both languages, which is the first result and the reassuring one: verification intent survived the language choice.
P5 is the one where both benches agree exactly — 4 errors each — and the reason is that both check it the same way: a direct, specification-level statement that a disabled endpoint never polls. Where the checks are equally abstract, the results are equal.
11. Assertions
Published for an SVA-capable simulator. Icarus Verilog does not support concurrent assertions, so these were reviewed rather than executed — §9's callout applies here too, and §12 explains what the procedural checks cover instead.
// S1 — SAFETY. A disarmed endpoint never polls.
// Temporal meaning: at every cycle, simultaneously.
// Bug caught: a schedule that free-runs while the endpoint is disabled.
// Vacuity: the antecedent is a bench-driven input, so a broken DUT cannot
// make it disappear.
property p_disabled_never_polls;
@(posedge clk) disable iff (!rst_n) (!ep_enabled) |-> !poll_due;
endproperty
a_disabled_never_polls: assert property (p_disabled_never_polls);
// S2 — SAFETY. A poll requires a frame boundary in the previous cycle.
// Bug caught: section 10's P3 -- a counter clocked by clk rather than by
// the frame, which drifts against the host's schedule.
// Vacuity: antecedent is poll_due, a DUT OUTPUT -- so a DUT that stops
// polling satisfies this vacuously. That is why P1 below exists.
property p_poll_needs_tick;
@(posedge clk) disable iff (!rst_n) poll_due |-> $past(frame_tick);
endproperty
a_poll_needs_tick: assert property (p_poll_needs_tick);
// S3 — SAFETY. poll_due is a pulse, never a level. Section 10's P4.
property p_pulse_only;
@(posedge clk) disable iff (!rst_n) poll_due |=> !poll_due;
endproperty
a_pulse_only: assert property (p_pulse_only);
// P1 — PROGRESS, and the answer to S2's vacuity risk. With the endpoint
// enabled and frames arriving, a poll occurs within a bounded window.
// BOUNDED deliberately: an unbounded `eventually` cannot fail in a finite
// simulation (Chapter 10.3 section 7).
// Vacuity: the antecedent is frame_tick -- a bench-driven input, NOT a DUT
// output -- so a DUT that stops polling cannot erase this property.
property p_poll_within_interval;
@(posedge clk) disable iff (!rst_n || !ep_enabled || bus_reset)
frame_tick |-> ##[0:INTERVAL*2] poll_due;
endproperty
a_poll_within_interval: assert property (p_poll_within_interval);
// COVER — prove the interesting states are reached at all.
c_poll_seen: cover property (@(posedge clk) poll_due);
c_reload: cover property (@(posedge clk)
poll_due ##1 (frames_left == CNT_W'(INTERVAL-1)));S2 and P1 are a deliberate pair, and the reason is the vacuity review. S2's antecedent is poll_due — a DUT output — so a design that never polls satisfies it perfectly. P1's antecedent is frame_tick, which the bench drives, so no DUT behaviour can make it vacuous. S2 constrains what a poll means; P1 constrains that polls happen. Neither alone is sufficient, which is why §10's P4 and P5 are caught by different checks.
12. Verification
This chapter's commit point is service came due exactly when the schedule says, and never otherwise.
Stimulus, identical across all three testbenches: reset; the phase delay before the first poll; steady state over INTERVAL × 6 frames; a single-cycle pulse check; disable, with INTERVAL × 2 frames confirming silence; re-enable; a bus reset mid-schedule, with the phase re-verified; and 20 clock cycles with no frame tick.
Observation. The due pulse, the armed flag, and the remaining count — in the SystemVerilog case against §9's independent schedule model, every frame.
Boundaries exercised: PHASE = 0 before the first poll; counter at 0 (terminal); counter at INTERVAL−1 (just reloaded); reset before operation; reset during operation; disable at a terminal count; and clock edges with no frame boundary.
What UVM would add here, and why it was not used. A UVM environment for this block would need a sequence item meaning a frame boundary, a driver that pulses one signal, a monitor that observes one output, and a scoreboard that reimplements §9's four-line model. The transaction abstraction and the pin abstraction are the same thing, which is the signal that UVM is the wrong tool — it adds a component hierarchy without adding an abstraction. A directed testbench plus SVA is the right weight, and Chapter 15.2 is where that stops being true.
Interval 4, phase 2
10 cycles13. Debugging: the Device That Is Polled Half as Often as It Asked
A device declares
bIntervalfor a 4 ms service interval. Measured on a bus analyser, it is polled every 5 ms. The device works; its data is simply older than intended. No error is reported anywhere.
What is the first thing to establish? Whether the host is slow or the device is counting. Chapter 10.4 §3 allows the host to poll more often than requested and says nothing about less — so a 5 ms actual interval against a 4 ms request is a genuine anomaly on one side or the other.
Where does 5 come from, given 4 was asked for? INTERVAL instead of INTERVAL − 1 — §10's P1. A counter reloaded with the interval rather than one less produces a period of interval + 1, which is exactly 5.
But the host built the schedule, not the device. True — and this is the useful part. If the device is not scheduling anything, the off-by-one is in the host controller's schedule walk, and the same arithmetic error produces the same symptom there.
So how do you attribute it? By first divergence:
| Layer | Observation | Tool |
|---|---|---|
| System | data is stale by one interval | application timing |
| USB-visible | tokens arrive every 5 frames | protocol analyser |
| Frame reference | count Chapter 11.4's SOF frame numbers between tokens | protocol analyser |
| Controller | the schedule entry's counter value per frame | host driver trace, or RTL waveform |
| RTL | the reload value at terminal count | first divergence |
The decisive observation is the third row and it is cheap: SOF packets carry a frame number (Chapter 11.4 §2), so the analyser can report the frame numbers of successive tokens rather than a time in milliseconds. Five frame numbers apart is unambiguous; 5 ms is a measurement.
And if the device is the one scheduling? Then its own tick is off, and the symptom is the opposite: the device's data is prepared 5 frames apart while being polled every 4 — so every fifth poll returns a report that has not been refreshed. The analyser shows polls at 4 and stale content, which is a completely different trace from polls at 5.
The signature to keep: a period one greater than requested is a terminal-count reload, and the frame numbers in the SOF stream tell you which side is counting wrong.
14. Common Misconceptions
15. Exercises
Trace. Given INTERVAL = 3, PHASE = 1, write the value of frames_left and poll_due for frames 0 through 9. Then do it again for PHASE = 0 and state which frames differ.
Verilog. Extend §5's module with a missed_poll output that asserts if a frame tick arrives while a previous poll_due has not yet been consumed. What new input does that require, and why can the block not infer it?
SystemVerilog. Rewrite §6's module so INTERVAL may be changed at run time by a SET_INTERFACE (Chapter 13.4). State what must happen to the in-flight counter, and defend the choice.
VHDL. Implement the same run-time-interval variant in VHDL. Which conversions become necessary that were not before?
Testbench. §9's Verilog bench checks the gap between polls. Write the specification-level check instead — a poll occurs exactly when (frame − PHASE) mod INTERVAL == 0 — in Verilog, and confirm it reports the same first divergence as the SystemVerilog bench does.
SVA. §11's S2 is vacuous under a DUT that never polls. Write one additional property that would catch a stuck-at-zero poll_due without referencing frame_tick, or argue that none exists.
Mutation. Predict, before running it, which of §12's stimulus phases catches a mutation that reloads with PHASE instead of INTERVAL − 1. Then reason about whether the Verilog bench or the SystemVerilog bench localises it better.
Debug. A device is polled at the right interval but every third report is stale. Using §13's layer table, state the first divergence and which tool shows it.
Architecture. A controller serves 16 interrupt endpoints. Compare one counter per endpoint against a single counter plus a 16-entry table of next-service frame numbers. State the gate cost, the per-frame work, and which scales.
16. Summary
Interrupt promises bounded latency, and a promise about time is implemented by something that counts. Chapter 10.4 built the endpoint's readiness; this chapter builds its timing.
The schedule is entirely host-side and never transmitted. A device declares bInterval and thereafter learns only that a token arrived — so a device that must prepare an answer, rather than merely hold one, has to count frames itself.
And it must count frames, not clocks, because the two oscillators are independent and Chapter 11.4's frame boundary is the only event both ends observe.
Phase is the part that looks optional and is not. Four endpoints of interval 4 at phase 0 produce a peak of four transactions in one frame; at phases 0–3 they produce a peak of one. The periodic budget is checked per frame against the peak, so phase decides whether a set of endpoints is admitted at all.
The hardware is one down-counter, and three of its details are the whole design: it advances on the frame, it reloads with INTERVAL − 1, and disable reloads the phase.
§10 applied five mutations identically to the Verilog and the SystemVerilog. All five were caught in both — verification intent survived the language. And the off-by-one was localised differently:
The Verilog bench reported the gap was wrong; the SystemVerilog bench reported frame 11 disagreed with the schedule model, one poll earlier. The difference was the abstraction the checker was written at, not the syntax it was written in — and the same model could have been written in Verilog.
Honest tooling note: the Verilog and SystemVerilog were compiled and simulated, both clean. The VHDL was reviewed, not run — no VHDL simulator is installed here, and reviewed is not verified.
17. What Comes Next
A schedule and a counter, and nothing has been said about what the report contains.
Chapter 15.2 is the keyboard — the canonical interrupt device, and a genuinely interesting hardware problem: a key matrix produces a set of pressed keys, and the report is a fixed eight bytes. Those two facts do not fit together, and the mechanism that reconciles them has a failure mode with its own name and its own reserved code.
It is also the first chapter in this module where the transaction the verification environment should reason about is not the same as the pins — which is where UVM starts to earn its place.
Browse the full path on the USB tutorials index.
Continue learning
Related tutorials
- Related topic
Interrupt Latency Requirements
bInterval is a request, not a contract, and it bounds one term of seven. A latency monitor in three HDLs, and the measurement showing 3000 randomised steps could not reach two of its own boundaries.
- Related topic
Interrupt Transfers
Nothing interrupts anything — the host polls. What the endpoint really buys is a bound on the gap between questions, and the device owes a sticky flag whose collision rule decides whether events survive.
- Related topic
Keyboards on USB
A key matrix produces a set; the boot report has six slots. The mechanism that reconciles them has its own reserved code — and the chapter where the verification transaction stops being the pins.
- Related topic
Mice on USB
A relative report cannot be resent, so the accumulator must saturate rather than wrap and must be cleared by the act of being read — and a real signedness bug the testbench caught on its first check.
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.
