Skip to practice questions

Subroutines, assignments, and scheduler intent

SystemVerilog Procedural Blocks

Write executable SystemVerilog whose timing, ownership, and completion semantics remain unambiguous in simulation, synthesis, and foreign-language boundaries.

A detailed guide to tasks, functions, argument passing, lifetime, program and clocking blocks, DPI, blocking and nonblocking assignments, delays, always variants, and case intent.

18 direct practice links6 connected chaptersReviewed July 2026
Event-queue debuggerSeparate evaluation, update, and process resume.
NonblockingActive: evaluate · NBA: update

The RHS is captured now and the LHS commits later in the same time slot.

Question-first preparation

Start with a curated practice sequence.

Begin with one representative question from each mechanism, then expand the complete directory when you need a targeted drill. Premium status is shown before you leave this guide.

Recommended start

One representative prompt from each major mechanism. Open any card in the live question bank.

  1. Q158Distinguish SystemVerilog Functions and TasksTiming · EasyPractice
  2. Q001Blocking and nonblocking assignmentSystemVerilog · EasyPractice
  3. Q265Always_combSystemVerilog · EasyPractice
  4. Q417SystemVerilog function timing ruleSystemVerilog · EasyPremium
  5. Q413Classify synthesizable and testbench-only constructsSystemVerilog · EasyPremium
  6. Q148Direct Programming Interface (DPI) 4-state data mappingVerification Utilities · MediumPractice
Complete question directoryFilter every stable practice link by mechanism.18 questions
Showing 18 questions across every track.Choose a track to narrow the practice set without changing its stable question links.
  1. Q158Distinguish SystemVerilog Functions and TasksTiming · EasyPractice
  2. Q417SystemVerilog function timing ruleSystemVerilog · EasyPremium
  3. Q413Classify synthesizable and testbench-only constructsSystemVerilog · EasyPremium
  4. Q148Direct Programming Interface (DPI) 4-state data mappingVerification Utilities · MediumPractice
  5. Q001Blocking and nonblocking assignmentSystemVerilog · EasyPractice
  6. Q934Procedural delaysSystemVerilog · EasyPremium
  7. Q1092Nonblocking assignment (NBA) evaluation and updateSystemVerilog · EasyPremium
  8. Q403Inactive, nonblocking assignment (NBA), and postponed regionsSystemVerilog · HardPremium
  9. Q409Delayed nonblocking assignmentsSystemVerilog · HardPremium
  10. Q943Choose Blocking and Nonblocking Assignments by Process TypeComputer Architecture · EasyPremium
  11. Q535Reason about nonblocking assignment updatesSystemVerilog · EasyPremium
  12. Q991Why one #0 is not an NBA waitSystemVerilog · MediumPremium
  13. Q265Always_combSystemVerilog · EasyPractice
  14. Q149Diagnose storage from an incomplete always_comb blockRTL Design · EasyPractice
  15. Q955Choose always_comb over a manual sensitivity listRTL Design · EasyPremium
  16. Q063Predict Priority from If and Case StatementsRTL Design · EasyPractice
  17. Q141Decode Z wildcards without hiding XRTL Design · EasyPractice
  18. Q969X and Z handling in case statementsSystemVerilog · EasyPremium

One statement · four temporal questions

Follow execution from entry through update and process resume.

A procedural statement is not fully explained until its inputs, side effects, event region, update time, and caller-visible completion are all explicit.

  1. 01Enter

    Establish argument direction, lifetime, and ownership.

  2. 02Evaluate

    Record when the RHS or expression is sampled.

  3. 03Update

    Place the LHS effect in its real region and time slot.

  4. 04Resume

    State when the caller or process may continue.

01 · Subroutine contract

Pick a function for a value and a task for an activity.

The distinction is not stylistic: a function must complete without consuming simulation time, while a task can synchronize, delay, and return several results through arguments.

Rule to say firstUse a function for zero-time computation and a task for an activity. A SystemVerilog function cannot enable a user-defined task, even when that task appears to contain no timing control.

Subroutine selector

Return value, time, and side effects

  1. 01Typed functionone return value

    Zero-time computation that can appear in an expression.

  2. 02function voidno return value

    Zero-time procedure, commonly used for model updates.

  3. 03taskactivity

    May wait, delay, synchronize, and use output/inout arguments.

Typed functionone return value

