← Design Verification Interviews

Interview Question · Waveform

One Attempt, Three Threads: A Delay Range

L0 FresherL1 Juniorsvasequencesdelays

The Question

The interviewer asks

For req |-> ##[1:3] ack, a request at 15 is answered at 35 and a request at 55 is never answered. When does each attempt pass or fail?

Reveal the Model Answer

A delay range starts one thread per delay, so each attempt checks ack at the next three edges. The attempt from 15 passes at 35, the first edge where a thread matches. The attempt from 55 fails only at 85, when its last thread finds no ack, not at 65. Failures from a range are therefore reported at the end of the window.

req |-> ##[1:3] ack
01234567edge (ns)1525354555657585reqackattempt from 15 passesattempt from 55 fails at the window end

Each request opens a three-edge window; one thread per edge.

The traffic from the trace. Only one failure, at the last edge of the second window.

RUNVerilator · simulator service
Edit the code, then run it
Expected: t=85 a_win failed failures=1

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Fresher)

A good fresher answer marks the window on the waveform. It predicts a pass at 35 and a failure at 85.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfProve ItBreak ItCompare

  1. One Attempt, Three Threads: A Delay Range
  2. WhyWhy a Window Fails LateL1 Junior

    The first edge of the window already had no ack. Why does the simulator wait until the window ends to report the failure?

    Reveal the Model Answer

    Until the last edge, a later thread could still match and turn the attempt into a pass. The verdict is known only when every thread has matched or died. For a missing response, that is the last edge of the range. That is why a wide window delays the failure report and makes debug start later in the log.

    Three Requests, One RangeOne lane per request for req |-> ##[1:3] ack. Each attempt forks one thread per delay in the range. The attempts at the other edges succeed vacuously and are left out.req |-> ##[1:3] ackMeasured on Verilator 5.052: one copy of the assertion per lane
    attempt startspassfailthread dies; the attempt goes onthread dropped: the attempt has already passed
    Three requests, one rangereq @15, from edge 2 (t=15): thread 1 (to edge 3 (t=25)) dies at edge 3 (t=25); thread 2 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 3 (to edge 5 (t=45)) fails at edge 5 (t=45). req @25, from edge 3 (t=25): thread 1 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 2 (to edge 5 (t=45)) dies at edge 5 (t=45); thread 3 (to edge 6 (t=55)) passes at edge 6 (t=55). req @45, from edge 5 (t=45): thread 1 (to edge 6 (t=55)) passes at edge 6 (t=55); thread 2 (to edge 7 (t=65)) is dropped at edge 6 (t=55), because the attempt has already passed; thread 3 (to edge 8 (t=75)) is dropped at edge 6 (t=55), because the attempt has already passed.edgetime15215325435545655765875clkreqackattemptsreq @15req @15: dies at edge 3 (t=25)req @15: dies at edge 4 (t=35)req @15: fails at edge 5 (t=45)req @25req @25: dies at edge 4 (t=35)req @25: dies at edge 5 (t=45)req @25: passes at edge 6 (t=55)req @45req @45: passes at edge 6 (t=55)
    1. Edge 2 · t=15

      req is high at 15, so this attempt forks three threads. Each one looks for ack at its own edge: 25, 35 or 45.

    2. Edge 3 · t=25

      A second request starts its own attempt with three new threads. The first thread from 15 finds ack low and dies, but its attempt goes on.

    3. Edge 4 · t=35

      Two more threads die, one in each attempt. The attempt from 15 now has a single thread left.

    4. Edge 5 · t=45

      The last thread from 15 finds ack low, so that attempt fails here, at the end of its window. A third request starts an attempt at the same edge.

    5. Edge 6 · t=55

      One ack passes two attempts. It is the last chance for the attempt from 25, which passes late. It is also the first chance for the attempt from 45, which passes early.

    Read the lanes as text
    • req @15, from edge 2 (t=15): thread 1 (to edge 3 (t=25)) dies at edge 3 (t=25); thread 2 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 3 (to edge 5 (t=45)) fails at edge 5 (t=45).
    • req @25, from edge 3 (t=25): thread 1 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 2 (to edge 5 (t=45)) dies at edge 5 (t=45); thread 3 (to edge 6 (t=55)) passes at edge 6 (t=55).
    • req @45, from edge 5 (t=45): thread 1 (to edge 6 (t=55)) passes at edge 6 (t=55); thread 2 (to edge 7 (t=65)) is dropped at edge 6 (t=55), because the attempt has already passed; thread 3 (to edge 8 (t=75)) is dropped at edge 6 (t=55), because the attempt has already passed.

    Sampled values, edge 1 to 8: req 0 1 1 0 1 0 0 0; ack 0 0 0 0 0 1 0 0.

    The program behind the lanes

    The lanes come from running this program on Verilator 5.052. Each lane is a separate copy of ack_soon that can start only at that lane's edge, so every verdict and its edge are read from that copy's own output.

    module tb;
      logic clk = 0;
      always #5 clk = ~clk;                 // rising edges at 5, 15, 25, ...
      logic req = 0, ack = 0;
    
      ack_soon: assert property (@(posedge clk) req |-> ##[1:3] ack);
    
      initial begin
        @(negedge clk); req = 1;            // requests sampled at 15 and 25
        @(negedge clk);
        @(negedge clk); req = 0;
        @(negedge clk); req = 1;            // request sampled at 45
        @(negedge clk); req = 0; ack = 1;   // one ack, sampled at 55
        @(negedge clk); ack = 0;
        repeat (2) @(negedge clk);          // edges at 65 and 75
        $finish;
      end
    endmodule
    

    Any thread that finds ack passes its attempt. An attempt fails only when its last thread finds nothing.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "It reports late because assertions are evaluated at the end of the time step."

    Each edge is evaluated at that edge. The delay is logical: the attempt is still open while any thread is alive.

    Source Mehta, SystemVerilog Assertions and Functional Coverage (3rd ed.), §8.5 (PDF p132)

  3. What IfA Response Window of a Thousand CyclesL1 JuniorL2 Mid-Level

    What if the window is ##[1:1000]? What does that do to debug and to run time?

    Reveal the Model Answer

    A missing ack is reported 1000 cycles after the request, far from the cause in the log. Every open attempt also keeps its threads alive until the window ends, which slows simulation. Use the tightest window the spec allows. For long latencies, a scoreboard timeout with a clear message is easier to debug.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "A wide window is safer, because it never fails falsely."

    It also reports real failures late and costs run time. The window should come from the spec latency.

  4. Break ItWhy One Response Satisfies Every Open WindowL1 JuniorL2 Mid-Level

    Asked if requests can overlap

    Why can one ack pass several overlapping attempts of req |-> ##[1:4] ack?

    Reveal the Model Answer

    Each attempt only asks whether ack is true at some edge of its own window. The attempts are evaluated independently, and none of them uses up the ack. So that one edge satisfies every window that contains it. Counting responses needs state outside the property, a ticket or a queue.

    One Ack Passes Three AttemptsOne lane per request for req |-> ##[1:4] ack. Each attempt forks a thread per delay of the range.req |-> ##[1:4] ackMeasured on Verilator 5.052: one copy of the assertion per lane
    attempt startspassthread dies; the attempt goes onthread dropped: the attempt has already passed
    One ack passes three attemptsreq @15, from edge 2 (t=15): thread 1 (to edge 3 (t=25)) dies at edge 3 (t=25); thread 2 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 3 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 4 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed. req @25, from edge 3 (t=25): thread 1 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 2 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 3 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 4 (to edge 7 (t=65)) is dropped at edge 5 (t=45), because the attempt has already passed. req @35, from edge 4 (t=35): thread 1 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 2 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 3 (to edge 7 (t=65)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 4 (to edge 8 (t=75)) is dropped at edge 5 (t=45), because the attempt has already passed.edgetime15215325435545655765875clkreqackattemptsreq @15req @15: dies at edge 3 (t=25)req @15: dies at edge 4 (t=35)req @15: passes at edge 5 (t=45)req @25req @25: dies at edge 4 (t=35)req @25: passes at edge 5 (t=45)req @35req @35: passes at edge 5 (t=45)
    1. Edge 2 · t=15

      The first request starts an attempt with four threads, one for each edge from 25 to 55.

    2. Edge 4 · t=35

      Two more requests have started their own attempts. No ack yet, so the earliest threads die, but every attempt still has threads looking.

    3. Edge 5 · t=45

      One ack at 45. Every attempt has a thread looking at this edge, so all three pass here, together.

      More context

      Nothing in ack says which request it answers. The two lost responses are never missed.

    4. Edge 8 · t=75

      The windows close with no further ack, and nothing fails.

    Read the lanes as text
    • req @15, from edge 2 (t=15): thread 1 (to edge 3 (t=25)) dies at edge 3 (t=25); thread 2 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 3 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 4 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed.
    • req @25, from edge 3 (t=25): thread 1 (to edge 4 (t=35)) dies at edge 4 (t=35); thread 2 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 3 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 4 (to edge 7 (t=65)) is dropped at edge 5 (t=45), because the attempt has already passed.
    • req @35, from edge 4 (t=35): thread 1 (to edge 5 (t=45)) passes at edge 5 (t=45); thread 2 (to edge 6 (t=55)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 3 (to edge 7 (t=65)) is dropped at edge 5 (t=45), because the attempt has already passed; thread 4 (to edge 8 (t=75)) is dropped at edge 5 (t=45), because the attempt has already passed.

    Sampled values, edge 1 to 8: req 0 1 1 1 0 0 0 0; ack 0 0 0 0 1 0 0 0.

    The program behind the lanes

    The lanes come from running this program on Verilator 5.052. Each lane is a separate copy of acked that can start only at that lane's edge, so every verdict and its edge are read from that copy's own output.

    module tb;
      logic clk = 0;
      always #5 clk = ~clk;                 // rising edges at 5, 15, 25, ...
      logic req = 0, ack = 0;
    
      // Ticket counters: a request takes ticket number issued, the next ack serves ticket served.
      int unsigned issued = 0, served = 0;
      always @(posedge clk) begin
        if (req) issued <= issued + 1;
        if (ack) served <= served + 1;
      end
    
      acked: assert property (@(posedge clk) req |-> ##[1:4] ack);
    
      initial begin
        @(negedge clk); req = 1;            // requests at 15, 25, 35
        repeat (2) @(negedge clk);
        @(negedge clk); req = 0; ack = 1;   // one ack, at 45
        @(negedge clk); ack = 0;
        repeat (3) @(negedge clk);          // edges at 55 ... 75
        $finish;
      end
    endmodule
    

    The lanes converge: one response, three passes.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The first attempt uses up the ack, so the second one fails."

    Attempts share no state; an ack is a value at an edge, not a token that one attempt can take.

    Source IEEE 1800-2017 §16.12.7

  5. CompareA Range vs One Assertion per LatencyL1 JuniorL2 Mid-Level

    Compare req |-> ##[1:3] ack with three assertions req |-> ##1 ack, req |-> ##2 ack and req |-> ##3 ack.

    Reveal the Model Answer

    They say different things. The range demands at least one ack somewhere in the window. Three fixed-delay assertions demand ack at every one of those edges, so a design that answers once fails two of them. To split a range for debug, the honest equivalent is a sequence or of the fixed delays, not separate assertions.

    Design answers once, at +2Verdict
    req |-> ##[1:3] ackPass
    req |-> ##1 ackFail
    req |-> ##2 ackPass
    req |-> ##3 ackFail

    Three fixed-delay assertions demand an ack at every edge of the window, not at one of them.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Splitting a range into fixed delays is the same rule, just easier to read."

    Separate assertions are ANDed by being separate; a range is an OR of its delays.

    Source IEEE 1800-2017 §16.9.7

    Tool as Truth

    "The or form works the same on any simulator."

    Verilator 5.052 misses failures of sequence or when both branches fail, so the range is also the form that engine checks correctly.

    Source ASIC Point engine notes (Verilator 5.052 SVA probes), row "sequence or: failure when both branches fail"

  6. Prove ItSolve 6.6.8: Answer in TimeSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem 6.6.8

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Misconception

"##[1:3] ack means ack stays high for one to three cycles."

The range is a window in which ack must occur once. In the twin a four-cycle ack passes; bounding the length needs a separate rule, which the fix adds.

Run It

The spec says ack lasts one to three cycles. The design holds it for four.

RUNVerilator · simulator service
Edit the code, then run it
Expected: FAIL: a four-cycle ack passed; the range is a window for the first ack, not a length
See the Fix
module tb;
  logic clk = 0, req = 0, ack = 0;
  int fails = 0;
  always #5 clk = ~clk;
  // Spec: ack lasts one to three cycles.
  // The fix: a separate rule bounds the length: after three cycles of ack, ack must drop.
  a_ack: assert property (@(posedge clk) $rose(ack) ##1 ack[*2] |=> !ack)
    else begin fails++; $display("t=%0t a_ack failed", $time); end
  initial begin
    @(negedge clk) req = 1;
    @(negedge clk) begin req = 0; ack = 1; end   // ack held for four cycles: a bug
    repeat (4) @(negedge clk);
    ack = 0;
    repeat (3) @(negedge clk);
    if (fails > 0) $display("PASS: the length rule caught the four-cycle ack");
    $finish;
  end
endmodule

Prints: t=55 a_ack failed PASS: the length rule caught the four-cycle ack

Misconception

"The attempt fails at the first edge without ack."

A thread dying does not fail the attempt while other threads are alive. The run reports its only failure at 85, the last edge of the window.

Source IEEE 1800-2017 §16.7

Wrong Altitude

"Make every window ##[1:$]; it can never be too short."

An unbounded window under assert is weak, so a missing ack is never reported. Every open attempt also costs simulation time.

Source IEEE 1800-2017 §16.12.2

Learn It, Then Prove It