cswork / libraryOpen practice workspace

Articles

UVM Debugging Interview Questions: Sequence Handshakes, Factory Overrides, and Scoreboards

Debug UVM interview exercises on stalled sequence handshakes, factory overrides, transaction ownership, scoreboard matching, and simulation completion.

UVM debugging interview questions ask you to locate the first broken contract in a verification environment. A sequence can be waiting for a grant, a driver can be waiting for a DUT handshake, a factory override can target the wrong creation path, or a scoreboard can compare objects whose contents changed after publication.

The fastest useful answer is a trace with transaction identity and ownership. State what should happen next, identify who must make it happen, and collect evidence at that boundary. A list of UVM class names does not explain a simulation that stopped progressing.

The scenarios below use ordinary sequence-driver handshaking and an in-order DUT unless another assumption is stated. Adapt them to the project's UVM reference implementation and protocol.

How do you start debugging a stalled sequence?

Draw the request path: sequence, sequencer, driver, interface, DUT. Then mark the last confirmed event for one transaction ID. Did the sequence begin? Did it receive a grant? Did finish_item return? Did the driver obtain the request? Did the DUT accept it?

A timestamped log at every loop iteration is usually less useful than a few logs at ownership transitions. Include sequence or component path, request identity, and the event. That gives you a causal trace rather than a wall of repeated “waiting” messages.

Start with one transaction and a bounded timeout. If the smallest test stalls, increasing sequence count or randomizing more fields only adds noise. Check reset and clock behavior early because a perfectly connected driver cannot complete a handshake with a DUT held permanently in reset.

What is the get_next_item and item_done contract?

In the common pull-driver pattern, the driver calls get_next_item to obtain a request and later calls item_done to indicate completion of that item. The sequence's item flow depends on this handshake being completed correctly.

task run_phase(uvm_phase phase);
  txn req;
  forever begin
    seq_item_port.get_next_item(req);
    drive_one(req);
    seq_item_port.item_done();
  end
endtask

This is a driver skeleton, not a complete protocol driver. drive_one must define what completion means: placing signals on the interface, the DUT accepting the transfer, or some later response. Match that meaning to the sequencer handshake and protocol.

If item_done is omitted on one branch, the next item can stall. If it is called too early, the sequence may reuse or modify an object while the driver still relies on it. If reset aborts a transfer, explicitly define how the driver completes or cancels the current request and resumes operation.

Work through a missing completion trace

Suppose logs show:

10 ns sequence: start transaction 7
12 ns driver: acquired transaction 7
18 ns driver: reset observed
1000 ns test: timeout waiting for sequence

The evidence says the sequencer-driver connection delivered the item. Investigating factory registration first would be poorly targeted. Inspect the driver's reset branch: does it leave the current request outstanding while restarting the loop or waiting for another item?

Add a log immediately before and after the protocol wait and before item completion. Then force reset at that exact point in a directed test. The repair should specify the aborted transaction's outcome and maintain a consistent handshake, not merely raise the timeout.

A different trace ending before the driver acquires the item points elsewhere: sequence start target, sequencer arbitration, driver connection, or a driver that never entered its pull loop. Follow the first missing transition instead of applying the same fix to every stall.

Why did a factory override have no effect?

Factory overrides affect objects and components created through the relevant factory mechanisms. A direct new construction bypasses that selection. The override must also be registered before the affected creation occurs, and an instance override must match the intended instance path.

base_txn req;
req = base_txn::type_id::create("req");

Compare that with directly calling new. If the test expects an extended transaction through a factory override, the construction path is part of the contract. Check registration macros, type identity, creation timing, and the path used for instance-specific matching.

Use factory diagnostics to print the configured overrides and inspect the actual dynamic type created. Do not infer success just because the test compiled or the override call executed. A type override and an instance override can also interact, so read the resulting selection rather than relying on an assumed precedence.

How do you diagnose a configuration database problem?

Treat configuration lookup as a contract among field name, type, scope, and timing. The producer must set the value in a scope visible to the consumer, and the consumer must request it with the compatible type and name before using it.

A virtual interface is a common example. A mismatch in the parameterized interface type or lookup path can make a value unavailable even when another setting with a similar name exists. Check the exact types and hierarchical context rather than adding broad wildcard settings everywhere.

Fail early when a required configuration is missing. A clear fatal message during build is easier to diagnose than a later null-handle access from the driver. Include the component path and requested setting in the diagnostic, without dumping unrelated configuration.

Wildcards can be useful, but overly broad scope may give multiple agents the same interface accidentally. Test a two-agent environment with distinct instance identities; a one-agent smoke test may never reveal that mistake.