Zero-time computation that can appear in an expression.

Task return type
None
A task does not have a void return type; function void is different.
Timing
Function: zero
No #, @, wait, or user-defined task call.
Defaults
input
Argument directions should still be written explicitly for review.

Design a subroutine like a small interface: inputs, outputs, time, side effects, and ownership must all be visible.

01

Observe

List outputs, side effects, timing controls, imported calls, and whether the caller uses the result inside an expression.

02

Decide

Choose a typed function or function void for zero-time work; choose a task when event controls, delays, or activity-style completion are part of the API.

03

Failure

Hiding time in a function breaks the expression model, while a task used as a pure calculator obscures ownership and makes composition harder.

04

Prove

Lint timing controls and side effects, unit-test automatic reentrancy, and assert the caller-visible postconditions at the API boundary.

Functions compose; tasks sequence

A non-void function returns through its name or return statement and can participate in an expression. function void performs zero-time side effects without a value. A task represents an operation whose completion is a sequencing event for the caller.

Make timing visible in the declarationsystemverilog
function automatic logic [7:0] parity_mask(
  input logic [63:0] data
);
  logic [7:0] result;
  foreach (result[i]) result[i] = ^data[i*8 +: 8];
  return result;
endfunction

task automatic send_beat(
  input  logic [31:0] data,
  output bit          accepted
);
  req <= 1'b1;
  payload <= data;
  do @(posedge clk); while (!ready);
  accepted = 1'b1;
  req <= 1'b0;
endtask

Synthesis depends on behavior, not the keyword alone

A statically bounded, zero-time task can be inlined by synthesis just as a function can. This describes analyzable behavior, not static storage lifetime; an automatic task can also synthesize. Delays, event controls, dynamic testbench objects, and simulator services make a subroutine testbench-only. Review the body and call context rather than declaring every task unsynthesizable.

Function and task review
QuestionFunctionTask
Consumes time?NoMay
Used in expression?Yes, if non-voidNo
Return valuesOne typed value plus argumentsArguments only
Can callFunctions onlyFunctions and tasks
Automatic recommended?Yes for reentrancyYes for concurrent calls
Synthesizable?If body is synthesizableIf behavior is statically bounded and zero-time

Built-ins still carry precise return and mutation rules

System functions and array methods often look self-explanatory, but interview bugs hide in width, four-state treatment, sampling context, and whether the operation mutates its receiver. State those contracts before using the result.

  • Array locator methods return queues; choose find, find_index, min, max, unique, or unique_index according to the required result type.
  • Reduction and query functions can collapse or expose X/Z differently, so include unknown-state tests at verification boundaries.
  • A function may call permitted non-time-consuming system subroutines, but it cannot use a user-defined task as a zero-time escape hatch.
Frequently tested built-ins
OperationContractCommon review point
$clog2(n)Ceiling log2 as an integerGuard n <= 1 and resulting zero-width declarations
$countones(expr)Count bits that are exactly 1X and Z are not counted as ones
$onehot(expr)Exactly one bit is 1Use $onehot0 when all-zero is legal
$onehot0(expr)At most one bit is 1Unknown control bits still need an explicit policy
$cast(dst, src)Checked runtime conversionTest success before consuming dst
$sampled(expr)Value sampled for the current assertion attemptDo not confuse it with an arbitrary procedural read
array.shuffle()Randomize element order in placeThe receiver mutates; preserve a copy if order matters later
array.find_index with (...)Queue of matching indicesIt does not return matching element values

Explain it out loud

Interview reasoning checkpoints

Strong answer

A function must complete in zero simulation time and may return a value for an expression. A task may consume time and represents an activity; results travel through output or inout arguments.

Reason it through
  1. Check for #, @, wait, or time-consuming calls.
  2. Check whether the caller needs a composable expression value.
  3. Choose automatic lifetime when calls can overlap.
Interviewer lens

The expected answer is about time and calling semantics, not just the number of outputs.

Common trap

Saying tasks are always unsynthesizable or that a task has a void return type.

Strong answer

No. A SystemVerilog function cannot enable a user-defined task, regardless of whether that task happens to contain a timing control. Factor shared zero-time work into another function and keep synchronization in the task caller.

Reason it through
  1. A function can appear inside an expression whose evaluation must finish in the current region.
  2. The language restriction is on calling a user-defined task, not only on whether the task body visibly blocks.
  3. Move synchronization to the task caller and pass computed values through a function.
