← Design Verification Interviews

Interview Question · Code Writing

Recovering a Write Through a Base Handle

L1 Juniorsystemverilogoopcasting

The Question

The interviewer asks

A mailbox carries base_txn handles, and some of the objects are wr_txn. Write the receiving loop that processes writes and skips everything else.

Reveal the Model Answer

You cannot assign a base handle to a derived handle without a cast. So call $cast(w, b) as a function and branch on its result. On success, w refers to the same object through the derived type; nothing is converted. On failure, $cast returns 0 and leaves w as it was, so code that ignores the result keeps using the previous write.

A Cast That Succeeds, Then One That FailsTwo base handles from one stream, cast to a derived handle e. Arrows between handles and objects are references; badges on the other arrows are the handle values that $cast checks and copies.
A cast that succeeds, then one that failsTwo base handles from one stream, cast to a derived handle `e`. Arrows between handles and objects are references; badges on the other arrows are the handle values that `$cast` checks and copies. handle stream[1] axi_txn handle handle stream[2] axi_txn handle object Object, ID 2 err_txn, err_beat 3 object Object, ID 3 plain axi_txn check $cast(e, ...) checks the objecttype handle e err_txn handle refers torefers tosourcesourcecopied on successrefers to
  1. Two base handles

    Both entries are axi_txn handles. Entry 1 refers to an err_txn object; entry 2 refers to a plain axi_txn.

  2. The cast succeeds

    $cast(e, stream[1]) checks the object, not the handle. The object is an err_txn, so the cast returns 1 and e refers to that same object. No object is created.

    Follow the calls, values and waits
    1. data handle to ID 2: stream[1] → $cast(e, ...)
    2. data handle to ID 2: $cast(e, ...) → e
  3. The cast fails

    $cast(e, stream[2]) finds a plain axi_txn. The function form returns 0 and leaves e as it was, still on ID 2.

    Follow the calls, values and waits
    1. data handle to ID 3: stream[2] → $cast(e, ...)
Read the diagram as text
  • stream[1]: axi_txn handle
  • stream[2]: axi_txn handle
  • Object, ID 2: err_txn, err_beat 3
  • Object, ID 3: plain axi_txn
  • $cast(e, ...): checks the object type
  • e: err_txn handle
  • stream[1] → Object, ID 2: refers to
  • stream[2] → Object, ID 3: refers to
  • stream[1] → $cast(e, ...): source
  • stream[2] → $cast(e, ...): source
  • $cast(e, ...) → e: copied on success
  • e → Object, ID 2: refers to

A cast moves a handle or leaves it alone. It never builds or changes an object.

Branch on the return value; never on the destination.
base_txn b;
wr_txn   w;
forever begin
  mb.get(b);
  if ($cast(w, b)) process_write(w);   // the same object, seen through wr_txn
  else             skipped++;          // w was not touched
end

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer writes if ($cast(w, b)) and says what w still holds after a failed cast.

Cross-Questions

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

