← Design Verification Interviews

Interview Question · Code Writing

A Task That Waits on the Caller's Flag

L1 Juniorsystemverilogtasksarguments

The Question

The interviewer asks

Write a task wait_for(flag) that blocks until a flag, set later by another process, goes high. Why does the obvious version with an input argument hang?

Reveal the Model Answer

An input argument is copied when the task is called. So the task waits on its own frozen copy and never sees the caller's variable change. Declare it ref bit f, which passes a reference, and make the task automatic, because ref is only allowed in automatic subroutines. Add const (const ref) when the task must read but never write the variable.

Two Waits for Ready: A Copy and a ReferenceTwo tasks wait for ready, both called at time 0. One takes an input argument, the other a ref. Arrows show where values flow: into a variable when it is written, and out of it when a task reads it.
Two waits for ready: a copy and a referenceTwo tasks wait for `ready`, both called at time 0. One takes an `input` argument, the other a `ref`. Arrows show where values flow: into a variable when it is written, and out of it when a task reads it. process initial block #32 ready = 1 variable ready module variable task wait_ref ref bit rdy variable rdy of wait_copy a local copy task wait_copy input bit rdy writesread through refcopied at the callreads its copy
  1. Both calls startt=0 (illustrative)

    The input argument copies the current ready, 0, into a local rdy. The ref argument copies nothing: inside wait_ref, rdy is another name for ready.

    Follow the calls, values and waits
    1. data 0: ready → rdy of wait_copy
    2. Waiting: wait_ref — waits for edge 1
    3. Waiting: wait_copy — waits for edge 1
  2. Edges 1 to 3t=5, 15, 25 (illustrative)

    On each edge, both tasks test rdy. Both read 0, so both wait for the next edge.

    Follow the calls, values and waits
    1. data 0: ready → wait_ref
    2. data 0: rdy of wait_copy → wait_copy
  3. ready risest=32 (illustrative)

    The initial block writes 1 into ready. Nothing copies it again, so the rdy of wait_copy keeps the 0 from the call.

    Follow the calls, values and waits
    1. data 1: initial block → ready
  4. Edge 4t=35 (illustrative)

    wait_ref reads ready itself, finds 1 and returns: ready seen on edge 4. wait_copy reads its copy and still finds 0.

    Follow the calls, values and waits
    1. data 1: ready → wait_ref
    2. data 0: rdy of wait_copy → wait_copy
    3. Waiting: wait_copy — waits for edge 5
    4. Released: wait_ref
    5. Released: wait_copy
  5. Edge 8t=75 (illustrative)

    wait_copy has read 0 on all eight edges. It gives up and returns edges as -1.

    Follow the calls, values and waits
    1. data 0: rdy of wait_copy → wait_copy
    2. Released: wait_copy
Read the diagram as text
  • initial block: #32 ready = 1
  • ready: module variable
  • wait_ref: ref bit rdy
  • rdy of wait_copy: a local copy
  • wait_copy: input bit rdy
  • initial block → ready: writes
  • ready → wait_ref: read through ref
  • ready → rdy of wait_copy: copied at the call
  • rdy of wait_copy → wait_copy: reads its copy

An input is a snapshot taken at the call. A ref follows the caller's variable.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer explains that an input is copied at the moment of the call. It writes task automatic wait_for(ref bit f).

Cross-Questions

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

WhyWhat IfProve ItCompare

  1. A Task That Waits on the Caller's Flag
  2. WhyWhy Ref Arguments Need an Automatic SubroutineL2 Mid-Level

    Why does the language forbid ref arguments in static tasks?

    Reveal the Model Answer

    A static task has one shared argument slot for all calls. Two overlapping calls would each want that slot to refer to a different variable, and the second would silently re-point the first. Automatic tasks allocate a fresh slot per call, so every reference stays tied to its own caller.

    Self-Check

    Tick each key point you covered in your answer.

    Tool as Truth

    "It is a simulator limitation that newer tools lift."

    It is a language rule, not a tool restriction.

    Source IEEE 1800-2017 §13.5.2

  3. What IfWhat a Const Ref Argument Still SeesL1 JuniorL2 Mid-Level

    What if you pass a large array as const ref? What does that buy, and what can still change it?

    Reveal the Model Answer

    const ref passes the array without a copy, and the compiler refuses writes to it inside the routine. The routine still reads the caller's variable. So changes the caller makes while the routine runs are visible to it. It cannot change the array by mistake.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "const ref is a snapshot of the array at the call."

    It is the caller's variable, made read-only. Later changes by the caller are visible inside the routine.

    Source IEEE 1800-2017 §13.5.2

  4. CompareTask or FunctionL0 FresherL1 Junior

    Compare a task with a function. Could wait_for have been a function?

    Reveal the Model Answer

    A function runs in zero simulation time and may not contain #, @ or wait, so wait_for must be a task. A function can return a value and appear inside an expression. A task returns results through output arguments and is called as a statement. Use a void function for zero-time work with no result, so the compiler guarantees it never blocks.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Functions cannot call tasks at all."

    A function may not call a task that could block. It may start one inside fork ... join_none, because the function itself does not wait.

    Source IEEE 1800-2017 §13.4

  5. Prove ItSolve 3.1.1: Popcount FunctionSimulator-graded

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

    Open Problem 3.1.1

  6. What IfA Ref Argument That Changed Under the TaskL1 JuniorL2 Mid-Level

    What if the task reads a ref array, waits a few cycles, then reads it again for a checksum?

    Reveal the Model Answer

    The second read sees whatever the caller wrote in between, because a reference is live. If the task needs a stable snapshot, copy the array into a local variable at the start. Or pass it by value.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "const ref freezes the array for the duration of the call."

    const only stops the task from writing. The caller can still change the variable, and the task sees the change.

    Source IEEE 1800-2017 §13.5.2

What People Get Wrong

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

Misconception

"An input argument tracks the caller's variable while the task waits."

Input arguments are copied once at the call. The twin waits on an input copy and times out; the ref version wakes at t=5.

Run It

A task waiting on its argument while another process raises the flag at t=5.

RUNVerilator · simulator service
Edit the code, then run it
Expected: t=20 woke=0 FAIL: the input argument was a copy taken at the call, so it never saw flag rise
See the Fix
module tb;
  bit flag = 0;
  bit woke = 0;
  // The fix: ref, so the task watches the caller's variable.
  task automatic wait_for(ref bit f);
    wait (f == 1);
    woke = 1;
  endtask
  initial begin
    fork
      wait_for(flag);
      #20;                     // give up after 20
    join_any
    $display("t=%0t woke=%0d", $time, woke);
    if (!woke) $display("FAIL: the input argument was a copy taken at the call, so it never saw flag rise");
    else $display("PASS: the ref argument saw flag rise at t=5");
    $finish;
  end
  initial #5 flag = 1;
endmodule

Prints: t=5 woke=1 PASS: the ref argument saw flag rise at t=5

Misconception

"Use inout; it passes by reference."

inout copies in at the call and copies back out at the return. While the task is waiting, it still sees its private copy.

Source IEEE 1800-2017 §13.3

Spec Misquote

"ref works in any task."

A ref argument is legal only in a subroutine with automatic lifetime. That is why a task in a module needs the keyword.

Source IEEE 1800-2017 §13.5.2

Delivery

Writes the task with ref and a busy loop: while (!f) #1;.

It works, but polls every time unit. wait (f) sleeps until the variable changes, which is what the interviewer is looking for.

Learn It, Then Prove It