Interviewer lens

Look for awareness of transitive timing, not only syntax in the immediate function body.

Common trap

Assuming an apparently zero-time task is a portable escape hatch.

02 · Arguments and storage lifetime

Direction says who writes; lifetime says whether calls can overlap.

input, output, inout, ref, const ref, automatic, and static control copying, aliasing, initialization, and reentrancy. Those are observable API properties.

Rule to say firstDefault reusable subroutines to automatic, use const ref for large read-only data, and reserve ref for intentional caller-visible mutation.

Call boundary

Copy, alias, then return

  1. 01inputcopy in

    Callee reads a snapshot; later caller changes are not observed.

  2. 02outputcopy out

    Formal is written locally and copied to actual on return.

  3. 03inoutcopy in/out

    Snapshot enters and final value returns.

  4. 04ref / const refalias

    Immediate shared object; const ref forbids callee writes.

inputcopy in

Callee reads a snapshot; later caller changes are not observed.

automatic
Per invocation
Arguments and locals are independent across concurrent calls.
static
Shared storage
State persists and overlapping calls can race.
Handles
Shallow copy
Value passing a class variable copies its handle, not the object.

Copy semantics and object-handle semantics coexist: the formal can be a copy while the referenced object remains shared.

01

Observe

For each formal, record whether the callee reads, writes, aliases, or retains it, plus whether concurrent invocations are possible.

02

Decide

Use value input/output for snapshot-and-return behavior, const ref for read-only aliasing, and ref only when changes must be immediate and type-exact.

03

Failure

A static local shared by overlapping calls creates a race; a ref formal can expose partial updates that a copy-out output would hide.

04

Prove

Run overlapping calls with distinct data, assert inputs remain unchanged for const APIs, and check that output values appear only at return when that is the contract.

Argument direction controls observation time

Value input is initialized on entry. Output is copied back on return. Inout does both. ref aliases the actual for the entire call, so writes are immediately visible; const ref keeps the performance and aliasing of ref but prevents assignments through the formal. Because a ref formal aliases live caller storage, it is legal only in an automatic-lifetime subroutine.

  • If a formal omits its direction, it inherits the preceding formal's direction; the first omitted direction defaults to input. Repeat directions when that sticky rule could surprise a reviewer.
  • Declare a task or function automatic before using ref or const ref formals; module-scope tasks otherwise default to static lifetime.
  • Use output for a final copy-back result and ref only when intermediate caller-visible mutation is part of the contract.
Use const ref for large read-only inputssystemverilog
function automatic int checksum(
  const ref byte unsigned data[],
  output bit valid
);
  int sum = 0;
  valid = (data.size() != 0);
  foreach (data[i]) sum = (sum + data[i]) & 'hFFFF;
  return sum;
endfunction

task automatic swap(ref int a, ref int b);
  int tmp = a;
  a = b;
  b = tmp;
endtask

Lifetime must match concurrency

Static subroutine storage persists for the containing scope and is shared among calls. Automatic storage is created for each invocation. Methods are normally automatic, but an explicit static local still shares state. Recursive and forked calls need automatic locals unless shared state is intentional and protected.

  • Initialize automatic variables before use; their per-call storage does not imply a desired value.
  • A static accumulator is hidden shared state and must be synchronized if calls overlap.
  • A ref actual must be a compatible variable, not an arbitrary expression.
  • ref and const ref formals require automatic subroutine lifetime because their aliases are invocation-specific.
  • Default arguments and named argument association improve call-site clarity but should not hide protocol-critical inputs.

Explain it out loud

Interview reasoning checkpoints

Strong answer

ref aliases the caller's variable for the duration of the call, so writes are immediate. output uses a local formal whose final value is copied back when the subroutine returns.

Reason it through
  1. Ask when the caller should observe intermediate values.
  2. Ask whether exact type and lvalue aliasing are required.
  3. Prefer output when only the final result belongs in the public contract.
Interviewer lens

A strong answer explains timing of visibility, not just “reference versus copy.”

Common trap

Using ref only as a performance optimization while ignoring caller-visible mutation.

Strong answer

Automatic gives each invocation independent arguments and locals, making recursion and overlapping calls reentrant. Static storage persists and is shared, which is useful only when that shared state is intentional.

