Skip to content
VLSI Mentor

USB · Module 31

“USB Devices Initiate Transfers”

A mouse appears to send, and the transfer type is literally called interrupt — so the belief has two strong supports. Its prediction is that a device with data can put a transaction on an idle bus, and a sixty-line block makes that impossible.

1. The Belief

"When a device has something to report, it sends it. That is what an interrupt transfer is — the device interrupts the host. Otherwise how would a mouse work?"

2. Why An Intelligent Engineer Believes It

Two supports, and the second is a naming accident that the specification is stuck with.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    THE OBSERVATION
    A mouse moves and the cursor moves. Nothing visible asked it to.
    Every user-facing description says the mouse "sends" its position,
    and every operating system exposes it as an event that arrives.

    THE NAME
    The transfer type is called an INTERRUPT transfer. In every other
    context an engineer meets that word -- a CPU, a peripheral, a
    signal line -- an interrupt is raised BY the thing that has news,
    and it arrives unbidden at the thing that must react.

3. The Prediction It Makes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    IF DEVICES INITIATED TRANSFERS, THEN:

    1  an idle bus with a device that has data would not stay idle

    2  a device would need a way to contend for the bus with other
       devices -- so there would be arbitration, collision detection or
       a grant mechanism somewhere in the protocol

    3  NAK would be meaningless. A device that speaks when it has
       something to say never needs to answer "not yet"

    4  the host would need to be ready to receive at any moment, rather
       than at moments it chose

    5  bandwidth could not be reserved at configuration time, because
       the host would not control when anything happened

Prediction 3 is the sharpest, and it is the one to hold on to: the existence of NAK is by itself almost a proof.

4. The Counterexample

Put an analyser on an idle bus with a mouse attached and stop moving the mouse. Then move it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    OBSERVED

    o  the bus is NOT idle. The host is issuing IN tokens to the
       mouse's endpoint at its configured interval, and the mouse is
       answering NAK to every one of them -- "nothing for you"

    o  when the mouse moves, the NEXT token gets a data packet. Not the
       instant of the movement: the next token

    o  the latency between moving the mouse and the data appearing is
       therefore bounded by the POLLING INTERVAL, and is visible as
       such on the trace

    o  nothing the mouse does changes when the tokens arrive

Prediction 1 fails on the first line. Prediction 3 fails on the second: NAK is not an error and not an edge case — on an idle mouse it is the overwhelmingly common answer, and it exists precisely because the host asks whether or not there is anything to collect.

What actually happens between a device event and the data appearing

A sequence diagram showing that a device event produces no bus activity until the host polls. The host issues an IN token to the device, which answers NAK because it has nothing. Some time later a physical event occurs at the device and data becomes pending, but no message appears on the bus. The host then issues its next scheduled IN token, and only then does the device answer with a data packet, which the host acknowledges.A device event, and the token that collects itHostDeviceThe function (sensor,button, encoder)IN token (scheduled)NAK -- nothingpendingan event happenspending = 1. NOTHINGON THE BUS.IN token (nextscheduled)DATAACK
Read downward; time flows down the page. The device event at the top produces no bus activity at all — the lifeline between it and the next token is empty, and that emptiness is the whole point. Every transaction on this diagram is started by the host. The device's only choices are which answer to give, and it has exactly two.

5. The Corrected Model

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    HAVING WORK                is a property of the DEVICE
    STARTING A TRANSACTION     is an authority held by the HOST

    They are different things, and the device has exactly one of them.

    What a device controls:
      o  WHETHER it has data          -- the pending bit
      o  WHICH answer it gives        -- DATA, NAK or STALL
      o  HOW FAST it can be polled    -- negotiated in the descriptors
                                         at configuration time

    What a device does not control:
      o  WHEN anything happens on the bus

That last line is what makes USB's scheduling possible at all. A host that controls every transaction's timing can reserve bandwidth, guarantee service intervals and plan a frame — which is 31.4's subject. A bus where devices spoke when they liked could do none of it.

6. The Hardware Contract

Small enough to hold in your head, and its job is to make the distinction physical.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    PURPOSE     hold the device's readiness, and answer the host when
                asked. Nothing else.

    INPUTS      clk, rst_n
                ev, ev_data          the function produced a byte
                token_valid          the host addressed this endpoint
                token_is_in          IN (host wants data) or OUT

    OUTPUTS     resp_valid           we are driving a response
                resp_is_data         DATA if 1, a handshake if 0
                resp_data
                pending              observable device state
                five counters        observation only

    AUTHORITATIVE STATE
                pend_r   one bit: is there something to give
                data_r   the byte

    DERIVED     resp_valid   = token_valid         <-- THE LINE
                resp_is_data = token_valid && token_is_in && pend_r

    RESET       rst_n clears everything

    PRIORITY, SAME CYCLE
                an event and a token together: the device answers with
                the state it HAD when the token arrived, and the event
                is kept. Data produced after the request was sampled
                does not appear in the response to that request.

    LATENCY     the response is combinational in the token; the state
                is registered.

    BOUNDARY    a token with nothing pending is answered, with NAK.
                A token while pending is answered with data and
                consumes it. An event and a consume together leave the
                endpoint pending with the NEW byte.

    ASSUMPTIONS the layer below delivers exactly one token per cycle
                and the response is consumed in the same cycle.

    OMISSIONS   the PHY, the serial interface engine, packet framing,
                CRC, the data toggle (29.5), endpoint 0, descriptors,
                and any notion of a real token on a real wire.

    MISCONCEPTION DEMONSTRATED
                "a device with data can start a transaction"

usb_ep_pending.v — the design, Verilog-2005

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// =====================================================================
//  usb_ep_pending -- the device side of "who starts a transaction".
//
//  CLASSIFICATION: simplified synthesisable teaching RTL, built for one
//  purpose: to make the difference between HAVING WORK and BEING ABLE
//  TO START A TRANSACTION a fact about hardware rather than a sentence
//  in a tutorial.
//
//  It is NOT a USB device controller. There is no PHY, no serial
//  interface engine, no packet framing, no CRC, no data toggle (29.5
//  owns that), no endpoint 0 machine and no descriptors. Everything is
//  reduced to two questions asked once per cycle:
//
//      does this endpoint have something to say?      ev / pending
//      has the host addressed it?                     token_valid
//
//  THE INVARIANT THIS MODULE EXISTS TO MAKE VISIBLE
//  ------------------------------------------------
//      resp_valid |-> token_valid
//
//  The device puts NOTHING on the bus unless the host addressed it.
//  Not when it has data. Not when it has a lot of data. Not when the
//  bus is idle. The `pending` bit and the `resp_valid` output are wired
//  to different things ON PURPOSE, and n_unsolicited counts the cycles
//  in which that separation was violated -- which, in a correct build,
//  is structurally impossible and therefore always zero.
//
//  A zero that is structurally guaranteed looks like a useless counter.
//  It is the whole point: section 8 turns the misconception into a
//  one-line mutation and the counter is what fires.
// =====================================================================
module usb_ep_pending (
  input  wire       clk,
  input  wire       rst_n,

  // ---- the device side: work arrives here ----
  // One cycle: firmware (or the function logic) has produced a byte it
  // would like the host to collect.
  input  wire       ev,
  input  wire [7:0] ev_data,

  // ---- the host side: permission arrives here ----
  // One cycle: the host has issued a token addressed to this endpoint.
  input  wire       token_valid,
  // 1 = IN  (the host is asking this endpoint for data)
  // 0 = OUT (the host is delivering data to this endpoint)
  input  wire       token_is_in,

  // ---- what this endpoint puts on the bus ----
  output wire       resp_valid,    // we are driving a response, this cycle
  output wire       resp_is_data,  // DATA if 1, a handshake if 0
  output wire [7:0] resp_data,

  // ---- observable device state ----
  output wire       pending,

  output wire [15:0] n_events,
  output wire [15:0] n_tokens,
  output wire [15:0] n_data,
  output wire [15:0] n_nak,
  // Responses driven without a token. Structurally impossible here.
  output wire [15:0] n_unsolicited
);

  reg       pend_r;
  reg [7:0] data_r;
  reg [15:0] c_ev, c_tok, c_data, c_nak, c_uns;

  // The response is a function of the TOKEN, and of the endpoint state
  // only insofar as it decides WHAT to answer -- never WHETHER to.
  //
  // Read the two lines below as the architecture: the first says the
  // host decides that something happens; the second says the device
  // decides what it is.
  assign resp_valid   = token_valid;
  assign resp_is_data = token_valid && token_is_in && pend_r;
  assign resp_data    = data_r;
  assign pending      = pend_r;

  // The device answers with the state it HAD when the token arrived.
  // An event landing in the same cycle as an IN token does not rescue
  // it: the data was not ready when the host asked, so the honest
  // answer is NAK and the event is kept for the next token. Making the
  // event win instead would mean a byte produced after the request was
  // sampled appearing in the response to that request.
  wire take_in  = token_valid &&  token_is_in;
  wire take_out = token_valid && !token_is_in;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      pend_r <= 1'b0;
      data_r <= 8'd0;
      c_ev   <= 16'd0;
      c_tok  <= 16'd0;
      c_data <= 16'd0;
      c_nak  <= 16'd0;
      c_uns  <= 16'd0;
    end else begin
      // An IN token that found data consumes it. The event, if one is
      // arriving this cycle, is captured regardless -- so a consume and
      // a produce in the same cycle leave the endpoint pending again,
      // with the NEW byte.
      if (ev) begin
        pend_r <= 1'b1;
        data_r <= ev_data;
        c_ev   <= c_ev + 16'd1;
      end else if (take_in && pend_r) begin
        pend_r <= 1'b0;
      end

      if (token_valid)              c_tok  <= c_tok  + 16'd1;
      if (take_in &&  pend_r)       c_data <= c_data + 16'd1;
      if (take_in && !pend_r)       c_nak  <= c_nak  + 16'd1;
      if (resp_valid && !token_valid) c_uns <= c_uns + 16'd1;
      if (take_out)                 ;  // accepted; 29.5 owns what happens next
    end
  end

  assign n_events      = c_ev;
  assign n_tokens      = c_tok;
  assign n_data        = c_data;
  assign n_nak         = c_nak;
  assign n_unsolicited = c_uns;

