Basic

SystemVerilog Foundations

Verification guidelines, data types, and procedural flow.

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:

Each layer communicates with the next through transactions, not signals. This is the architecture the remainder of this manual is teaching you to build.

Precept

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.

Common gotcha

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.

Plate II.1 fixed_size.sv
int lo_hi [0:15]; // Verilog-style bounds int array [16]; // SystemVerilog: C-style size int grid [8][4]; // 2-D, 8 rows × 4 cols // foreach auto-iterates all dimensions foreach (grid[i,j]) grid[i][j] = i*4 + j;
Figure II.1 — three equivalent declarations; note the foreach comma-form for multi-dim arrays.
The Logic Type

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.

Plate III.1 arguments.sv
task transfer( input bit [31:0] addr, // copied in output bit [31:0] data, // copied out ref mailbox mbx, // live reference input int retries = 3 // default value ); // … endtask
Figure III.1 — the four argument directions, with an argument default.
Task vs Function

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.