Reason it through
  1. Determine whether calls can overlap through fork, callbacks, or parallel components.
  2. Identify every mutable local.
  3. Protect intentional shared state or move it into an explicit object.
Interviewer lens

Connect lifetime to races and reentrancy.

Common trap

Assuming a static local is safe because each source call appears in a different block.

03 · Program, clocking, and scheduler regions

Race freedom comes from explicit sampling and driving contracts.

Program blocks and clocking blocks were designed to separate testbench activity from RTL updates. Modern environments still need to understand the event regions even when they use interfaces, classes, and UVM instead.

Rule to say firstSample and drive through a clocking-block contract or an equivalent explicit discipline; do not treat program as a universal race-elimination switch.

One simulation time slot

Observe first, update deliberately

RTL
evaluate#0NBA commitstable
always_ff evaluates before its nonblocking update.
Assertion
sampleevaluate
Testbench
clocking samplereact / drive by skew
  1. 01Preponedsample

    Concurrent assertions capture values before current-slot design updates.

  2. 02Active / Inactiveblocking / #0

    Design evaluation and zero-delay rescheduling; order within a region can race.

  3. 03NBAcommit

    Deferred nonblocking LHS updates apply.

  4. 04Observed / Reactivecheck / react

    Assertions evaluate and program/testbench reactions can follow stabilized design state.

Preponedsample

Concurrent assertions capture values before current-slot design updates.

#0
Inactive
It runs before NBA and is not a substitute for nonblocking assignment.
Program
Reactive domain
Useful historical construct, but uncommon in current UVM architectures.
Completion
Explicit
A program does not automatically solve end-of-test coordination.

Source order is not a synchronization primitive. The event region and edge-relative skew are the actual temporal contract.

01

Observe

Trace each read and write through Preponed, Active, Inactive, NBA, Observed, Reactive, and postponed behavior at the same simulation time.

02

Decide

Put RTL in design regions, assertions in their sampling/reactive flow, and testbench access behind a clocking or synchronization boundary.

03

Failure

Two blocks that read and write the same signal in the Active region can race even when source order looks deterministic.

04

Prove

Use sampled-value functions, scheduler-aware assertions, randomized compilation order, and tests that drive exactly on a clock edge.

Program blocks are one tool, not a magic partition

A program executes testbench activity in Reactive regions after design and assertion work for the slot. It restricts some design constructs and was intended to reduce DUT/testbench races. Modern UVM usually uses modules, interfaces, clocking blocks, and explicit objections instead.

  • A program may instantiate classes and call tasks, but should not be used to model synthesizable hardware.
  • Reactive scheduling does not fix two testbench threads racing with each other.
  • End-of-simulation still needs an explicit completion protocol.
  • Use one disciplined interface boundary rather than mixing direct signal access and clocking-block access.

Clocking blocks define sampling and drive skew

A clocking block names a clocking event and direction for each signal. Input skew chooses when values are sampled relative to the event; output skew chooses when drives occur. Assignments to clocking outputs follow clocking-drive semantics, so a universal rule such as “always use =” is incomplete.

Edge-relative testbench accesssystemverilog
interface bus_if(input logic clk);
  logic valid, ready;
  logic [31:0] data;

  clocking drv_cb @(posedge clk);
    default input #1step output #0;
    input  ready;
    output valid, data;
  endclocking

  clocking mon_cb @(posedge clk);
    default input #1step;
    input valid, ready, data;
  endclocking
endinterface

Explain it out loud

Interview reasoning checkpoints

Strong answer

No. They schedule program activity in Reactive regions relative to design activity, but they do not resolve races among testbench threads or replace a clear clocking, ownership, and completion protocol.

Reason it through
  1. Identify which competing operations live in which regions.
  2. Separate DUT-versus-testbench races from testbench-versus-testbench races.
  3. Use clocking blocks and explicit synchronization for the remaining boundaries.
Interviewer lens

A nuanced answer acknowledges the feature without presenting it as the modern UVM default.

Common trap

Claiming program automatically makes any testbench deterministic.

Strong answer

It defines a named edge-relative sampling and driving contract, including input and output skew, so monitors and drivers do not depend on same-region execution order.

Reason it through
  1. Choose the clocking event.
  2. Set input sampling before or at the edge as the protocol requires.
  3. Set output drive timing and access signals only through that contract.