WhyWhat IfProve ItCompareScale It

  1. Recovering a Write Through a Base Handle
  2. WhyWhy the Compiler Refuses a DowncastL1 Junior

    Why can the compiler accept b = w; but not w = b;?

    Reveal the Model Answer

    Every wr_txn is a base_txn, so storing a derived handle in a base variable is always safe. A base variable might hold any subclass, or a plain base_txn. Only the object present at run time can say whether it is a wr_txn. So the check has to happen at run time, which is what $cast does.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "b = w; copies the write into a smaller base object."

    Nothing is sliced or copied: b refers to the full wr_txn object, and a later $cast can get the derived view back.

    Source IEEE 1800-2017 §8.16

  3. What IfA Cast That Succeeds on NothingL1 JuniorL2 Mid-Level

    Asked if they say a successful cast means there is an object

    What if the base handle is null? Does $cast(w, b) fail?

    Reveal the Model Answer

    No: casting a null handle succeeds and sets the destination to null, because null is a legal value of every class type. A loop that treats success as "I have a write" then dereferences null. Check for null before the cast when a null can arrive.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "A successful $cast guarantees an object."

    It guarantees only that the value fits the destination type, and null fits every class type. The twin casts a null handle and the cast returns 1.

    Run It

    An unconstructed base handle passed to $cast.

    RUNVerilator · simulator service
    Edit the code, then run it
    Expected: FAIL: $cast succeeded on a null source, so w is null
    See the Fix
    class base_txn; int id; endclass
    class wr_txn extends base_txn; int data; endclass
    module tb;
      base_txn b;                   // never constructed: null
      wr_txn w;
      initial begin
        if ($cast(w, b) && w != null) begin
          $display("FAIL: should not get here");
        end
        else $display("PASS: the null handle was rejected before use");
      end
    endmodule
    

    Prints: PASS: the null handle was rejected before use

  4. CompareCasting in the Consumer vs a Virtual MethodL2 Mid-Level

    Compare a consumer that tests each object with $cast against putting a virtual process() method on the transaction classes.

    Reveal the Model Answer

    A virtual process() keeps behaviour with the data, so a new transaction type needs no change in the consumer. A $cast chain puts the knowledge in the consumer. Every new type means editing every chain, and a forgotten branch silently skips objects. Casting is still right when the consumer needs fields that only one subclass has, such as a scoreboard comparing write data.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Casting is bad style; never downcast."

    Some code genuinely needs derived fields, and UVM's own copy and compare hooks downcast their argument. The design question is where the knowledge lives.

  5. Scale ItA Scoreboard for a Dozen Transaction TypesL3 Senior

    Your subsystem scoreboard handles twelve transaction subtypes through long $cast chains, and each new type has broken it once. How do you restructure it?

    Reveal the Model Answer

    Move the knowledge about each type into the type. Each subclass overrides virtual compare and convert2string. The scoreboard matches by key and calls them through base handles. Keep one explicit cast, with an error on failure, at the boundary where the monitor hands objects over. Add a check that every subtype produced by the monitors is covered, so a new type cannot be silently ignored.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer turns a repeated breakage into a structural rule. It adds a coverage or error check that fails loudly when a new type appears.

    Wrong Altitude

    Proposes adding a default branch that logs "unknown type" at info level.

    That keeps the silent skip and only adds a log line nobody reads. An unknown type in a scoreboard is an error.

  6. Prove ItSolve 4.5.3: Stale Cast DestinationSimulator-graded

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

    Open Problem 4.5.3

What People Get Wrong

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

Misconception

"A failed $cast sets the destination to null, so test w != null."

A failure leaves the destination untouched. In the twin the read arrives after a write, the cast fails, and the stale write is processed again.

Run It

A write then a read arrive; the loop casts with the result discarded and tests the handle.

RUNVerilator · simulator service
Edit the code, then run it
Expected: writes processed: 2 FAIL: the failed cast left w pointing to the previous write, so it was processed twice
See the Fix
class base_txn; int id; endclass
class wr_txn extends base_txn; int data; endclass
class rd_txn extends base_txn; int addr; endclass
module tb;
  mailbox #(base_txn) mb = new;
  base_txn b;
  wr_txn w, tmp_w;
  rd_txn r;
  int writes = 0;
  initial begin
    tmp_w = new; tmp_w.data = 5; mb.put(tmp_w);
    r = new; mb.put(r);
    repeat (2) begin
      mb.get(b);
      if ($cast(w, b)) writes++;          // the fix: act only when the cast succeeds
    end
    $display("writes processed: %0d", writes);
    if (writes == 1) $display("PASS: the read was skipped");
    else $display("FAIL: the failed cast left w pointing to the previous write, so it was processed twice");
  end
endmodule

Prints: writes processed: 1 PASS: the read was skipped

Misconception

"w = b; works as long as the object really is a wr_txn."

The compiler checks assignments against the declared types. A base handle might point to any subclass, so this assignment never compiles.

Source IEEE 1800-2017 §8.16

Half-Truth

"Call $cast as a task; it is shorter."

As a task, a failed cast is a run-time error. That suits a cast that must succeed, not a filter that expects some objects to fail.

Source IEEE 1800-2017 §6.24.2

Buzzword Without Mechanism

Calls it "converting the object to a write".

No object changes. $cast checks the object's type and copies the handle into a variable of the narrower type.

Learn It, Then Prove It