Part 7 · Advanced & Integration · Intermediate
Q&A: Races & Scheduling
Interview-format answers — TB races, NBA vs blocking at the boundary, clocking blocks, program blocks, assertion sampling, force vs deposit — with senior vs junior contrasts.
Core questions
Q1: What is a TB race and how do you prevent it?
A race exists when two processes scheduled in the same region of the same time step touch the same signal, and the LRM leaves their order to the simulator — so two legal executions give two different results. The canonical case is a testbench blocking drive or immediate sample at the same clock edge the RTL uses. Prevention is region separation : RTL uses nonblocking assignments internally, and the TB samples and drives through clocking-block skews (inputs in Preponed, output drives in Re-NBA), so no TB access competes with RTL in the Active region. Delays like #0 or #1 only relocate the ambiguity.
Q2: Explain NBA vs blocking at the boundary.
A blocking assignment updates its target immediately, mid-Active-region — other processes at the same edge see old or new value depending on run order. A nonblocking assignment splits read and write: the right side is read now, the left side updates later in the NBA region, after every same-edge process has done its reads . That read-all-then-update-all discipline is why NBA-coded RTL is internally deterministic, and why a TB blocking write into that edge re-introduces the very ambiguity NBA removed.
Q3: What does a clocking block actually do?
Two things, both about when , not what. Inputs are sampled with #1step skew — the stable pre-edge value from the Preponed region, the same value the design's flops capture. Output drives are applied in the Re-NBA region — after all RTL reads for that edge are complete, so the design consumes them at the next edge. It does not reorder the scheduler; it moves TB sample and drive points into regions where no design process competes, making every legal ordering produce the same result. The bundled signal view, modport enforcement, and ##N cycle delays come along with it.
Differentiating questions
Q4: Program block vs clocking block — are they the same fix?
No — they attack the same race from different directions. A program block moves where TB code executes (the Reactive region, after design settling). A clocking block moves where TB signal accesses take effect (Preponed sampling, Re-NBA driving) regardless of where the code runs. Modern class-based methodology generally treats clocking blocks as the essential piece and program blocks as optional — a module-based TB that strictly samples and drives through clocking blocks is already race-free at the boundary.
Q5: Why does my assertion see a different value than my $display?
Because they read in different regions. A concurrent assertion samples its operands in Preponed — the value just before the edge. A $display inside an always_ff prints mid-Active, and $monitor/$strobe print the settled Postponed value, after NBA updates. On the edge where a counter goes 4 to 5: the assertion judges 4, an in-block $display shows 4, and $strobe shows 5. All three are correct — they are snapshots of different pipeline stages of the same time step.
Q6: Force vs deposit — when each?
Force is a continuous override: the signal is pinned and every driver loses until an explicit release — right for error injection windows and documented bring-up hacks, dangerous when forgotten because the net is dead from then on. Deposit sets a value once and lets normal drivers overwrite it at their next evaluation — right for initializing state or preloading memories. Rule of thumb: deposit to set a starting point, force-with-guaranteed-release to temporarily overrule the design, and both only through a centralized, auditable access layer.
Senior vs junior contrasts
SAME QUESTION, TWO ANSWERS
Q: "Your test passes on VCS and fails on Questa. What now?"
Junior: "File a Questa bug; VCS is our sign-off tool anyway."
Senior: "Both behaviors are probably LRM-legal — that fingerprint
means a same-region race. Find the blocking drive or raw
sample at the RTL's clock edge, route it through the
clocking block, and the test will pass on BOTH."
Q: "There's a #0 in the driver. Should I remove it?"
Junior: "It works, don't touch it."
Senior: "A #0 is a fossilized race. Find what ordering it was
papering over, fix it with cb skews or an event
handshake, then delete the #0 — otherwise the next
optimization flag flips the coin again."
Q: "Why not just force the FSM into the error state to test it?"
Junior: forces it inline in the test, no release, ships it.
Senior: adds inject/restore tasks to the hierarchy-access layer
with an ID and owner, scopes the force to one test,
releases on every exit path — and asks whether a real
stimulus sequence could reach that state instead.What interviewers are actually probing
Whether you explain races with the region model (mechanism) or with anecdotes (luck).
Whether you know clocking-block skew semantics precisely — #1step Preponed sampling is the give-away detail.
Whether you treat #0, #1, and stray forces as debt to remove, not tricks to keep.
Whether your cross-boundary access story includes audit and removal, not just capability.
Key takeaways
Answer race questions with the region pipeline — Preponed sampling, Active reads, NBA updates, Re-NBA TB drives.
Clocking blocks make ordering irrelevant; program blocks relocate code; know the difference crisply.
Assertion-vs-$display discrepancies are region snapshots, not bugs — explain which region each reads.
Senior answers always include the discipline: centralized XMRs, released forces, deleted #0s.
Common pitfalls
Saying 'add a small delay' as a race fix in an interview — instant credibility loss.
Claiming program blocks are required for race-free TBs — clocking-block discipline is the load-bearing piece.
Confusing #1step with a 1ns delay when explaining input skew.
Describing force for initialization where deposit is correct — it signals you have been burned by neither.