Interviewer lens

Look for sampling versus driving skew and the reason it removes ambiguity.

Common trap

Treating a clocking block as only a namespace for interface signals.

04 · DPI

Treat the language boundary as an ABI, ownership, and state-domain contract.

DPI makes C calls convenient, not free. Direction, type mapping, open-array representation, scope, reentrancy, and context all affect correctness and performance.

Rule to say firstKeep DPI adapters narrow, copy or pin data according to documented lifetime, and preserve four-state information when the C side must observe X or Z.

Foreign-function boundary

SystemVerilog value → adapter → C ABI

  1. 01SV declarationtyped import/export

    Direction and state domain start the contract.

  2. 02DPI adapteropen-array / vector API

    Simulator-defined handles preserve shape and four-state bits.

  3. 03C implementationowned storage

    Copy if data must survive beyond the call.

  4. 04Validationround trip

    Compare size, order, values, and error behavior.

SV declarationtyped import/export

Direction and state domain start the contract.

pure
No side effects
Enables optimization only when the promise is true.
context
Simulator scope
Needed for calls that use context-dependent simulator services.
Cost
Non-zero
Marshalling and call frequency matter; batch where appropriate.

One explicit adapter is easier to validate than many call sites that each guess at C and SystemVerilog representation.

01

Observe

List every imported or exported symbol, data direction, state domain, array shape, callback, simulator-service call, and ownership transition.

02

Decide

Use pure for side-effect-free calls, context only when simulator context is required, and open-array APIs for portable array access.

03

Failure

Assuming native struct layout or retaining a temporary simulator pointer can corrupt data even when the call signature compiles.

04

Prove

Round-trip boundary values, X/Z vectors, empty arrays, callbacks, and repeated calls; profile large transfers rather than assuming zero overhead.

Map semantics, not only syntax

Simple 2-state scalars often map naturally to C integer types. Four-state packed vectors require separate value and unknown masks. Open arrays should be accessed with the DPI API because range direction and representation are simulator-managed.

DPI review checklist
BoundaryQuestionProof
2-state scalarWidth and signedness?Min/max and negative tests
4-state vectorAre aval and bval preserved?0/1/X/Z round trip
Open arrayShape and direction?Every dimension and empty case
StringEncoding and lifetime?Copy and termination checks
chandleWho owns the object?Create/use/free lifecycle
CallbackDoes it need context?Scope and reentrancy test

Batch work at the boundary

A DPI call crosses runtimes and may marshal data. Small calls in a hot per-cycle loop can dominate model time. Move stable policy into C only when it improves clarity or performance, pass larger batches, and keep cycle-accurate synchronization on the SystemVerilog side.

State-domain-aware declarationssystemverilog
import "DPI-C" pure function int crc32_2state(
  input byte unsigned data[]
);

import "DPI-C" context task inspect_4state(
  input logic [127:0] value,
  output int unknown_bits
);

export "DPI-C" task report_error;

Explain it out loud

Interview reasoning checkpoints

Strong answer

A four-state packed value crosses with separate value and unknown-state representation, commonly aval and bval words. Mapping it to a plain C integer would lose X/Z.

Reason it through
  1. Classify the SystemVerilog source as 2-state or 4-state.
  2. Use the DPI vector representation appropriate to that domain.
  3. Round-trip all four logic values.
Interviewer lens

The answer should identify information loss and a validation strategy.

Common trap

Casting a logic vector pointer to uint32_t and assuming portable layout.

Strong answer

No. It is lightweight compared with many alternatives, but call transitions, marshalling, context, and array handling still cost time. Measure and batch hot-path work.

Reason it through
  1. Estimate call frequency and transferred bytes.
  2. Separate pure computation from cycle synchronization.
  3. Profile before moving more logic across the boundary.
Interviewer lens

A practical answer balances interoperability with performance and ownership.

Common trap

Assuming convenience syntax means no runtime or copying cost.

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.

Standards and primary references

Check the contract at its source.

IEEE Std 1800-2023 — SystemVerilogNormative reference for subroutines, processes, event regions, assignments, DPI, and selection statements.Accellera SystemVerilog draftHistorical specification text for tasks, functions, assignments, and simulation scheduling.Accellera event-semantics clarificationUseful scheduler background for event triggering and current-time-slot observation.