endmodule

7. The Testbench, And Its First Phase

Phase 1 is unlike any other phase in this curriculum and it is why the specimen exists. It does not compare the design to a reference model. It states the claim and requires it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    fill the endpoint with sixteen bytes of work
    then do nothing for two hundred cycles
    and require, EVERY CYCLE, that the bus stays silent
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  task intent_check; begin
    bump;
    if (resp_valid && !token_valid) begin
      err = err + 1;
      $display("  ** INTENT VIOLATED: the endpoint drove the bus with no token ...");
    end
  end endtask

That check consults no model, which is the point. 30.2 established why: a reference model written by the design's author from the design's own sentence is one artefact wearing two hats, and 30.4's specimen agreed with its own bug for 39,108 checks. A claim stated in its own terms can disagree with both.

tb_usb_ep_pending.v — the testbench, Verilog-2005

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// =====================================================================
//  tb_usb_ep_pending -- Verilog-2005 testbench for usb_ep_pending.
//
//  PHASE 1 is unlike any other phase in this curriculum and it is the
//  reason the module exists. It does not compare the design to a model.
//  It states the architectural claim directly and requires it:
//
//      however much work this endpoint has, and for however long,
//      NOTHING APPEARS ON THE BUS until the host addresses it.
//
//  That check cannot be satisfied by a reference model that shares the
//  design's structure, because it does not consult a model at all. 30.2
//  established why that matters: a model written by the design's author
//  from the design's sentence is one artefact wearing two hats, and
//  30.4's specimen agreed with its own bug for 39,108 checks.
//
//  PHASES
//    1 INTENT      the silence claim, stated and required directly
//    2 EXHAUSTIVE  16 combinations: 4 binary axes, all reachable
//    3 SCENARIO    the named cases, including the same-cycle collision
//    4 RANDOM      supplementary, and audited for what it reaches
// =====================================================================
`timescale 1ns/1ps

module tb_usb_ep_pending;

  reg        clk = 1'b0;
  reg        rst_n;
  reg        ev;
  reg  [7:0] ev_data;
  reg        token_valid, token_is_in;

  wire       resp_valid, resp_is_data, pending;
  wire [7:0] resp_data;
  wire [15:0] n_events, n_tokens, n_data, n_nak, n_unsolicited;

  usb_ep_pending dut (
    .clk(clk), .rst_n(rst_n),
    .ev(ev), .ev_data(ev_data),
    .token_valid(token_valid), .token_is_in(token_is_in),
    .resp_valid(resp_valid), .resp_is_data(resp_is_data),
    .resp_data(resp_data), .pending(pending),
    .n_events(n_events), .n_tokens(n_tokens), .n_data(n_data),
    .n_nak(n_nak), .n_unsolicited(n_unsolicited)
  );

  always #5 clk = ~clk;

  // ---- the independent reference model ---------------------------
  // Written from the ARCHITECTURAL RULES, in the order a specification
  // would state them, not from the RTL's expressions:
  //   R1  a response happens if and only if the host issued a token
  //   R2  an IN token is answered with DATA if the endpoint held data
  //       WHEN THE TOKEN ARRIVED, and with NAK otherwise
  //   R3  an event makes the endpoint pending and replaces its byte
  //   R4  a consumed byte clears pending unless an event replaces it
  reg        rm_pend;
  reg [7:0]  rm_data;
  reg [15:0] rm_ev, rm_tok, rm_dat, rm_nak, rm_uns;

  integer chk_dir, chk_rnd, err, in_random;
  integer m_ev, m_tok, m_in, m_out, m_data_resp, m_nak_resp,
          m_collide, m_pend_cycles, m_silent_cycles, m_setupfail;
  integer i, j, a, b, c, d;

  task bump; begin
    if (in_random) chk_rnd = chk_rnd + 1; else chk_dir = chk_dir + 1;
  end endtask

  task ck;
    input [255:0] what;
    input [31:0]  got;
    input [31:0]  exp;
    begin
      bump;
      if (got !== exp) begin
        err = err + 1;
        if (!in_random && err <= 40)
          $display("  ** %0s: got %0d expected %0d  (t=%0t)", what, got, exp, $time);
      end
    end
  endtask

  task ref_step;
    reg t_in, t_out, consumed;
    begin
      if (!rst_n) begin
        rm_pend = 0; rm_data = 0;
        rm_ev = 0; rm_tok = 0; rm_dat = 0; rm_nak = 0; rm_uns = 0;
      end else begin
        t_in  = token_valid &&  token_is_in;
        t_out = token_valid && !token_is_in;
        // R2 uses the value of pending BEFORE this edge
        consumed = t_in && rm_pend;
        if (token_valid) rm_tok = rm_tok + 1;
        if (consumed)    rm_dat = rm_dat + 1;
        if (t_in && !rm_pend) rm_nak = rm_nak + 1;
        // R3 beats R4: an event in the same cycle as a consume leaves
        // the endpoint pending with the NEW byte
        if (ev) begin
          rm_pend = 1; rm_data = ev_data; rm_ev = rm_ev + 1;
        end else if (consumed) begin
          rm_pend = 0;
        end
        // tallies
        if (ev)       m_ev   = m_ev   + 1;
        if (t_in)     m_in   = m_in   + 1;
        if (t_out)    m_out  = m_out  + 1;
        if (consumed) m_data_resp = m_data_resp + 1;
        if (t_in && !consumed) m_nak_resp = m_nak_resp + 1;
        if (ev && token_valid) m_collide = m_collide + 1;
      end
    end
  endtask

  // What the endpoint put on the bus THIS cycle, captured while the
  // token is still driven. The scenarios inspect these rather than the
  // live outputs, which are only meaningful before the edge.
  reg       cap_valid, cap_isdata;
  reg [7:0] cap_data;

  // Pre-edge: the response, compared against the model's state AS IT IS
  // WHEN THE TOKEN ARRIVES -- which is the architectural rule.
  task cmp_comb;
    reg exp_valid, exp_isdata;
    begin
      exp_valid  = token_valid;
      exp_isdata = token_valid && token_is_in && rm_pend;
      cap_valid  = resp_valid;
      cap_isdata = resp_is_data;
      cap_data   = resp_data;
      ck("resp_valid",   {31'd0, resp_valid},   {31'd0, exp_valid});
      ck("resp_is_data", {31'd0, resp_is_data}, {31'd0, exp_isdata});
      ck("pending",      {31'd0, pending},      {31'd0, rm_pend});
      if (exp_isdata)
        ck("resp_data",  {24'd0, resp_data},    {24'd0, rm_data});
      else bump;
      if (rm_pend)      m_pend_cycles   = m_pend_cycles + 1;
      if (!token_valid) m_silent_cycles = m_silent_cycles + 1;
    end
  endtask

  // Post-edge: the registered tallies.
  task cmp_regs; begin
    ck("n_events",     {16'd0, n_events},     {16'd0, rm_ev});
    ck("n_tokens",     {16'd0, n_tokens},     {16'd0, rm_tok});
    ck("n_data",       {16'd0, n_data},       {16'd0, rm_dat});
    ck("n_nak",        {16'd0, n_nak},        {16'd0, rm_nak});
    ck("n_unsolicited",{16'd0, n_unsolicited},32'd0);
  end endtask

  // The INTENT check, run every cycle of every phase. It consults no
  // model: it is the architectural claim, asserted directly.
  task intent_check; begin
    bump;
    if (resp_valid && !token_valid) begin
      err = err + 1;
      $display("  ** INTENT VIOLATED: the endpoint drove the bus with no token (t=%0t, pending=%0d)",
               $time, pending);
    end
  end endtask

  task step; begin
    #1;
    cmp_comb;
    intent_check;
    @(posedge clk);
    ref_step;
    #1;
    cmp_regs;
    ev = 0; ev_data = 0; token_valid = 0; token_is_in = 0;
  end endtask

  task idle; begin step; end endtask

  task hard_reset; begin
    rst_n = 0; ev = 0; ev_data = 0; token_valid = 0; token_is_in = 0;
    repeat (3) begin @(posedge clk); ref_step; end
    #1; rst_n = 1;
    @(posedge clk); ref_step; #1; cmp_regs;
  end endtask

  task device_event; input [7:0] v;
    begin ev = 1; ev_data = v; step; end
  endtask
  task in_token;  begin token_valid = 1; token_is_in = 1; step; end endtask
  task out_token; begin token_valid = 1; token_is_in = 0; step; end endtask

  // -----------------------------------------------------------------
  //  PHASE 1 -- THE SILENCE CLAIM.
  //
  //  Fill the endpoint with work and then do nothing for a long time.
  //  If the misconception were true -- if a device with data could
  //  start a transaction -- something would appear. Nothing does, and
  //  this phase requires it every cycle rather than inferring it.
  // -----------------------------------------------------------------
  integer silent_run;

  task phase_intent;
    begin
      hard_reset;
      silent_run = 0;
      // sixteen events, no tokens at all
      for (i = 0; i < 16; i = i + 1) begin
        device_event(8'hA0 + i[7:0]);
        ck("still pending", {31'd0, pending}, 32'd1);
        ck("and still silent", {31'd0, cap_valid}, 32'd0);
      end
      // then two hundred idle cycles with the endpoint full
      for (i = 0; i < 200; i = i + 1) begin
        idle;
        ck("silent while pending", {31'd0, cap_valid}, 32'd0);
        silent_run = silent_run + 1;
      end
      ck("nothing unsolicited", {16'd0, n_unsolicited}, 32'd0);
      ck("no data was sent",    {16'd0, n_data},        32'd0);
      ck("no tokens arrived",   {16'd0, n_tokens},      32'd0);
      // and the moment the host asks, the data is there
      in_token;
      ck("now it answers",      {31'd0, cap_valid},    32'd1);
      ck("and it is DATA",      {31'd0, cap_isdata},  32'd1);
      ck("the newest byte",     {24'd0, cap_data},     {24'd0, 8'hA0 + 8'd15});
      // The scenario must have HAPPENED: 216 cycles of held work.
      bump;
      if (silent_run < 200) begin
        err = err + 1;
        $display("  ** intent: only %0d silent cycles", silent_run);
      end
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 2 -- the exhaustive sweep.
  //
  //  AXES, named so that what is absent is visible:
  //    pending before the edge     0, 1                        2
  //    a device event this cycle   0, 1                        2
  //    a token this cycle          0, 1                        2
  //    token direction             OUT, IN                     2
  //    ------------------------------------------------------------
  //                                           2^4 =           16
  //
  //  All sixteen are reachable: the four inputs come from two agents
  //  that do not constrain each other, which is the architectural fact
  //  the chapter is about.
  //
  //  WHAT THIS SWEEP DOES NOT CONTAIN: history longer than one edge.
  //  Two tokens in a row, and an event between two tokens, are phase 3.
  //  An exhaustive sweep is exhaustive over the axes it has -- 30.2 §10.
  // -----------------------------------------------------------------
  task setup_pending;
    input want;
    begin
      hard_reset;
      if (want) begin
        device_event(8'h5A);
        bump;
        if (pending !== 1'b1) begin
          err = err + 1; m_setupfail = m_setupfail + 1;
          $display("  ** setup: pending was not established");
        end
      end else begin
        bump;
        if (pending !== 1'b0) begin
          err = err + 1; m_setupfail = m_setupfail + 1;
          $display("  ** setup: pending was not clear");
        end
      end
    end
  endtask

  task phase_sweep;
    begin
      for (a = 0; a < 2; a = a + 1)     // pending
      for (b = 0; b < 2; b = b + 1)     // event
      for (c = 0; c < 2; c = c + 1)     // token
      for (d = 0; d < 2; d = d + 1)     // direction
      begin
        setup_pending(a[0]);
        ev          = b[0];
        ev_data     = 8'hC0 + {4'd0, a[0], b[0], c[0], d[0]};
        token_valid = c[0];
        token_is_in = d[0];
        step;
        idle;
      end
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 3 -- the named scenarios.
  // -----------------------------------------------------------------
  task phase_scenarios;
    begin
      // S1 a token with nothing pending is answered, and answered NAK.
      //    The misconception has no account of this: if devices spoke
      //    when they had something to say, there would be no reason to
      //    have a way of saying "not yet".
      hard_reset;
      in_token;
      ck("S1 the device answered",   {31'd0, cap_valid},   32'd1);
      ck("S1 but not with data",     {31'd0, cap_isdata}, 32'd0);
      ck("S1 counted as a NAK",      {16'd0, n_nak},        32'd1);

      // S2 an event and an IN token in the SAME cycle. The device
      //    answers with the state it had when the token arrived, so the
      //    answer is NAK -- and the byte is kept for the next token.
      hard_reset;
      ev = 1; ev_data = 8'h77; token_valid = 1; token_is_in = 1; step;
      ck("S2 answered NAK",          {31'd0, cap_isdata}, 32'd0);
      ck("S2 the event was kept",    {31'd0, pending},      32'd1);
      in_token;
      ck("S2 next token gets data",  {31'd0, cap_isdata}, 32'd1);
      ck("S2 and it is the byte",    {24'd0, cap_data},    {24'd0, 8'h77});

      // S3 back-to-back IN tokens: the first gets the data, the second
      //    gets a NAK. The host asks twice; the device has one byte.
      hard_reset;
      device_event(8'h11);
      in_token;
      ck("S3 first token: data",     {31'd0, cap_isdata}, 32'd1);
      in_token;
      ck("S3 second token: NAK",     {31'd0, cap_isdata}, 32'd0);
      ck("S3 one data, one NAK",     {16'd0, n_data + n_nak}, 32'd2);

      // S4 consume and produce in the same cycle: the endpoint is
      //    pending again immediately, with the NEW byte.
      hard_reset;
      device_event(8'h22);
      ev = 1; ev_data = 8'h33; token_valid = 1; token_is_in = 1; step;
      ck("S4 data was sent",         {31'd0, cap_isdata}, 32'd1);
      ck("S4 still pending",         {31'd0, pending},      32'd1);
      in_token;
      ck("S4 the new byte",          {24'd0, cap_data},    {24'd0, 8'h33});

      // S5 an OUT token is also the host's move. The direction changes
      //    who carries the data; it does not change who starts.
      hard_reset;
      out_token;
      ck("S5 the device answered",   {31'd0, cap_valid},   32'd1);
      ck("S5 not an IN response",    {31'd0, cap_isdata}, 32'd0);
      ck("S5 no NAK counted",        {16'd0, n_nak},        32'd0);

      // S6 a long run of events with no tokens, then one token.
      //    Sixty-four bytes of work produce exactly zero bus activity.
      hard_reset;
      for (i = 0; i < 64; i = i + 1) device_event(8'h40 + i[7:0]);
      ck("S6 64 events",             {16'd0, n_events},     32'd64);
      ck("S6 zero bus activity",     {16'd0, n_tokens},     32'd0);
      ck("S6 nothing unsolicited",   {16'd0, n_unsolicited},32'd0);
      in_token;
      ck("S6 one token, one answer", {16'd0, n_data},       32'd1);
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 4 -- random, audited.
  // -----------------------------------------------------------------
  task phase_random;
    integer r;
    begin
      in_random = 1;
      hard_reset;
      for (j = 0; j < 4000; j = j + 1) begin
        r = {$random} % 100;
        if (r < 30) begin
          device_event({$random} % 256);
        end else if (r < 55) begin
          in_token;
        end else if (r < 65) begin
          out_token;
        end else if (r < 78) begin
          // the same-cycle collision, steered: unsteered, an event and a
          // token landing together is rare, and it is the case the
          // architecture has an opinion about
          ev = 1; ev_data = {$random} % 256;
          token_valid = 1; token_is_in = (({$random} % 100) < 70);
          step;
        end else begin
          idle;
        end
      end
      in_random = 0;
    end
  endtask

  initial begin
    chk_dir = 0; chk_rnd = 0; err = 0; in_random = 0;
    m_ev=0; m_tok=0; m_in=0; m_out=0; m_data_resp=0; m_nak_resp=0;
    m_collide=0; m_pend_cycles=0; m_silent_cycles=0; m_setupfail=0;

    phase_intent;
    $display("  phase 1 intent      : %0d checks, %0d errors  (%0d silent cycles while pending)",
             chk_dir, err, silent_run);
    phase_sweep;
    $display("  phase 2 exhaustive  : %0d checks, %0d errors  (16 combinations)", chk_dir, err);
    phase_scenarios;
    $display("  phase 3 scenarios   : %0d checks, %0d errors", chk_dir, err);
    $display("  ---- DIRECTED-ONLY  : %0d checks, %0d errors ----", chk_dir, err);
    phase_random;

    $display("");
    $display("  measured reachability (all phases)");
    $display("    device events .......... %0d", m_ev);
    $display("    IN tokens .............. %0d", m_in);
    $display("    OUT tokens ............. %0d", m_out);
    $display("    answered with DATA ..... %0d", m_data_resp);
    $display("    answered with NAK ...... %0d", m_nak_resp);
    $display("    event+token same cycle . %0d", m_collide);
    $display("    cycles holding work .... %0d", m_pend_cycles);
    $display("    cycles with no token ... %0d", m_silent_cycles);
    $display("    setup failures ......... %0d", m_setupfail);
    $display("");
    $display("  directed checks ........ %0d", chk_dir);
    $display("  random checks .......... %0d", chk_rnd);
    $display("  TOTAL checks ........... %0d", chk_dir + chk_rnd);
    $display("  ERRORS ................. %0d", err);
    if (err == 0) $display("  PASS"); else $display("  FAIL");
    $finish;
  end

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                          VERILOG   SYSTEMVERILOG   VHDL-2008
    phase 1 intent          2,414         2,414        2,414
    phase 2 exhaustive      2,910         2,910        2,910
    phase 3 scenarios       3,710         3,710        3,710
    ---- DIRECTED           3,710         3,710        3,710
    errors                      0             0            0
    TOTAL                  43,715        43,715       43,715

    measured reachability, Verilog run
      silent cycles while holding work ...... 200
      device events ....................... 1,798
      IN tokens ........................... 1,368
      OUT tokens ............................ 574
      answered with DATA .................... 877
      answered with NAK ..................... 491
      event and token in the same cycle ..... 535
      cycles holding work ................. 2,821
      cycles with no token ................ 2,390
      responses with no token ................. 0

Two of those rows carry the argument. 2,390 cycles with no token, and 2,821 cycles holding work — overlapping heavily, and zero responses in any of them. And 491 NAKs, which is the answer prediction 3 said would not need to exist.

The exhaustive sweep, and what it does not contain

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    AXES
      pending before the edge     0, 1                      2
      a device event this cycle   0, 1                      2
      a token this cycle          0, 1                      2
      token direction             OUT, IN                   2
      ------------------------------------------------------
                                          2^4 =            16

All sixteen are reachable, and the reason is itself the chapter's point: the four inputs come from two agents that do not constrain each other. What the sweep does not contain is history — two tokens in a row, an event between two tokens. Those are phase 3. An exhaustive sweep is exhaustive over the axes it has, which 30.2 §10 measured the hard way.

8. SystemVerilog

usb_ep_pending_sv.sv — the design, SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// =====================================================================
//  usb_ep_pending_sv -- the same contract in SystemVerilog. Same ports,
//  same rule, same reset, same latency.
//
//  The reason this file exists in a MISCONCEPTION chapter is that the
//  invariant can be written down as an assertion here, in one line, in
//  the language of the architecture:
//
//      resp_valid |-> token_valid
//
//  A belief that a device can start a transaction is, in this module,
//  a property that fails. That is a stronger statement than any amount
//  of prose, and it is why the SystemVerilog version carries the SVA.
//
//  ------------------------------------------------------------------
//  The device side of "who starts a transaction".
//
//  CLASSIFICATION: simplified synthesisable teaching RTL, built for one
//  purpose: to make the difference between HAVING WORK and BEING ABLE
//  TO START A TRANSACTION a fact about hardware rather than a sentence
//  in a tutorial.
//
//  It is NOT a USB device controller. There is no PHY, no serial
//  interface engine, no packet framing, no CRC, no data toggle (29.5
//  owns that), no endpoint 0 machine and no descriptors. Everything is
//  reduced to two questions asked once per cycle:
//
//      does this endpoint have something to say?      ev / pending
//      has the host addressed it?                     token_valid
//
//  THE INVARIANT THIS MODULE EXISTS TO MAKE VISIBLE
//  ------------------------------------------------
//      resp_valid |-> token_valid
//
//  The device puts NOTHING on the bus unless the host addressed it.
//  Not when it has data. Not when it has a lot of data. Not when the
//  bus is idle. The `pending` bit and the `resp_valid` output are wired
//  to different things ON PURPOSE, and n_unsolicited counts the cycles
//  in which that separation was violated -- which, in a correct build,
//  is structurally impossible and therefore always zero.
//
//  A zero that is structurally guaranteed looks like a useless counter.
//  It is the whole point: section 8 turns the misconception into a
//  one-line mutation and the counter is what fires.
// =====================================================================
module usb_ep_pending_sv (
  input  logic       clk,
  input  logic       rst_n,

  // ---- the device side: work arrives here ----
  // One cycle: firmware (or the function logic) has produced a byte it
  // would like the host to collect.
  input  logic       ev,
  input  logic [7:0] ev_data,

  // ---- the host side: permission arrives here ----
  // One cycle: the host has issued a token addressed to this endpoint.
  input  logic       token_valid,
  // 1 = IN  (the host is asking this endpoint for data)
  // 0 = OUT (the host is delivering data to this endpoint)
  input  logic       token_is_in,

  // ---- what this endpoint puts on the bus ----
  output logic       resp_valid,    // we are driving a response, this cycle
  output logic       resp_is_data,  // DATA if 1, a handshake if 0
  output logic [7:0] resp_data,

  // ---- observable device state ----
  output logic       pending,

  output logic [15:0] n_events,
  output logic [15:0] n_tokens,
  output logic [15:0] n_data,
  output logic [15:0] n_nak,
  // Responses driven without a token. Structurally impossible here.
  output logic [15:0] n_unsolicited
);

  logic       pend_r;
  logic [7:0] data_r;
  logic [15:0] c_ev, c_tok, c_data, c_nak, c_uns;

  // The response is a function of the TOKEN, and of the endpoint state
  // only insofar as it decides WHAT to answer -- never WHETHER to.
  //
  // Read the two lines below as the architecture: the first says the
  // host decides that something happens; the second says the device
  // decides what it is.
  assign resp_valid   = token_valid;
  assign resp_is_data = token_valid && token_is_in && pend_r;
  assign resp_data    = data_r;
  assign pending      = pend_r;

  // The device answers with the state it HAD when the token arrived.
  // An event landing in the same cycle as an IN token does not rescue
  // it: the data was not ready when the host asked, so the honest
  // answer is NAK and the event is kept for the next token. Making the
  // event win instead would mean a byte produced after the request was
  // sampled appearing in the response to that request.
  logic take_in, take_out;
  assign take_in  = token_valid &&  token_is_in;
  assign take_out = token_valid && !token_is_in;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      pend_r <= 1'b0;
      data_r <= 8'd0;
      c_ev   <= 16'd0;
      c_tok  <= 16'd0;
      c_data <= 16'd0;
      c_nak  <= 16'd0;
      c_uns  <= 16'd0;
    end else begin
      // An IN token that found data consumes it. The event, if one is
      // arriving this cycle, is captured regardless -- so a consume and
      // a produce in the same cycle leave the endpoint pending again,
      // with the NEW byte.
      if (ev) begin
        pend_r <= 1'b1;
        data_r <= ev_data;
        c_ev   <= c_ev + 16'd1;
      end else if (take_in && pend_r) begin
        pend_r <= 1'b0;
      end

      if (token_valid)              c_tok  <= c_tok  + 16'd1;
      if (take_in &&  pend_r)       c_data <= c_data + 16'd1;
      if (take_in && !pend_r)       c_nak  <= c_nak  + 16'd1;
      if (resp_valid && !token_valid) c_uns <= c_uns + 16'd1;
      // an OUT token is accepted; 29.5 owns what happens to the byte
    end
  end

  assign n_events      = c_ev;
  assign n_tokens      = c_tok;
  assign n_data        = c_data;
  assign n_nak         = c_nak;
  assign n_unsolicited = c_uns;


`ifdef SVA_ON
  // ---------------------------------------------------------------
  //  The misconception, and its refutation, as properties. Icarus
  //  Verilog 13.0 rejects concurrent assertions, so under Icarus each
  //  is enforced by the named procedural check in the testbench.
  // ---------------------------------------------------------------

  // THE ONE. If this fails, devices initiate transactions. It is the
  // entire chapter in one line, and mutation P-M1 is written to break
  // exactly it.
  property p_no_unsolicited;
    @(posedge clk) disable iff (!rst_n) resp_valid |-> token_valid;
  endproperty
  a_no_unsolicited: assert property (p_no_unsolicited);

  // SAFETY. Data is only ever offered when there is data. The device
  // decides WHAT, and this is the what.
  property p_data_needs_pending;
    @(posedge clk) disable iff (!rst_n) resp_is_data |-> (pend_r && token_is_in);
  endproperty
  a_data_needs_pending: assert property (p_data_needs_pending);

  // SAFETY. The pending bit changes for exactly two reasons, and a
  // token that found nothing is not one of them.
  property p_pending_changes_for_a_reason;
    @(posedge clk) disable iff (!rst_n)
      ##1 (pend_r != $past(pend_r)) |-> ($past(ev) || $past(take_in && pend_r));
  endproperty
  a_pending_changes_for_a_reason: assert property (p_pending_changes_for_a_reason);

  // PROGRESS. A byte the endpoint is holding IS delivered on the next
  // IN token. The device never withholds -- it only ever waits.
  //
  // Note the antecedent: it requires the host to ask. An unconditional
  // progress claim would be FALSE for a correct design, because a
  // device that is never polled correctly never sends, and a property
  // that blames the device for the host's silence would be switched off
  // within a week.
  property p_held_data_is_delivered;
    @(posedge clk) disable iff (!rst_n)
      (pend_r && token_valid && token_is_in) |-> resp_is_data;
  endproperty
  a_held_data_is_delivered: assert property (p_held_data_is_delivered);

  // COVER, so none of the above can pass by never happening.
  c_pending_idle:  cover property (@(posedge clk) pend_r && !token_valid);
  c_nak:           cover property (@(posedge clk) token_valid && token_is_in && !pend_r);
  c_data:          cover property (@(posedge clk) resp_is_data);
  c_out:           cover property (@(posedge clk) take_out);
  c_collide:       cover property (@(posedge clk) ev && token_valid);
  c_consume_produce: cover property (@(posedge clk) ev && take_in && pend_r);
