← Design Verification Interviews

Interview Question · Code Writing

Sending a Callback to the Right Instance

L2 Mid-LevelL3 Seniordpiscope

The Question

The interviewer asks

Two instances of a module export sv_accept. Your C function must send one log line to a central logger instance, then call sv_accept in the instance that called it. Write the C.

Reveal the Model Answer

Save the caller with svGetScope() as the first step. Switch to the logger with svSetScope, using a scope from svGetScopeFromName("tb.logger"), and call the log export. Then restore the saved scope before calling sv_accept. An export always runs in the current scope, and svSetScope changes it for the rest of the call. Look the name up once and check it for null.

Save, switch, restore. The name lookup happens once, and a missing instance returns an error.
extern "C" int c_visit(int value) {
  const svScope caller = svGetScope();          // 1. save the calling instance
  static svScope logger = svGetScopeFromName("tb.logger");   // look the name up once
  if (!logger) return -1;                       // a renamed hierarchy fails loudly
  svSetScope(logger);                           // 2. talk to the logger
  sv_log(value);
  svSetScope(caller);                           // 3. restore before the next export
  return sv_accept(value);
}

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

Saves the scope at entry and restores it before the second export.

Cross-Questions

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

What IfBreak ItCompareScale It

  1. Sending a Callback to the Right Instance
  2. What IfA Context Import Declared in a PackageL3 Senior

    Asked if the import is declared in a package

    What if the context import is declared in a package, and two module instances call it? Which scope does C see?

    Reveal the Model Answer

    The context of an import is where it is declared, so a package import has the package as its scope. Verilator 5.052 reported the calling instances, tb.u0 and tb.u1, in our probe. Code that relies on that may break on another simulator. Declare a context import inside the module whose exports it calls.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    Separates what the standard says from what one tool did, and fixes it by moving the declaration.

    Tool as Truth

    "Verilator showed tb.u0, so the scope is always the caller."

    That is one tool's behaviour. The context follows the import declaration, and a package is a scope of its own.

    Source Spear & Tumbush, SystemVerilog for Verification (3rd ed.), §12.8.5

  3. Scale ItPer-Instance C State Keyed by ScopeL3 Senior

    How can C keep separate state for each module instance without passing a handle on every call?

    Reveal the Model Answer

    Attach it to the scope. svPutUserData(scope, key, ptr) stores a pointer for that instance, and svGetUserData(scope, key) gets it back. The key is the address of a static variable, so two libraries never collide. Create the state on the first call, when the lookup returns null.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Use one static and index it by the scope name string."

    It works, but string compares on every call are slow, and the table needs its own cleanup. The standard already provides a per-scope slot.

  4. CompareScope Data Compared with a ChandleL3 Senior

    Compare scope user data with a chandle per model. Which one fits a UVM scoreboard?

    Reveal the Model Answer

    Scope data belongs to a module, interface or program instance. A UVM scoreboard is a class object, and all its calls come from the same import scope. So scope data cannot tell two scoreboards apart. A chandle from model_new() belongs to the object that holds it. Use the chandle for class-based benches.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Each class object has its own DPI scope."

    Scopes come from where the import is declared, not from objects. Two scoreboards calling the same import share it.

    Source Spear & Tumbush, SystemVerilog for Verification (3rd ed.), §12.8.4–12.8.5

  5. Break ItA Scope Name That No Longer ExistsL2 Mid-Level

    Break it: the bench is reused one level deeper, and svGetScopeFromName("tb.logger") now finds nothing. What happens?

    Reveal the Model Answer

    The lookup returns null. If C passes that to svSetScope and calls an export, the export has no scope. Verilator 5.052 stops the run with a "scope wasn't set" error. Check every lookup, and return an error code the testbench reports.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The export then runs in the caller's scope instead."

    Setting a null scope does not fall back to the caller. The export has no valid scope at all.

    Source ASIC Point engine notes (Verilator 5.052 DPI probe, 2026-11-08: svSetScope(NULL) then an export call)

What People Get Wrong

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

Misconception

"svSetScope affects only the next export call."

It changes the current scope until it is set again. The twin's accept lands in the logger, and the caller sees nothing.

Run It

C logs through tb.logger, then calls sv_accept without restoring the scope.

RUNVerilator + C/C++ over DPI · simulator service
Edit the code, then run it
Expected: u0.accepted=0 logger.accepted=3 logger.logged=1 FAIL: svSetScope stayed in force, so sv_accept ran in the logger, not in the caller
See the Fix
module acceptor;
  import "DPI-C" context function int c_visit(input int value);
  export "DPI-C" function sv_accept;
  export "DPI-C" function sv_log;
  int accepted = 0, logged = 0;
  function int sv_accept(input int value); accepted += value; return accepted; endfunction
  function void sv_log(input int value); logged++; endfunction
  function int visit(input int value); return c_visit(value); endfunction
endmodule
module tb;
  acceptor u0();
  acceptor logger();             // a central instance that keeps the log
  initial begin
    void'(u0.visit(3));
    $display("u0.accepted=%0d logger.accepted=%0d logger.logged=%0d", u0.accepted, logger.accepted, logger.logged);
    if (u0.accepted != 3) $display("FAIL: svSetScope stayed in force, so sv_accept ran in the logger, not in the caller");
    else $display("PASS: the log went to the logger and the accept went back to the caller");
    $finish;
  end
endmodule

Prints: u0.accepted=3 logger.accepted=0 logger.logged=1 PASS: the log went to the logger and the accept went back to the caller

Misconception

"svGetScope works from any import."

Only a context import has a scope. Lesson D1.7 measured a null scope from a plain import on Verilator 5.052.

Source IEEE 1800-2017 §35.5.3; lesson D1.7 review claims (null scope without context)

Wrong Altitude

Writes svGetScopeFromName("tb.env.agent0.u_if") in ten places in the C.

Hierarchy names change when the bench is reused. A missing name gives a null scope, and the next export call stops the run.

Source ASIC Point engine notes (Verilator 5.052 DPI probe, 2026-11-08: a null scope from a failed name lookup, then an export call, aborts the run)

Learn It, Then Prove It