Skip to practice questions

Part 3 · Assignments and intent

Assignment timing and procedural intent

Distinguish evaluation from update time and use specialized procedural blocks and case semantics to state hardware intent precisely.

Reading path
3 of 3
Concept chapters
2
Practice links
6

Question-first preparation

Practice the mechanisms on this page.

Each question maps directly to one of the chapters below, so you can test the contract before reading the explanation.

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.

Rule to say firstUse blocking for local combinational calculation, nonblocking for clocked state, and compute an explicit evaluate/update timeline whenever a delay appears.

Evaluation versus update

Four statements, four temporal contracts

t0
RHS bRHS bRHS bRHS b
current slot
LHS updateNBA pendingprocess blockedprocess continues
t+5
LHS updateNBA LHS update
  1. 01a = bevaluate + update now

    Caller blocks only for expression execution.

  2. 02a <= bevaluate now · NBA update

    Process continues; update commits later in the slot.

  3. 03a = #5 bevaluate now · wait · update

    Intra-assignment delay blocks the process until the assignment completes.

  4. 04a <= #5 bevaluate now · schedule update

    The caller does not wait for the delayed NBA update.

a = bevaluate + update now

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.

01

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.

02

Decide

Match assignment semantics to the hardware contract rather than applying a keyword mechanically outside its intended process.

03

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.

04

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.

Separate next-state calculation from state updatesystemverilog
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;
end

Delay 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.

Corrected absolute-time walkthrough
Statement reachedRHS sampledLHS updatedProcess resumes
t0: #5;t5
t5: a = #4 b;t5t9t9
t9: #5;t14
t14: $displayt14t14

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

Strong answer

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.

Reason it through
  1. Record the statement execution time.
  2. Snapshot the RHS at that time.
  3. Apply the stored value when the scheduled LHS update occurs.
Interviewer lens

The key is separating evaluation from update.

Common trap

Re-evaluating the RHS at NBA commit time.

Strong answer

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.

Reason it through
  1. Place Active, Inactive, and NBA in order.
  2. Identify every other process touching the signal.
  3. Use a real ownership or clocking contract instead of scheduler tricks.
Interviewer lens

A strong answer names the Inactive region and warns about race dependence.

Common trap

Using #0 to make testbench timing appear deterministic.

06 · Process and decision intent

Let always and case variants state promises that tools can check.

always_comb, always_ff, always_latch, unique, unique0, priority, case, casez, and inside are contracts about ownership, completeness, and legal overlap—not decoration.

Rule to say firstChoose the strictest construct whose promise is true, assign every combinational output on every path, and keep X/Z matching explicit.

Intent matrix

Process ownership and decision completeness

  1. 01always_combcomplete transform

    Implicit sensitivity and single procedural writer intent.

  2. 02always_ffsampled state

    One event control and one sequential owner.

  3. 03always_latchintentional retention

    Incomplete assignment is the storage contract.

  4. 04unique / prioritychecked promise

    Overlap and missing-match semantics guide simulation and synthesis.

always_combcomplete transform

Implicit sensitivity and single procedural writer intent.

case
Exact 4-state
X and Z must match exactly.
casez
Z/? wildcard
A selector Z may be masked; use with caution.
casex
X/Z wildcard
Usually too permissive for synthesizable control decode.

Intent keywords allow simulators, linters, formal tools, and synthesis to challenge an incorrect assumption early.

01

Observe

List all read and written variables, every decision branch, legal overlap, missing cases, reset behavior, and X/Z policy.

02

Decide

Use always_comb for complete combinational logic, always_ff for one event-controlled state owner, and exact case unless wildcard behavior is truly part of the protocol.

03

Failure

priority does not prevent latch inference, and casez can let a Z in the selector masquerade as a don't-care match.

04

Prove

Lint process restrictions, assert onehot/onehot0 as appropriate, drive X/Z selector tests, and review synthesized priority and parallel structures.

Process variants encode ownership

always_comb executes once at time zero, infers sensitivity through called functions, and forbids other processes from writing its variables. always_ff restricts event controls and represents sequential ownership. always_latch communicates intentional level-sensitive storage.

Defaults make completeness visiblesystemverilog
always_comb begin
  grant = '0;
  error = 1'b0;

  unique0 case (request)
    4'b0001: grant = 4'b0001;
    4'b0010: grant = 4'b0010;
    4'b0100: grant = 4'b0100;
    4'b1000: grant = 4'b1000;
    4'b0000: ; // no request is legal
    default: error = 1'b1;
  endcase
end

Decision modifiers are promises, not fixes

unique promises no more than one match and normally expects a match; unique0 permits no match. priority promises that the first matching branch wins and normally expects at least one match. Violating those promises can produce warnings, simulation X behavior, or synthesis optimization surprises.

Decision behavior
ConstructOverlapNo matchPrimary review risk
plain caseFirst matching itemNo assignment unless defaultLatch if output retains
unique caseViolationViolation without defaultPromise must be true
unique0 caseViolationAllowedExplicit idle encoding
priority caseFirst match winsViolation without defaultPriority depth and completeness
casezZ/? wildcardsNormal case behaviorMasks selector Z
case insideWildcard set membershipNormal case behaviorUse explicit intended wildcards

Unknown handling belongs in the specification

Exact equality and plain case keep X/Z visible. Wildcard equality, casez, and casex intentionally ignore some unknown bits. Use that power only for encoded don't-care patterns, not to silence an uninitialized selector.

  • Prefer inside or case inside when wildcard intent belongs to case items rather than the selector.
  • Add an explicit default that drives a safe output and reports illegal control state.
  • priority does not assign missing branches and therefore cannot prevent a latch.
  • unique does not guarantee faster hardware; it licenses assumptions whose truth must be verified.

Explain it out loud

Interview reasoning checkpoints

Strong answer

It adds stronger intent: execution once at time zero, sensitivity through referenced functions, and restrictions that support one procedural writer and complete combinational behavior.

Reason it through
  1. List outputs and assign defaults before branches.
  2. Confirm no other process writes those outputs.
  3. Use lint to detect incomplete assignment and prohibited timing controls.
Interviewer lens

Mention at least one semantic guarantee beyond inferred sensitivity.

Common trap

Assuming always_comb automatically fills missing assignments or prevents latches.

Strong answer

casez treats Z and ? as wildcards in both the selector and items, so a floating or uninitialized selector bit can match a valid branch. Plain case or case inside keeps unknown handling more explicit.

Reason it through
  1. Identify which side contains intended don't-care bits.
  2. Preserve X/Z on the selector unless the protocol explicitly permits them.
  3. Test the decoder with X and Z, not just all binary encodings.
Interviewer lens

The important point is selector masking, not simply that wildcard cases exist.

Common trap

Using casex to make simulation warnings disappear.