`endif

endmodule

tb_usb_ep_pending_sv.sv — the testbench, SystemVerilog

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// =====================================================================
//  tb_usb_ep_pending_sv -- SystemVerilog testbench for usb_ep_pending_sv.
//
//  Phases 1-3 present the SAME directed stimulus, in the same order, as
//  the Verilog bench, so their directed counts must agree to the digit.
//
//  PHASE 1 is unlike any other phase in this curriculum and it is the
//  reason the module exists. It does not compare the design to a model.
//  It states the architectural claim directly and requires it:
//
//      however much work this endpoint has, and for however long,
//      NOTHING APPEARS ON THE BUS until the host addresses it.
//
//  That check cannot be satisfied by a reference model that shares the
//  design's structure, because it does not consult a model at all. 30.2
//  established why that matters: a model written by the design's author
//  from the design's sentence is one artefact wearing two hats, and
//  30.4's specimen agreed with its own bug for 39,108 checks.
//
//  PHASES
//    1 INTENT      the silence claim, stated and required directly
//    2 EXHAUSTIVE  16 combinations: 4 binary axes, all reachable
//    3 SCENARIO    the named cases, including the same-cycle collision
//    4 RANDOM      supplementary, and audited for what it reaches
// =====================================================================
`timescale 1ns/1ps

