Skip to content
VLSI Mentor

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 whereChapter
Have an answer readythe endpoint's state10.4 — the sticky pending flag
Know when service is duea periodic counterthis 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:

IntervalServiced in framesLoad
1every frameheavy, unavoidable
40, 4, 8, 12 …¼ of a frame's periodic budget
80, 8, 16 …⅛
320, 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:

Frame01234567
Endpoints serviced40004000

Peak load is four transactions in one frame and zero in the next three.

Placed with a phase offset — positions 0, 1, 2, 3:

Frame01234567
Endpoints serviced11111111

Same average, quarter the peak.

A block diagram comparing two placements of four interrupt endpoints, each with a service interval of four frames. On the left, all four endpoints are given phase zero, so all four are serviced in frame zero, frame four and frame eight, while frames one, two and three carry nothing; the peak load is four transactions in a single frame. On the right, the four endpoints are given phases zero, one, two and three, so exactly one endpoint is serviced in every frame; the average load is the same but the peak is one quarter. An annotation notes that the periodic bandwidth cap is applied per frame against the peak, so the left arrangement may be refused admission while the right is accepted.4 endpointsinterval 4 eachAll phase 0same framePhase 0,1,2,3spreadPeak 4 per frameavg 1 · peak 4Peak 1 per frameavg 1 · peak 1Budget checkper frame, on the peakplacedplacedmay failpasses12
Figure 1 — four endpoints of interval 4, placed two ways. The average load is identical; the peak differs by a factor of four. The periodic budget is checked against the peak, so the left arrangement can be refused admission while the right one is accepted, for the same set of devices.

4. The Hardware, Before Any Language

One endpoint's position in a periodic schedule is a down-counter clocked by the frame boundary.

ElementPurpose
A counter, ⌈log₂ INTERVAL⌉ bitsframes remaining until service
Load value INTERVAL − 1reload at terminal count
Initial value PHASE§3's spread
A terminal-count pulseservice is due now
An armed flagthe 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// 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
endmodule

Contract. 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
endmodule

What the language bought, concretely and not decoratively:

  • always_ff makes 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 Verilog PHASE[CNT_W-1:0] is a slice that silently truncates a too-large parameter.
  • $fatal at elaboration turns §4's PHASE < INTERVAL assumption from a comment into a build failure.
  • '0 is width-agnostic, so changing CNT_W cannot leave a stale literal behind.

7. VHDL

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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.

ConcernVerilogSystemVerilogVHDL
Register intentalways @(posedge) — conventionalways_ff — checkedprocess(clk, rst_n) + rising_edge
Counter typereg [N-1:0], arithmetic impliedlogic [N-1:0]unsigned — arithmetic is in the type
Port ↔ countersame objectsame objectexplicit conversion both ways
Width of a constantslice: PHASE[N-1:0]cast: CNT_W'(PHASE)to_unsigned(PHASE, CNT_W)
Parameter constraintcomment only$fatal at elaborationassert … severity failure
Zero literal{CNT_W{1'b0}}'0to_unsigned(0, CNT_W)
Async resetin the sensitivity listin the sensitivity listin 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    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
    end

The SystemVerilog testbench checks every frame against an independent model of the schedule — not a second down-counter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // 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++;
    end

The VHDL testbench uses assert with the same scenario:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    -- 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.

MutationWhat it doesVerilog TBSystemVerilog TB
P1 reload INTERVAL not INTERVAL−1period becomes INTERVAL + 16 errors14 errors
P2 ignore PHASE, always start at 0§3's spread destroyed8 errors21 errors
P3 count clocks, not framesdrifts against the host5 errors18 errors
P4 poll_due never cleareda level, not a pulse70 errors75 errors
P5 disable does not disarma disabled endpoint keeps polling4 errors4 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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 cycles
A waveform of the poll scheduler over ten frames with an interval of four and a phase of two. The counter enters frame zero holding two and frame one holding one, with no poll in either. In frame two the counter holds zero and the poll-due output asserts, after which the counter reloads to three. Frames three, four and five see the counter at three, two and one with no poll. In frame six the counter is again zero and a second poll fires, four frames after the first. Frames seven and eight follow with the counter at three and two. At frame eight the endpoint is disabled, which reloads the counter to the phase value of two, and no further polls occur in frames eight or nine.phase elapsed — first pollphase elapsed — first pollreload to INTERVAL−1, not INTERVALreload to INTERVAL−1, notINTERVALexactly 4 frames laterexactly 4 frames laterdisabled — reloads the phasedisabled — reloads thephaseframe0123456789frame_tickep_enabledframes_left2103210322poll_duet0t1t2t3t4t5t6t7t8t9
Figure 2 — ten frames of a scheduler with interval 4 and phase 2, every column taken from a simulation of §6's design. The counter shown is its value going into each frame; the poll fires on the frame in which it reaches zero, and the reload is to 3 — one less than the interval, because the terminal frame is itself part of the period. Controller-domain signals, not USB bus signalling.
A sequence diagram of an interrupt endpoint being serviced. The host's schedule reaches this endpoint's entry and issues an IN token. The device's endpoint already holds a prepared report, because its own frame counter fired earlier and triggered the work that produced it, so it can answer within the turnaround window. The device returns its data and the host acknowledges. Between this poll and the next, the device's frame counter reaches zero again and triggers the next round of work, so that another report is ready before the following poll arrives. An annotation notes that the device never receives the schedule and infers it only from the arrival of tokens.Work between polls, not during themHost scheduleEndpointDevice workcounter hits 0 —sample and preparea report is nowwaitingIN token — thisendpoint's turnDATA — alreadypreparedACK…the schedule movesto other entriescounter hits 0 again— prepare the nextIN tokenDATA — ready againthe device never sawthe schedule
Figure 3 — one service opportunity end to end. The device's own periodic work happens between polls, not in response to one, because the turnaround window is far too short to do anything in. The schedule exists only on the host side; the device infers it from the arrival of tokens.

13. Debugging: the Device That Is Polled Half as Often as It Asked

A device declares bInterval for 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:

LayerObservationTool
Systemdata is stale by one intervalapplication timing
USB-visibletokens arrive every 5 framesprotocol analyser
Frame referencecount Chapter 11.4's SOF frame numbers between tokensprotocol analyser
Controllerthe schedule entry's counter value per framehost driver trace, or RTL waveform
RTLthe reload value at terminal countfirst 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

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.