Part 1 · Language Foundations · Intermediate
Procedural Code & Scheduling
Hub — the always family, blocking vs nonblocking, tasks vs functions, the event scheduler's regions, initial/final ordering, and flow control.
Overview
Procedural code is where SystemVerilog stops being a netlist description and becomes a programming language — but a programming language whose statements execute inside a discrete-event scheduler with strict rules about when each statement runs relative to clock edges, other processes, and assertion sampling. Most “my simulation behaves differently from my synthesis” bugs, and nearly every testbench race condition, trace back to misunderstanding these rules rather than to syntax.
This topic walks from the intent-checked always_comb/always_ff blocks, through the blocking-vs-nonblocking distinction that makes flip-flops simulate correctly, into the stratified event regions that explain why those rules work — and finishes with the task/function and flow-control machinery you use to build monitors, drivers, and checkers. Scheduling questions (the two-flop swap, where assertions sample, what #0 really does) are interview staples at every seniority level.
Sub-topics
always_comb, always_ff, always_latch — intent checking, sensitivity inference, tool enforcement.
Blocking vs Nonblocking Assignments — scheduling semantics, the two-register swap, race diagrams.
Tasks vs Functions — time consumption, automatic vs static lifetime, argument directions.
The Event Scheduler & Stratified Regions — active/NBA/observed/reactive, #0 abuse, assertion sampling.
initial, final & Startup Ordering — nondeterministic initial order, end-of-sim reporting, TB startup.
Loops & Flow Control — foreach/repeat/forever, break/continue, named blocks, disable, monitor idioms.
PROCEDURAL CODE & SCHEDULING — TOPIC MAP
┌─────────────────────────────────────────────────────────┐
│ one simulation time slot │
│ │
│ Active ──► Inactive(#0) ──► NBA ──► Observed ──► │
│ ▲ (assertions) │
│ │ │ │
│ │ ▼ │
│ └◄──── iterate ◄──── Reactive(program) ──► Postponed│
└──────────────────────────┬──────────────────────────────┘
│ explains the behavior of
┌──────────────┬─────┴──────┬───────────────┐
▼ ▼ ▼ ▼
┌───────────┐ ┌────────────┐ ┌──────────┐ ┌──────────────┐
│ always │ │ = vs <= │ │ initial/ │ │ tasks, loops │
│ family │ │ (blocking/ │ │ final │ │ fork, disable│
│ comb/ff/ │ │ NBA) │ │ ordering │ │ (TB control) │
│ latch │ └────────────┘ └──────────┘ └──────────────┘
└───────────┘
RTL intent RTL correctness sim lifecycle TB machineryKey takeaways
Every procedural statement runs inside scheduler regions — region order explains races, not luck.
always_comb/always_ff/always_latch declare intent that tools verify; plain always declares nothing.
Blocking for combinational and TB sequencing; nonblocking for clocked state — one rule, zero races.
Scheduling questions (swap idiom, #0, assertion sampling) are interview staples — learn the regions once.