← Design Verification Interviews

Interview Question · Concept

A Random Lane Number Below Zero

L0 FresherL1 Juniorsystemverilograndomizationsystem functions

The Question

The interviewer asks

A test picks a lane with lane = $random % 4;. What is wrong, and what would you write instead?

Reveal the Model Answer

$random returns a signed 32-bit value, so $random % 4 is negative about half the time and gives lanes from -3 to 3. Use $urandom_range(3, 0), which returns an unsigned value in the inclusive range. Inside classes, randomize() and std::randomize() are better still, because they take constraints and draw from the thread's or object's own generator.

Why $random % 4 Can Go NegativeTwo ways to pick a lane from 0 to 3. Arrows show the value moving through each expression.
Why $random % 4 can go negativeTwo ways to pick a lane from 0 to 3. Arrows show the value moving through each expression. component $random a signed 32-bit value component % 4 the result keeps thesign of the leftoperand component lane from -3 to 3 negative about halfthe time component $urandom_range(3,0) unsigned, both endsincluded component lane from 0 to 3 every value legal one call
Read the diagram as text
  • $random: a signed 32-bit value
  • % 4: the result keeps the sign of the left operand
  • lane from -3 to 3: negative about half the time
  • $urandom_range(3, 0): unsigned, both ends included
  • lane from 0 to 3: every value legal
  • $random → % 4
  • % 4 → lane from -3 to 3
  • $urandom_range(3, 0) → lane from 0 to 3: one call

Inside classes, randomize() is better still, because it takes constraints and keeps random stability.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Fresher)

A good fresher answer spots that the result can be negative. It rewrites the line with $urandom_range.

Cross-Questions

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

WhyProve ItBreak ItCompare

  1. A Random Lane Number Below Zero
  2. CompareScope Randomize vs Class RandomizeL1 Junior

    Compare std::randomize(len) with { len inside {[1:16]}; } with putting len in a class and calling randomize().

    Reveal the Model Answer

    std::randomize() randomizes ordinary variables in the current scope, with constraints written at the call. It suits a one-off value in a sequence or a module. A class gives the constraints a name and a home. Tests can extend them, switch them off, freeze fields and reuse them everywhere. Use the scope form for local choices and classes for anything that describes a transaction.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "std::randomize() can only randomize class members."

    It is the scope randomization function: its arguments are ordinary variables visible at the call.

    Source IEEE 1800-2017 §18.12

  3. WhyRandom StabilityL1 JuniorL2 Mid-Level

    Why does SystemVerilog give every thread and every object its own random number generator?

    Reveal the Model Answer

    So that one part of the testbench cannot disturb another's values. Each thread and object has its own generator, seeded from its parent thread at creation. So randomizing an unrelated object does not shift this object's values. With Verilog's single shared $random stream, adding one call anywhere reshuffled everything after it.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Random stability means the same seed always reproduces the run, whatever I change."

    It protects against unrelated calls, not against changing what is created and in which order. New objects or threads shift the seeds of those created after them.

    Source Spear & Tumbush, SystemVerilog for Verification (3rd ed.), ch. 6.16.3 (PDF p258)

  4. Break ItA Harmless Edit That Moved the Failing SeedL2 Mid-Level

    A failing seed stops failing after someone adds one debug object to the environment. Nothing else changed. How?

    Reveal the Model Answer

    Each object takes its seed from the creating thread, in creation order. A new object created earlier takes the seed that a later object used to get. Every object built after it now draws different values, and the failing stimulus is gone. Seed critical objects explicitly with srandom(), or keep new objects at the end of construction.

    Same Seed, One Extra Object Created First
    0123run 1: addr3c8018fcrun 1: resultokERRORrun 2: addr9408c430run 2: resultokburst 4,512
    1. Cycle 0

      Run 1 and run 2 start from the same seed. In run 2 an extra object, created first, takes the seed the generator used to get, so every address differs.

    2. Cycle 3

      Run 1 fails at burst 4,512. In run 2 the stimulus at that point is different, and the bug stays hidden.

    The last four bursts before the failure. With a different object order, run 2 never builds the failing burst.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Creating an extra object cannot change anyone else's random values."

    It takes the next seed from the thread. The twin replays the same thread seed with one extra new and the next object's value changes; seeding it explicitly keeps it.

    Run It

    The same thread seed twice; the second build creates one extra object before b.

    RUNVerilator · simulator service
    Edit the code, then run it
    Expected: FAIL: creating one more object before b changed b's values under the same seed
    See the Fix
    class txn;
      rand int unsigned v;
    endclass
    module tb;
      txn a, b, extra;
      int unsigned first, second;
      initial begin
        // Build 1: the code that produced the failing run.
        process::self().srandom(42);
        a = new; b = new;
        b.srandom(7);                          // the fix: seed b explicitly
        void'(b.randomize()); first = b.v;
        // Build 2: the same seed after a "harmless" edit that creates one more object first.
        process::self().srandom(42);
        a = new; extra = new; b = new;
        b.srandom(7);
        void'(b.randomize()); second = b.v;
        if (first == second) $display("PASS: an explicitly seeded object keeps its values after the edit");
      end
    endmodule
    

    Prints: PASS: an explicitly seeded object keeps its values after the edit

  5. Prove ItSolve 5.6.1: Replay One Object StreamSimulator-graded

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

    Open Problem 5.6.1

What People Get Wrong

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

Misconception

"$random % 10 gives 0 to 9."

The remainder takes the sign of the signed operand, so negative draws give -9 to -1. The twin counts out-of-range values; $urandom_range has none.

Run It

100 draws of $random % 10.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: FAIL: $random is signed, so $random % 10 also gives -9..-1
See the Fix
module tb;
  int v, out_of_range = 0;
  initial begin
    repeat (100) begin
      v = $urandom_range(9, 0);         // the fix: an unsigned value in 0..9
      if (v < 0 || v > 9) out_of_range++;
    end
    if (out_of_range == 0) $display("PASS: every value was in 0..9");
  end
endmodule

Prints: PASS: every value was in 0..9

Spec Misquote

"$urandom_range(0, 3) is a bug; the maximum must come first."

If the first argument is smaller, the two are swapped automatically, so both orders give 0 to 3.

Source IEEE 1800-2017 §18.13.2

Tool as Truth

"The same seed gives the same values on every simulator."

The standard does not define the generator, so values for one seed differ between tools and versions. A seed reproduces a run only on the same tool, version and code.

Source IEEE 1800-2017 §18.13

Learn It, Then Prove It