Verification Guidelines.
Before you learn the logic type or the first covergroup, you must decide how you mean to verify. A house is not begun with paint chips; a testbench is not begun with syntax.
Verification is the art of proving that a design does what its specification says it does. The specification is a human-language document — ambiguous, incomplete, sometimes self-contradictory — and the RTL is one engineer's reading of it. Your job is to offer the second reading. Where the two interpretations disagree, there is a bug. The goal is to find it before the customer does.
The verification plan
Verification begins with a plan derived from the hardware specification. The plan catalogs what features must be exercised and by which techniques — directed tests, constrained-random stimulus, assertions, formal proofs, or emulation. A good plan names corners the designer likely missed.
Directed vs constrained-random
A directed test exercises a specific feature with hand-written stimulus. It finds the bugs you thought of. Constrained-random testing (CRT) uses the simulator's pseudo-random generator, shaped by declarative constraints, to produce stimulus you never explicitly enumerated. It finds the bugs you didn't think of. Modern methodologies — VMM, OVM, UVM — are all CRT methodologies.
You can never prove there are no bugs left, so you need to constantly come up with new verification tactics. — Spear & Tumbush, Chapter 1
The layered testbench
A serious testbench is not a flat file of initial blocks.
It is organized in layers, each with a single responsibility:
- Signal layer — the physical wires of the DUT.
- Command layer — drivers that turn transactions into signal wiggles; monitors that turn signal wiggles back into transactions.
- Functional layer — generators, agents, self-checkers, scoreboards.
- Scenario layer — sequences that orchestrate interesting traffic patterns.
- Test layer — the top-level test and its coverage goals.
Each layer communicates with the next through transactions, not signals. This is the architecture the remainder of this manual is teaching you to build.
Bugs are good. Each one found before tape-out is one fewer that ships.
Data Types.
Verilog's reg-or-wire dilemma is gone. SystemVerilog offers a richer palette — some elegant, some signed, some treacherous. Choose carefully.
The logic type
SystemVerilog's logic type is an improved reg that can
be driven by continuous assignments, gates, modules, and procedural code.
It is 4-state (0, 1, X, Z)
and it accepts exactly one structural driver. That last restriction turns the
compiler into a netlist checker: if you accidentally connect two drivers to
the same signal, the tool complains. For bidirectional buses, you still need
wire or another net type so that multi-driver resolution can happen.
2-state data types
For simulation speed and memory, SystemVerilog adds five 2-state types:
the unsigned bit, and the signed byte,
shortint, int, and longint.
Use 2-state types in testbench-internal arithmetic where you don't need X/Z
propagation. Be careful when wiring them to DUT outputs: X or Z becomes 0 or 1
silently, potentially masking bugs.
byte is signed. Its range is −128 to +127,
not 0 to 255. For an unsigned octet, declare byte unsigned or
bit [7:0].
Fixed-size arrays
SystemVerilog relaxes Verilog's array declaration: you may give just the size, C-style, instead of full low:high bounds. Out-of-bounds reads return the element type's default — X for 4-state types, 0 for 2-state.
An improved reg — usable where a net is, but with a single-driver restriction that exposes netlist bugs.
Procedural Statements & Routines.
Tasks, functions, arguments, and time. The unshowy machinery that every line of your testbench will rest on.
Tasks, functions, and void functions
The distinction between a task and a function is the difference between a
subroutine that may wait and one that may not. A function executes in
zero simulation time; it may not contain # delays, @
event controls, wait statements, or calls to tasks. A task
may contain all of them.
A void function is a function declared to return nothing. Use one
when you want the zero-time discipline of a function but have no useful value
to return — void function monitor_drive();.
Argument directions
Routine arguments may be declared input, output,
inout, or ref. The first three copy values at call
and return. The fourth — ref — passes by reference: the routine
sees live updates to the caller's variable. Pass large arrays, mailboxes,
and class handles by ref to avoid copy cost.
A task may consume simulation time; a function may not. A void function is a function with no return — zero-time semantics, no throwaway value.