Why can a scoreboard compare the wrong values?

UVM transactions are objects, so passing a handle does not automatically create an independent snapshot. If a monitor publishes a handle and then reuses the same object for the next transaction, a subscriber retaining that handle may later observe changed fields.

Suppose the scoreboard queues transaction 7's handle. The monitor updates the object to transaction 8 before the scoreboard compares it. A mismatch that appears to be a DUT error may actually be transaction aliasing in the testbench.

Choose an ownership policy. The monitor can allocate a new object for each published transaction, or a subscriber can copy or clone the object when retaining it. Ensure copy behavior includes the fields that matter, including nested objects where a shallow copy would still share mutable state.

Immediate analysis callbacks do not eliminate this problem if the receiver stores the handle for later. The important question is how long the object is retained relative to subsequent mutation, not whether the original method call was synchronous.

Should expected and actual transactions be compared in arrival order?

Only when the protocol and DUT guarantee that order. An in-order queue comparison is simple and useful for an in-order design. It becomes incorrect if responses can be reordered, split, merged, retried, or tagged independently.

For out-of-order responses, match using a stable transaction identifier or another protocol-defined key. Define how duplicate IDs, timeouts, partial responses, and reset affect outstanding expectations. A map keyed by ID is not enough if IDs can wrap or be reused while an older transaction remains outstanding.

DUT behaviorScoreboard requirement
Strictly in orderOrdered expected and actual queues may fit
Out of order with tagsMatch by valid tag and generation or lifetime rules
One request, several responsesTrack completion of all expected parts
Reset aborts workApply the specified flush or survival policy
Duplicate response is illegalDetect a second completion explicitly

In an interview, ask for the ordering contract before choosing the data structure. That is a correctness question, not an attempt to avoid coding.

How can the reference model be wrong?

A scoreboard's expected result is only as reliable as its model and observation point. If the reference model consumes attempted requests while the DUT consumes only accepted handshakes, their inputs already differ. Comparing their outputs will produce false failures.

Drive the predictor from the correct observed event or make acceptance explicit in its input. For a ready-valid interface, a request is typically accepted only when the protocol's handshake condition is met at the defined sampling edge. The exact rule comes from that interface specification.

Also avoid duplicating the DUT implementation so closely that both share the same bug. A simpler independently expressed model, invariants, and carefully chosen directed examples can give stronger evidence than a line-for-line software translation of the RTL.

When a mismatch occurs, record the input, expected value, actual value, relevant mode, and transaction identity. Preserve the first mismatch; later failures may only be consequences of a desynchronized checker.

Why does simulation end before checking finishes?

Phase objections control whether a task phase remains active under the relevant UVM rules. A test that drops its objection immediately after stimulus finishes may still have responses in flight or queued scoreboard comparisons.

Do not solve every early ending by inserting an arbitrary delay. Define completion: all planned stimulus sent, all required responses observed, and no unexpected outstanding expectations. Then wait for that condition with a bounded timeout before allowing the test to finish.

Likewise, an objection that is raised and never dropped can make a completed test appear hung. Log objection ownership in a focused diagnosis and inspect exceptional paths. The same principle applies as with item handshakes: every acquired responsibility needs a defined completion path.

At end of test, report unmatched expected and actual transactions. A simulation that exits without an error is not evidence of success if the checker never compared the remaining work.

How do you build a useful minimal reproducer?

Reduce to one agent, one sequence, one driver, one monitor, and the smallest checker that preserves the failure. Keep the interface protocol explicit. Replace a large random stream with a directed request that reaches the failing branch, then add only the timing condition required to reproduce it.

For a factory problem, the reproducer can be even smaller: register the base and derived types, apply one override, create one object through the factory, and print its actual type. For aliasing, publish two values using one reused object and show what a retaining subscriber sees.

The reduced test should answer one question unambiguously. Once the fix works, restore it to the full environment and keep the reproducer as a focused regression. This approach saves more time than searching thousands of lines for every UVM keyword mentioned in a timeout log.

What makes a strong UVM interview answer?

Name the contract and the last confirmed event. For a sequence stall, identify who owns the outstanding item and which completion is missing. For an override, identify the factory creation path. For a scoreboard mismatch, check input acceptance, matching rules, and object ownership before concluding that the DUT is wrong.

Practice narrating one trace from request creation to comparison and test completion. Include reset and an out-of-order response as follow-ups. The result is a debugging method you can apply to an unfamiliar environment, which is more useful than a memorized terminology checklist.

Sources and Further Reading

Research checked September 24, 2026. Handshake examples use established UVM patterns; check APIs and diagnostics against the reference implementation selected by the project.