05 · Assignment and delay semantics
Track RHS evaluation separately from LHS update.
Blocking and nonblocking assignments, statement delays, intra-assignment delays, continuous assignments, and procedural assignments each place evaluation and update at different times.
Evaluation versus update
Four statements, four temporal contracts
- →
- →
- →
- 01a = bevaluate + update now
Caller blocks only for expression execution.
- 02a <= bevaluate now · NBA update
Process continues; update commits later in the slot.
- 03a = #5 bevaluate now · wait · update
Intra-assignment delay blocks the process until the assignment completes.
- 04a <= #5 bevaluate now · schedule update
The caller does not wait for the delayed NBA update.
Caller blocks only for expression execution.
- NBA RHS
- Immediate
- It is captured when the statement executes, not when the LHS updates.
- #0
- Inactive region
- Runs before NBA; it does not emulate sequential state.
- Continuous assign
- Net driver
- It continuously reacts to RHS changes and is distinct from legacy procedural continuous assignment.
Every delayed assignment needs two timestamps: RHS evaluation and LHS update. Add process-resume time when the statement blocks.
Observe
For every statement, mark when execution reaches it, when the RHS is sampled, which event region receives the update, and when the process can continue.
Decide
Match assignment semantics to the hardware contract rather than applying a keyword mechanically outside its intended process.
Failure
Mixing blocking and nonblocking writes to shared state creates order-dependent behavior, while a delayed NBA can preserve an old RHS longer than expected.
Prove
Write a time-slot table, instrument $display and $strobe deliberately, assert cycle relationships, and prohibit multiple writers with lint.
Blocking and nonblocking express dependency
Blocking assignment updates immediately, so later statements in the same process see the new value. Nonblocking assignment captures its RHS and schedules the LHS for NBA, so all clocked registers can sample the old state before updates commit.
always_comb begin
next_count = count;
if (enable) next_count = count + 1'b1;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) count <= '0;
else count <= next_count;
endDelay placement changes process behavior
A statement delay such as #5 a = b waits before evaluating and assigning. An intra-assignment delay such as a = #5 b evaluates b immediately, waits, then assigns. For nonblocking intra-assignment delay, evaluation is immediate and the future update is scheduled without blocking the caller.
| Statement reached | RHS sampled | LHS updated | Process resumes |
|---|---|---|---|
| t0: #5; | — | — | t5 |
| t5: a = #4 b; | t5 | t9 | t9 |
| t9: #5; | — | — | t14 |
| t14: $display | t14 | — | t14 |
Continuous and procedural assignment are different models
A continuous assign is a persistent net driver whose RHS is reevaluated when dependencies change. A procedural assignment executes only when control reaches it. Legacy procedural continuous assign/deassign exists but should not be confused with an assign statement outside a procedure and is rarely appropriate in modern code.
- Use always_comb for combinational procedural logic and assign for simple net equations.
- Use always_ff with nonblocking assignments for modeled sequential state.
- Avoid delayed RTL assignments in synthesizable logic; model physical delay in timing-aware flows.
- If two forked branches wake in the same region, a blocking write in one and RHS evaluation in another can race.
Explain it out loud
Interview reasoning checkpoints
When execution reaches the statement. The computed value is saved, and only the LHS update is deferred to the NBA region or specified future time.
- Record the statement execution time.
- Snapshot the RHS at that time.
- Apply the stored value when the scheduled LHS update occurs.
The key is separating evaluation from update.
Re-evaluating the RHS at NBA commit time.
No. #0 reschedules a process into the Inactive region of the current time slot, which still precedes NBA updates. It can move a race rather than remove it.
- Place Active, Inactive, and NBA in order.
- Identify every other process touching the signal.
- Use a real ownership or clocking contract instead of scheduler tricks.
A strong answer names the Inactive region and warns about race dependence.
Using #0 to make testbench timing appear deterministic.