module tb_usb_ep_pending_sv;

  logic      clk = 1'b0;
  logic      rst_n;
  logic      ev;
  logic [7:0] ev_data;
  logic      token_valid, token_is_in;

  wire       resp_valid, resp_is_data, pending;
  wire [7:0] resp_data;
  wire [15:0] n_events, n_tokens, n_data, n_nak, n_unsolicited;

  usb_ep_pending_sv dut (
    .clk(clk), .rst_n(rst_n),
    .ev(ev), .ev_data(ev_data),
    .token_valid(token_valid), .token_is_in(token_is_in),
    .resp_valid(resp_valid), .resp_is_data(resp_is_data),
    .resp_data(resp_data), .pending(pending),
    .n_events(n_events), .n_tokens(n_tokens), .n_data(n_data),
    .n_nak(n_nak), .n_unsolicited(n_unsolicited)
  );

  always #5 clk = ~clk;

  // ---- the independent reference model ---------------------------
  // Written from the ARCHITECTURAL RULES, in the order a specification
  // would state them, not from the RTL's expressions:
  //   R1  a response happens if and only if the host issued a token
  //   R2  an IN token is answered with DATA if the endpoint held data
  //       WHEN THE TOKEN ARRIVED, and with NAK otherwise
  //   R3  an event makes the endpoint pending and replaces its byte
  //   R4  a consumed byte clears pending unless an event replaces it
  logic      rm_pend;
  logic [7:0] rm_data;
  logic [15:0] rm_ev, rm_tok, rm_dat, rm_nak, rm_uns;

  int  chk_dir, chk_rnd, err;
  bit  in_random;
  int  m_ev, m_tok, m_in, m_out, m_data_resp, m_nak_resp,
       m_collide, m_pend_cycles, m_silent_cycles, m_setupfail;
  int  i, j, a, b, c, d;

  task bump; begin
    if (in_random) chk_rnd = chk_rnd + 1; else chk_dir = chk_dir + 1;
  end endtask

  task ck(string what, logic [31:0] got, logic [31:0] exp);
    begin
      bump;
      if (got !== exp) begin
        err = err + 1;
        if (!in_random && err <= 40)
          $display("  ** %s: got %0d expected %0d  (t=%0t)", what, got, exp, $time);
      end
    end
  endtask

  task ref_step;
    logic t_in, t_out, consumed;
    begin
      if (!rst_n) begin
        rm_pend = 0; rm_data = 0;
        rm_ev = 0; rm_tok = 0; rm_dat = 0; rm_nak = 0; rm_uns = 0;
      end else begin
        t_in  = token_valid &&  token_is_in;
        t_out = token_valid && !token_is_in;
        // R2 uses the value of pending BEFORE this edge
        consumed = t_in && rm_pend;
        if (token_valid) rm_tok = rm_tok + 1;
        if (consumed)    rm_dat = rm_dat + 1;
        if (t_in && !rm_pend) rm_nak = rm_nak + 1;
        // R3 beats R4: an event in the same cycle as a consume leaves
        // the endpoint pending with the NEW byte
        if (ev) begin
          rm_pend = 1; rm_data = ev_data; rm_ev = rm_ev + 1;
        end else if (consumed) begin
          rm_pend = 0;
        end
        // tallies
        if (ev)       m_ev   = m_ev   + 1;
        if (t_in)     m_in   = m_in   + 1;
        if (t_out)    m_out  = m_out  + 1;
        if (consumed) m_data_resp = m_data_resp + 1;
        if (t_in && !consumed) m_nak_resp = m_nak_resp + 1;
        if (ev && token_valid) m_collide = m_collide + 1;
      end
    end
  endtask

  // What the endpoint put on the bus THIS cycle, captured while the
  // token is still driven. The scenarios inspect these rather than the
  // live outputs, which are only meaningful before the edge.
  logic       cap_valid, cap_isdata;
  logic [7:0] cap_data;

  // Pre-edge: the response, compared against the model's state AS IT IS
  // WHEN THE TOKEN ARRIVES -- which is the architectural rule.
  task cmp_comb;
    logic exp_valid, exp_isdata;
    begin
      exp_valid  = token_valid;
      exp_isdata = token_valid && token_is_in && rm_pend;
      cap_valid  = resp_valid;
      cap_isdata = resp_is_data;
      cap_data   = resp_data;
      ck("resp_valid",   {31'd0, resp_valid},   {31'd0, exp_valid});
      ck("resp_is_data", {31'd0, resp_is_data}, {31'd0, exp_isdata});
      ck("pending",      {31'd0, pending},      {31'd0, rm_pend});
      if (exp_isdata)
        ck("resp_data",  {24'd0, resp_data},    {24'd0, rm_data});
      else bump;
      if (rm_pend)      m_pend_cycles   = m_pend_cycles + 1;
      if (!token_valid) m_silent_cycles = m_silent_cycles + 1;
    end
  endtask

  // Post-edge: the registered tallies.
  task cmp_regs; begin
    ck("n_events",     {16'd0, n_events},     {16'd0, rm_ev});
    ck("n_tokens",     {16'd0, n_tokens},     {16'd0, rm_tok});
    ck("n_data",       {16'd0, n_data},       {16'd0, rm_dat});
    ck("n_nak",        {16'd0, n_nak},        {16'd0, rm_nak});
    ck("n_unsolicited",{16'd0, n_unsolicited},32'd0);
  end endtask

  // The INTENT check, run every cycle of every phase. It consults no
  // model: it is the architectural claim, asserted directly.
  task intent_check; begin
    bump;
    if (resp_valid && !token_valid) begin
      err = err + 1;
      $display("  ** INTENT VIOLATED: the endpoint drove the bus with no token (t=%0t, pending=%0d)",
               $time, pending);
    end
  end endtask

  task step; begin
    #1;
    cmp_comb;
    intent_check;
    @(posedge clk);
    ref_step;
    #1;
    cmp_regs;
    ev = 0; ev_data = 0; token_valid = 0; token_is_in = 0;
  end endtask

  task idle; step; endtask

  task hard_reset; begin
    rst_n = 0; ev = 0; ev_data = 0; token_valid = 0; token_is_in = 0;
    repeat (3) begin @(posedge clk); ref_step; end
    #1; rst_n = 1;
    @(posedge clk); ref_step; #1; cmp_regs;
  end endtask

  task device_event(logic [7:0] v);
    ev = 1; ev_data = v; step;
  endtask
  task in_token;  token_valid = 1; token_is_in = 1; step; endtask
  task out_token; token_valid = 1; token_is_in = 0; step; endtask

  // -----------------------------------------------------------------
  //  PHASE 1 -- THE SILENCE CLAIM.
  //
  //  Fill the endpoint with work and then do nothing for a long time.
  //  If the misconception were true -- if a device with data could
  //  start a transaction -- something would appear. Nothing does, and
  //  this phase requires it every cycle rather than inferring it.
  // -----------------------------------------------------------------
  int  silent_run;

  task phase_intent;
    begin
      hard_reset;
      silent_run = 0;
      // sixteen events, no tokens at all
      for (i = 0; i < 16; i = i + 1) begin
        device_event(8'hA0 + 8'(i));
        ck("still pending", {31'd0, pending}, 32'd1);
        ck("and still silent", {31'd0, cap_valid}, 32'd0);
      end
      // then two hundred idle cycles with the endpoint full
      for (i = 0; i < 200; i = i + 1) begin
        idle;
        ck("silent while pending", {31'd0, cap_valid}, 32'd0);
        silent_run = silent_run + 1;
      end
      ck("nothing unsolicited", {16'd0, n_unsolicited}, 32'd0);
      ck("no data was sent",    {16'd0, n_data},        32'd0);
      ck("no tokens arrived",   {16'd0, n_tokens},      32'd0);
      // and the moment the host asks, the data is there
      in_token;
      ck("now it answers",      {31'd0, cap_valid},    32'd1);
      ck("and it is DATA",      {31'd0, cap_isdata},  32'd1);
      ck("the newest byte",     {24'd0, cap_data},     {24'd0, 8'hA0 + 8'd15});
      // The scenario must have HAPPENED: 216 cycles of held work.
      bump;
      if (silent_run < 200) begin
        err = err + 1;
        $display("  ** intent: only %0d silent cycles", silent_run);
      end
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 2 -- the exhaustive sweep.
  //
  //  AXES, named so that what is absent is visible:
  //    pending before the edge     0, 1                        2
  //    a device event this cycle   0, 1                        2
  //    a token this cycle          0, 1                        2
  //    token direction             OUT, IN                     2
  //    ------------------------------------------------------------
  //                                           2^4 =           16
  //
  //  All sixteen are reachable: the four inputs come from two agents
  //  that do not constrain each other, which is the architectural fact
  //  the chapter is about.
  //
  //  WHAT THIS SWEEP DOES NOT CONTAIN: history longer than one edge.
  //  Two tokens in a row, and an event between two tokens, are phase 3.
  //  An exhaustive sweep is exhaustive over the axes it has -- 30.2 §10.
  // -----------------------------------------------------------------
  task setup_pending(logic want);
    begin
      hard_reset;
      if (want) begin
        device_event(8'h5A);
        bump;
        if (pending !== 1'b1) begin
          err = err + 1; m_setupfail = m_setupfail + 1;
          $display("  ** setup: pending was not established");
        end
      end else begin
        bump;
        if (pending !== 1'b0) begin
          err = err + 1; m_setupfail = m_setupfail + 1;
          $display("  ** setup: pending was not clear");
        end
      end
    end
  endtask

  task phase_sweep;
    begin
      for (a = 0; a < 2; a = a + 1)     // pending
      for (b = 0; b < 2; b = b + 1)     // event
      for (c = 0; c < 2; c = c + 1)     // token
      for (d = 0; d < 2; d = d + 1)     // direction
      begin
        setup_pending(a[0]);
        ev          = b[0];
        ev_data     = 8'hC0 + {4'd0, a[0], b[0], c[0], d[0]};
        token_valid = c[0];
        token_is_in = d[0];
        step;
        idle;
      end
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 3 -- the named scenarios.
  // -----------------------------------------------------------------
  task phase_scenarios;
    begin
      // S1 a token with nothing pending is answered, and answered NAK.
      //    The misconception has no account of this: if devices spoke
      //    when they had something to say, there would be no reason to
      //    have a way of saying "not yet".
      hard_reset;
      in_token;
      ck("S1 the device answered",   {31'd0, cap_valid},   32'd1);
      ck("S1 but not with data",     {31'd0, cap_isdata}, 32'd0);
      ck("S1 counted as a NAK",      {16'd0, n_nak},        32'd1);

      // S2 an event and an IN token in the SAME cycle. The device
      //    answers with the state it had when the token arrived, so the
      //    answer is NAK -- and the byte is kept for the next token.
      hard_reset;
      ev = 1; ev_data = 8'h77; token_valid = 1; token_is_in = 1; step;
      ck("S2 answered NAK",          {31'd0, cap_isdata}, 32'd0);
      ck("S2 the event was kept",    {31'd0, pending},      32'd1);
      in_token;
      ck("S2 next token gets data",  {31'd0, cap_isdata}, 32'd1);
      ck("S2 and it is the byte",    {24'd0, cap_data},    {24'd0, 8'h77});

      // S3 back-to-back IN tokens: the first gets the data, the second
      //    gets a NAK. The host asks twice; the device has one byte.
      hard_reset;
      device_event(8'h11);
      in_token;
      ck("S3 first token: data",     {31'd0, cap_isdata}, 32'd1);
      in_token;
      ck("S3 second token: NAK",     {31'd0, cap_isdata}, 32'd0);
      ck("S3 one data, one NAK",     {16'd0, n_data + n_nak}, 32'd2);

      // S4 consume and produce in the same cycle: the endpoint is
      //    pending again immediately, with the NEW byte.
      hard_reset;
      device_event(8'h22);
      ev = 1; ev_data = 8'h33; token_valid = 1; token_is_in = 1; step;
      ck("S4 data was sent",         {31'd0, cap_isdata}, 32'd1);
      ck("S4 still pending",         {31'd0, pending},      32'd1);
      in_token;
      ck("S4 the new byte",          {24'd0, cap_data},    {24'd0, 8'h33});

      // S5 an OUT token is also the host's move. The direction changes
      //    who carries the data; it does not change who starts.
      hard_reset;
      out_token;
      ck("S5 the device answered",   {31'd0, cap_valid},   32'd1);
      ck("S5 not an IN response",    {31'd0, cap_isdata}, 32'd0);
      ck("S5 no NAK counted",        {16'd0, n_nak},        32'd0);

      // S6 a long run of events with no tokens, then one token.
      //    Sixty-four bytes of work produce exactly zero bus activity.
      hard_reset;
      for (i = 0; i < 64; i = i + 1) device_event(8'h40 + 8'(i));
      ck("S6 64 events",             {16'd0, n_events},     32'd64);
      ck("S6 zero bus activity",     {16'd0, n_tokens},     32'd0);
      ck("S6 nothing unsolicited",   {16'd0, n_unsolicited},32'd0);
      in_token;
      ck("S6 one token, one answer", {16'd0, n_data},       32'd1);
    end
  endtask

  // -----------------------------------------------------------------
  //  PHASE 4 -- random, audited.
  // -----------------------------------------------------------------
  task phase_random;
    int r;
    begin
      in_random = 1;
      hard_reset;
      for (j = 0; j < 4000; j = j + 1) begin
        r = $urandom_range(99);
        if (r < 30) begin
          device_event(8'($urandom_range(255)));
        end else if (r < 55) begin
          in_token;
        end else if (r < 65) begin
          out_token;
        end else if (r < 78) begin
          // the same-cycle collision, steered: unsteered, an event and a
          // token landing together is rare, and it is the case the
          // architecture has an opinion about
          ev = 1; ev_data = 8'($urandom_range(255));
          token_valid = 1; token_is_in = (($urandom_range(99)) < 70);
          step;
        end else begin
          idle;
        end
      end
      in_random = 0;
    end
  endtask

  initial begin
    chk_dir = 0; chk_rnd = 0; err = 0; in_random = 0;
    m_ev=0; m_tok=0; m_in=0; m_out=0; m_data_resp=0; m_nak_resp=0;
    m_collide=0; m_pend_cycles=0; m_silent_cycles=0; m_setupfail=0;

    phase_intent;
    $display("  phase 1 intent      : %0d checks, %0d errors  (%0d silent cycles while pending)",
             chk_dir, err, silent_run);
    phase_sweep;
    $display("  phase 2 exhaustive  : %0d checks, %0d errors  (16 combinations)", chk_dir, err);
    phase_scenarios;
    $display("  phase 3 scenarios   : %0d checks, %0d errors", chk_dir, err);
    $display("  ---- DIRECTED-ONLY  : %0d checks, %0d errors ----", chk_dir, err);
    phase_random;

    $display("");
    $display("  measured reachability (all phases)");
    $display("    device events .......... %0d", m_ev);
    $display("    IN tokens .............. %0d", m_in);
    $display("    OUT tokens ............. %0d", m_out);
    $display("    answered with DATA ..... %0d", m_data_resp);
    $display("    answered with NAK ...... %0d", m_nak_resp);
    $display("    event+token same cycle . %0d", m_collide);
    $display("    cycles holding work .... %0d", m_pend_cycles);
    $display("    cycles with no token ... %0d", m_silent_cycles);
    $display("    setup failures ......... %0d", m_setupfail);
    $display("");
    $display("  directed checks ........ %0d", chk_dir);
    $display("  random checks .......... %0d", chk_rnd);
    $display("  TOTAL checks ........... %0d", chk_dir + chk_rnd);
    $display("  ERRORS ................. %0d", err);
    if (err == 0) $display("  PASS"); else $display("  FAIL");
    $finish;
  end

endmodule

The reason this version matters to a misconception chapter is that the belief becomes a property that fails:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  property p_no_unsolicited;
    @(posedge clk) disable iff (!rst_n) resp_valid |-> token_valid;
  endproperty

One line, in the language of the architecture. And beside it, the progress property — with an antecedent worth reading twice:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  property p_held_data_is_delivered;
    @(posedge clk) disable iff (!rst_n)
      (pend_r && token_valid && token_is_in) |-> resp_is_data;
  endproperty

token_valid is in the antecedent. An unconditional claim — "data the device holds is eventually delivered" — would be false for a correct design, because a device that is never polled correctly never sends. A progress property that blames the device for the host's silence gets switched off within a week, and 30.2 §9 made that a standing item.

9. VHDL-2008

usb_ep_pending.vhd — the design, VHDL-2008

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- =====================================================================
--  usb_ep_pending (VHDL-2008) -- the same contract. Same ports, same
--  rule, same reset, same latency.
--
--  In VHDL the architectural separation the chapter is about becomes a
--  statement about DEPENDENCIES: resp_valid is a concurrent assignment
--  whose right-hand side mentions token_valid and nothing else. A
--  reader checking the claim "a device cannot start a transaction" has
--  to read one line and check what is in it.
--
--  CLASSIFICATION: simplified synthesisable teaching RTL. It is not a
--  USB device controller; see the Verilog header for what is omitted.
-- =====================================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity usb_ep_pending is
  port (
    clk           : in  std_logic;
    rst_n         : in  std_logic;

    ev            : in  std_logic;
    ev_data       : in  std_logic_vector(7 downto 0);

    token_valid   : in  std_logic;
    token_is_in   : in  std_logic;

    resp_valid    : out std_logic;
    resp_is_data  : out std_logic;
    resp_data     : out std_logic_vector(7 downto 0);

    pending       : out std_logic;

    n_events      : out unsigned(15 downto 0);
    n_tokens      : out unsigned(15 downto 0);
    n_data        : out unsigned(15 downto 0);
    n_nak         : out unsigned(15 downto 0);
    n_unsolicited : out unsigned(15 downto 0)
  );
end entity usb_ep_pending;

architecture rtl of usb_ep_pending is
  signal pend_r  : std_logic := '0';
  signal data_r  : std_logic_vector(7 downto 0) := (others => '0');
  signal c_ev, c_tok, c_data, c_nak, c_uns : unsigned(15 downto 0)
         := (others => '0');
  signal take_in, take_out : std_logic;
  signal rv_i, rd_i        : std_logic;
begin

  -- The host decides THAT something happens.
  rv_i <= token_valid;
  -- The device decides WHAT it is.
  rd_i <= '1' when (token_valid = '1' and token_is_in = '1' and pend_r = '1')
          else '0';

  resp_valid   <= rv_i;
  resp_is_data <= rd_i;
  resp_data    <= data_r;
  pending      <= pend_r;

  take_in  <= token_valid and token_is_in;
  take_out <= token_valid and (not token_is_in);

  seq : process (clk, rst_n)
  begin
    if rst_n = '0' then
      pend_r <= '0';
      data_r <= (others => '0');
      c_ev   <= (others => '0');
      c_tok  <= (others => '0');
      c_data <= (others => '0');
      c_nak  <= (others => '0');
      c_uns  <= (others => '0');
    elsif rising_edge(clk) then
      -- An event beats a consume: the endpoint is pending again with
      -- the new byte.
      if ev = '1' then
        pend_r <= '1';
        data_r <= ev_data;
        c_ev   <= c_ev + 1;
      elsif take_in = '1' and pend_r = '1' then
        pend_r <= '0';
      end if;

      if token_valid = '1' then
        c_tok <= c_tok + 1;
      end if;
      if take_in = '1' and pend_r = '1' then
        c_data <= c_data + 1;
      end if;
      if take_in = '1' and pend_r = '0' then
        c_nak <= c_nak + 1;
      end if;
      if rv_i = '1' and token_valid = '0' then
        c_uns <= c_uns + 1;
      end if;
    end if;
  end process seq;

  n_events      <= c_ev;
  n_tokens      <= c_tok;
  n_data        <= c_data;
  n_nak         <= c_nak;
  n_unsolicited <= c_uns;

end architecture rtl;

tb_usb_ep_pending.vhd — the testbench, VHDL-2008

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- =====================================================================
--  tb_usb_ep_pending -- VHDL-2008 testbench for usb_ep_pending.
--  Phases 1-3 present the SAME directed stimulus as the Verilog and
--  SystemVerilog benches, so their directed counts must agree.
--
--  Phase 1 states the architectural claim directly and requires it,
--  rather than comparing against a model. See the Verilog header.
-- =====================================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

entity tb_usb_ep_pending is
end entity tb_usb_ep_pending;

architecture sim of tb_usb_ep_pending is
  constant HALF : time := 10 ns;

  signal clk         : std_logic := '0';
  signal rst_n       : std_logic := '0';
  signal ev          : std_logic := '0';
  signal ev_data     : std_logic_vector(7 downto 0) := (others => '0');
  signal token_valid : std_logic := '0';
  signal token_is_in : std_logic := '0';

  signal resp_valid, resp_is_data, pending : std_logic;
  signal resp_data : std_logic_vector(7 downto 0);
  signal n_events, n_tokens, n_data, n_nak, n_unsolicited : unsigned(15 downto 0);

  signal done_flag : boolean := false;

  function b2i (s : std_logic) return integer is
  begin
    if s = '1' then return 1; else return 0; end if;
  end function b2i;
begin

  dut : entity work.usb_ep_pending
    port map (clk => clk, rst_n => rst_n, ev => ev, ev_data => ev_data,
              token_valid => token_valid, token_is_in => token_is_in,
              resp_valid => resp_valid, resp_is_data => resp_is_data,
              resp_data => resp_data, pending => pending,
              n_events => n_events, n_tokens => n_tokens, n_data => n_data,
              n_nak => n_nak, n_unsolicited => n_unsolicited);

  clkgen : process
  begin
    while not done_flag loop
      clk <= '0'; wait for HALF;
      clk <= '1'; wait for HALF;
    end loop;
    wait;
  end process clkgen;

  stim : process
    variable chk_dir, chk_rnd, errs, shown : natural := 0;
    variable in_random : boolean := false;

    variable rm_pend : std_logic := '0';
    variable rm_data : std_logic_vector(7 downto 0) := (others => '0');
    variable rm_ev, rm_tok, rm_dat, rm_nak : natural := 0;

    variable m_ev, m_in, m_out, m_data_resp, m_nak_resp : natural := 0;
    variable m_collide, m_pend_cycles, m_silent_cycles  : natural := 0;
    variable m_setupfail, silent_run                    : natural := 0;

    variable cap_valid, cap_isdata : std_logic := '0';
    variable cap_data : std_logic_vector(7 downto 0) := (others => '0');

    variable seed1 : positive := 551_213;
    variable seed2 : positive := 81_907;

    procedure bump is
    begin
      if in_random then chk_rnd := chk_rnd + 1; else chk_dir := chk_dir + 1; end if;
    end procedure bump;

    procedure ck (what : string; got : integer; exp : integer) is
    begin
      bump;
      if got /= exp then
        errs := errs + 1;
        if (not in_random) and shown < 40 then
          shown := shown + 1;
          report "  ** " & what & ": got " & integer'image(got) &
                 " expected " & integer'image(exp) severity warning;
        end if;
      end if;
    end procedure ck;

    procedure ref_step is
      variable t_in, t_out, consumed : boolean;
    begin
      if rst_n = '0' then
        rm_pend := '0'; rm_data := (others => '0');
        rm_ev := 0; rm_tok := 0; rm_dat := 0; rm_nak := 0;
      else
        t_in     := (token_valid = '1') and (token_is_in = '1');
        t_out    := (token_valid = '1') and (token_is_in = '0');
        consumed := t_in and (rm_pend = '1');
        if token_valid = '1' then rm_tok := rm_tok + 1; end if;
        if consumed then rm_dat := rm_dat + 1; end if;
        if t_in and rm_pend = '0' then rm_nak := rm_nak + 1; end if;
        if ev = '1' then
          rm_pend := '1'; rm_data := ev_data; rm_ev := rm_ev + 1;
        elsif consumed then
          rm_pend := '0';
        end if;
        if ev = '1' then m_ev := m_ev + 1; end if;
        if t_in then m_in := m_in + 1; end if;
        if t_out then m_out := m_out + 1; end if;
        if consumed then m_data_resp := m_data_resp + 1; end if;
        if t_in and not consumed then m_nak_resp := m_nak_resp + 1; end if;
        if ev = '1' and token_valid = '1' then m_collide := m_collide + 1; end if;
      end if;
    end procedure ref_step;

    procedure cmp_comb is
      variable exp_valid, exp_isdata : integer;
    begin
      exp_valid := b2i(token_valid);
      if token_valid = '1' and token_is_in = '1' and rm_pend = '1' then
        exp_isdata := 1;
      else
        exp_isdata := 0;
      end if;
      cap_valid  := resp_valid;
      cap_isdata := resp_is_data;
      cap_data   := resp_data;
      ck("resp_valid",   b2i(resp_valid),   exp_valid);
      ck("resp_is_data", b2i(resp_is_data), exp_isdata);
      ck("pending",      b2i(pending),      b2i(rm_pend));
      if exp_isdata = 1 then
        ck("resp_data", to_integer(unsigned(resp_data)),
                        to_integer(unsigned(rm_data)));
      else
        bump;
      end if;
      if rm_pend = '1' then m_pend_cycles := m_pend_cycles + 1; end if;
      if token_valid = '0' then m_silent_cycles := m_silent_cycles + 1; end if;
    end procedure cmp_comb;

    procedure cmp_regs is
    begin
      ck("n_events",     to_integer(n_events), rm_ev);
      ck("n_tokens",     to_integer(n_tokens), rm_tok);
      ck("n_data",       to_integer(n_data),   rm_dat);
      ck("n_nak",        to_integer(n_nak),    rm_nak);
      ck("n_unsolicited",to_integer(n_unsolicited), 0);
    end procedure cmp_regs;

    -- The claim, asserted directly. No model consulted.
    procedure intent_check is
    begin
      bump;
      if resp_valid = '1' and token_valid = '0' then
        errs := errs + 1;
        report "  ** INTENT VIOLATED: the endpoint drove the bus with no token"
          severity warning;
      end if;
    end procedure intent_check;

    procedure step is
    begin
      wait for 1 ns;
      cmp_comb;
      intent_check;
      wait until rising_edge(clk);
      ref_step;
      wait for 1 ns;
      cmp_regs;
      ev <= '0'; ev_data <= (others => '0');
      token_valid <= '0'; token_is_in <= '0';
    end procedure step;

    procedure idle is begin step; end procedure;

    procedure hard_reset is
    begin
      rst_n <= '0'; ev <= '0'; ev_data <= (others => '0');
      token_valid <= '0'; token_is_in <= '0';
      for i in 0 to 2 loop wait until rising_edge(clk); ref_step; end loop;
      wait for 1 ns; rst_n <= '1';
      wait until rising_edge(clk); ref_step; wait for 1 ns; cmp_regs;
    end procedure hard_reset;

    procedure device_event (v : natural) is
    begin
      ev <= '1'; ev_data <= std_logic_vector(to_unsigned(v, 8)); step;
    end procedure;
    procedure in_token  is begin token_valid <= '1'; token_is_in <= '1'; step; end procedure;
    procedure out_token is begin token_valid <= '1'; token_is_in <= '0'; step; end procedure;

    procedure setup_pending (want : std_logic) is
    begin
      hard_reset;
      if want = '1' then
        device_event(16#5A#);
        bump;
        if pending /= '1' then
          errs := errs + 1; m_setupfail := m_setupfail + 1;
          report "  ** setup: pending was not established" severity warning;
        end if;
      else
        bump;
        if pending /= '0' then
          errs := errs + 1; m_setupfail := m_setupfail + 1;
          report "  ** setup: pending was not clear" severity warning;
        end if;
      end if;
    end procedure setup_pending;

    impure function rnd (n : positive) return natural is
      variable x : real;
    begin
      uniform(seed1, seed2, x);
      return natural(real(n - 1) * x);
    end function rnd;

    variable r : natural;
  begin
    -- ---- PHASE 1 : the silence claim ----
    hard_reset;
    silent_run := 0;
    for i in 0 to 15 loop
      device_event(16#A0# + i);
      ck("still pending", b2i(pending), 1);
      ck("and still silent", b2i(cap_valid), 0);
    end loop;
    for i in 0 to 199 loop
      idle;
      ck("silent while pending", b2i(cap_valid), 0);
      silent_run := silent_run + 1;
    end loop;
    ck("nothing unsolicited", to_integer(n_unsolicited), 0);
    ck("no data was sent",    to_integer(n_data),        0);
    ck("no tokens arrived",   to_integer(n_tokens),      0);
    in_token;
    ck("now it answers",   b2i(cap_valid),  1);
    ck("and it is DATA",   b2i(cap_isdata), 1);
    ck("the newest byte",  to_integer(unsigned(cap_data)), 16#AF#);
    bump;
    if silent_run < 200 then
      errs := errs + 1;
      report "  ** intent: too few silent cycles" severity warning;
    end if;
    report "  phase 1 intent      : " & integer'image(chk_dir) &
           " checks, " & integer'image(errs) & " errors  (" &
           integer'image(silent_run) & " silent cycles while pending)";

    -- ---- PHASE 2 : 2^4 = 16 ----
    for a in 0 to 1 loop
      for b in 0 to 1 loop
        for c in 0 to 1 loop
          for d in 0 to 1 loop
            if a = 1 then setup_pending('1'); else setup_pending('0'); end if;
            if b = 1 then ev <= '1'; else ev <= '0'; end if;
            ev_data <= std_logic_vector(to_unsigned(
                        16#C0# + a*8 + b*4 + c*2 + d, 8));
            if c = 1 then token_valid <= '1'; else token_valid <= '0'; end if;
            if d = 1 then token_is_in <= '1'; else token_is_in <= '0'; end if;
            step;
            idle;
          end loop;
        end loop;
      end loop;
    end loop;
    report "  phase 2 exhaustive  : " & integer'image(chk_dir) &
           " checks, " & integer'image(errs) & " errors  (16 combinations)";

    -- ---- PHASE 3 : the named scenarios ----
    hard_reset;
    in_token;
    ck("S1 the device answered", b2i(cap_valid),  1);
    ck("S1 but not with data",   b2i(cap_isdata), 0);
    ck("S1 counted as a NAK",    to_integer(n_nak), 1);

    hard_reset;
    ev <= '1'; ev_data <= x"77"; token_valid <= '1'; token_is_in <= '1'; step;
    ck("S2 answered NAK",       b2i(cap_isdata), 0);
    ck("S2 the event was kept", b2i(pending),    1);
    in_token;
    ck("S2 next token gets data", b2i(cap_isdata), 1);
    ck("S2 and it is the byte", to_integer(unsigned(cap_data)), 16#77#);

    hard_reset;
    device_event(16#11#);
    in_token;
    ck("S3 first token: data",  b2i(cap_isdata), 1);
    in_token;
    ck("S3 second token: NAK",  b2i(cap_isdata), 0);
    ck("S3 one data, one NAK",  to_integer(n_data) + to_integer(n_nak), 2);

    hard_reset;
    device_event(16#22#);
    ev <= '1'; ev_data <= x"33"; token_valid <= '1'; token_is_in <= '1'; step;
    ck("S4 data was sent", b2i(cap_isdata), 1);
    ck("S4 still pending", b2i(pending),    1);
    in_token;
    ck("S4 the new byte",  to_integer(unsigned(cap_data)), 16#33#);

    hard_reset;
    out_token;
    ck("S5 the device answered", b2i(cap_valid),  1);
    ck("S5 not an IN response",  b2i(cap_isdata), 0);
    ck("S5 no NAK counted",      to_integer(n_nak), 0);

    hard_reset;
    for i in 0 to 63 loop device_event(16#40# + i); end loop;
    ck("S6 64 events",           to_integer(n_events), 64);
    ck("S6 zero bus activity",   to_integer(n_tokens), 0);
    ck("S6 nothing unsolicited", to_integer(n_unsolicited), 0);
    in_token;
    ck("S6 one token, one answer", to_integer(n_data), 1);

    report "  phase 3 scenarios   : " & integer'image(chk_dir) &
           " checks, " & integer'image(errs) & " errors";
    report "  ---- DIRECTED-ONLY  : " & integer'image(chk_dir) &
           " checks, " & integer'image(errs) & " errors ----";

    -- ---- PHASE 4 : random ----
    in_random := true;
    hard_reset;
    for j in 0 to 3999 loop
      r := rnd(100);
      if r < 30 then
        device_event(rnd(256));
      elsif r < 55 then
        in_token;
      elsif r < 65 then
        out_token;
      elsif r < 78 then
        ev <= '1';
        ev_data <= std_logic_vector(to_unsigned(rnd(256), 8));
        token_valid <= '1';
        if rnd(100) < 70 then token_is_in <= '1'; else token_is_in <= '0'; end if;
        step;
      else
        idle;
      end if;
    end loop;
    in_random := false;

    report "  measured reachability (all phases)";
    report "    device events .......... " & integer'image(m_ev);
    report "    IN tokens .............. " & integer'image(m_in);
    report "    OUT tokens ............. " & integer'image(m_out);
    report "    answered with DATA ..... " & integer'image(m_data_resp);
    report "    answered with NAK ...... " & integer'image(m_nak_resp);
    report "    event+token same cycle . " & integer'image(m_collide);
    report "    cycles holding work .... " & integer'image(m_pend_cycles);
    report "    cycles with no token ... " & integer'image(m_silent_cycles);
    report "    setup failures ......... " & integer'image(m_setupfail);
    report "  directed checks ........ " & integer'image(chk_dir);
    report "  random checks .......... " & integer'image(chk_rnd);
    report "  TOTAL checks ........... " & integer'image(chk_dir + chk_rnd);
    report "  ERRORS ................. " & integer'image(errs);
    if errs = 0 then report "  PASS"; else report "  FAIL" severity failure; end if;
    done_flag <= true;
    wait;
  end process stim;

end architecture sim;

In VHDL the claim becomes a statement about dependencies: rv_i is a concurrent assignment whose right-hand side mentions token_valid and nothing else. A reviewer checking "can a device start a transaction" reads one line and checks what is in it.

10. The Misconception As Hardware

The mutations are not arbitrary edits. Each one is the belief, written as RTL.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    MUT    THE BELIEF ENCODED                      V-DIR  SV-DIR  VH-DIR
    P-M1   a device with data can start a
           transaction                              1,098  1,098   1,098
    P-M2   data produced now can answer a
           request already made                         3      3       3

BASE reads zero in all six columns and both directed columns are identical across the three languages.

P-M1 is one character of change:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    assign resp_valid = token_valid;            // architecture
    assign resp_valid = token_valid || pend_r;  // the misconception

It is caught 1,098 times in the directed suite. The intent phase alone accounts for 862 of those, and the rest come from the ordinary output comparison, because a device that speaks unbidden is wrong in a great many cycles.

P-M2 is the subtler one and scores 3. It lets a byte produced in the same cycle as the token answer that token — data that did not exist when the host asked, appearing in the response to the request. Three checks catch it, all in the scenario written for the same-cycle case, and its total of 130 (against P-M1's 7,700) is the measure of how much rarer the situation is.

11. Where UVM Would And Would Not Help

Not here, and saying why is more useful than an environment that does not earn its place.

This block has two agents but no shared state: the function produces, the host asks, and the endpoint holds one bit between them. The interesting cases are a four-axis product with sixteen members, and sixteen is a sweep, not a constrained-random space.

Where UVM becomes right for this subject is one level up, and it is worth naming so the boundary is clear:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    WORTH A UVM ENVIRONMENT
      a host-poll SEQUENCE across several endpoints with different
      intervals, against device-side event generators with independent
      rates -- the question being whether any endpoint's events are
      collected within its configured interval under load

      that is a scheduling question with a large scenario space, it is
      31.4's subject, and its reference model is a service calendar
      rather than a copy of any RTL

    NOT WORTH IT
      this block. The item would be a pair of bits.

12. What The Wrong Model Does To Debugging

This is the most valuable part of the chapter, because the belief costs days rather than producing a bug.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    SYMPTOM      "the device isn't sending its data"

    WRONG MODEL  the device decides when to send
    WRONG QUESTION   why isn't the device sending?
    WASTED ON        device firmware, the function logic, the buffer,
                     the interrupt handler inside the device -- all of
                     which may be perfectly correct

    CORRECT MODEL  the device answers; the host asks
    THE FIRST QUESTION, and it is one trace away:
                 IS THE HOST POLLING THE ENDPOINT AT ALL?

    and the three answers lead to three different halves of the tree:

      no tokens at all          -> the endpoint is not in the host's
                                   schedule. Enumeration, the interface
                                   setting, or the driver never opened
                                   the pipe. NOT a device problem.

      tokens, all NAKed         -> the host is asking and the device
                                   says it has nothing. The bug is
                                   inside the device, between the
                                   function and the pending bit.

      tokens, data flowing      -> the bug is above USB entirely: the
                                   host software is receiving and not
                                   using it.

Three outcomes, one observation, and the wrong model does not suggest making it. It suggests looking inside the device, which is right in exactly one of the three cases.

13. Interview Reasoning

"A USB mouse sends the host its movements. Walk me through what actually happens on the bus."

The premise has the verb wrong, and correcting it precisely is the answer.

The mouse does not send. At configuration time the host read the mouse's endpoint descriptor, which asks to be polled at some interval, and the host put that endpoint into its schedule. From then on the host issues an IN token to that endpoint every interval, whether or not the mouse has moved. Most of those tokens are answered NAK — "nothing for you" — and that is the normal state of an idle mouse, not an error.

When the mouse moves, its firmware makes a byte pending. Nothing appears on the bus at that moment. The next scheduled token gets a data packet, the host ACKs it, and the pending state clears. So the latency between the physical movement and the data is bounded by the polling interval, which is the number the endpoint descriptor negotiated.

Worth adding, because it is where the belief comes from: the transfer type is called an interrupt transfer, and that name describes the service guarantee — polled at least this often — rather than the initiator. It is not related to a CPU interrupt, and an engineer who imports that meaning will be right about everything except who started the transaction.

And the consequence I would want a team to take from it: when somebody says "the device isn't sending", the first observation is not inside the device. It is whether the host is issuing tokens to that endpoint at all, because the three possible answers lead to three unrelated investigations and two of them are not about the device.

14. Exercises

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    1  PREDICTION
       Prediction 3 in section 3 says NAK would be meaningless if
       devices initiated. Explain why, in your own words, and then say
       what else in the protocol would be unnecessary.

    2  THE TRACE
       Sketch what a bus trace of an idle mouse looks like. How would
       you tell, from that trace alone, what its polling interval is?

    3  VERILOG
       Add STALL as a third possible answer. What state does it need,
       what clears it, and does it change the resp_valid line?

    4  SYSTEMVERILOG
       Implement the same, and write the property that says a stalled
       endpoint never answers with data.

    5  VHDL
       Implement it, and say which of the three languages made the
       three-way answer clearest.

    6  TESTBENCH
       Write the directed scenario that distinguishes "the device
       withheld data it had" from "the device was never asked". Then
       say which of the two the intent phase already covers.

    7  SVA
       The progress property in section 8 has token_valid in its
       antecedent. Write the version without it, and describe the
       design that satisfies the weak version and not the strong one.

    8  MUTATION
       Write a third mutation encoding a different version of the
       belief -- one where the device initiates only SOMETIMES. Predict
       its directed score before running it.

    9  DEBUG
       A device enumerates and its interrupt endpoint never delivers.
       Give the single first observation and the three branches.

   10  INTERVIEW
       Answer "does a USB device ever talk without being asked?" in one
       sentence, then qualify it honestly in two more.

15. What Carries Forward

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    THE CORRECTION
    o  HAVING WORK is the device's; STARTING A TRANSACTION is the
       host's, and a device holds exactly one of the two
    o  "interrupt transfer" names the SERVICE GUARANTEE -- polled at
       least this often -- not the initiator
    o  the existence of NAK is nearly a proof on its own: a device that
       spoke when it had news would never need to say "not yet"
    o  latency from event to data is bounded by the POLLING INTERVAL,
       and that bound is negotiated in a descriptor

    THE HARDWARE
    o  resp_valid = token_valid, and pend_r does not appear in it
    o  a counter for the impossible case, so a build in which it became
       possible would say so
    o  an event arriving with a token does not rescue that token's
       answer: the device answers with the state it HAD

    THE METHOD
    o  an INTENT CHECK states the claim and consults no model, which is
       the only kind of check that can disagree with a model that
       shares the design's mistake
    o  a progress property's antecedent must exclude the environment --
       "if the host asks" -- or it is false for a correct design
    o  a misconception encoded as hardware is wrong almost EVERYWHERE,
       so removing its dedicated scenario takes 1,098 to 236 rather
       than to zero. The danger of a false belief is not a subtle bug;
       it is a confident wrong question.

    THE DEBUG CONSEQUENCE
    o  "why isn't the device sending" has no answer. "Is the host
       polling the endpoint" has three, and they lead to three
       different investigations.

The next belief is about the word endpoint, and it is the one most reinforced by ordinary English.

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.