SystemVerilog for Verification: Comprehensive Study Guide
VERIFICATION GUIDELINES
Introduction to SystemVerilog for Verification
- Goal of Verification: To ensure a design matches its specification. Bugs are defined as discrepancies between the device's behavior and the documentation.
- Hardware Verification Language (HVL) Features:
- Constrained-random stimulus generation (CRSG).
- Functional coverage to measure progress.
- Object-Oriented Programming (OOP) for higher abstraction.
- Multi-threading and interprocess communication (IPC).
- Tight integration with event-simulators.
The Verification Process and Plan
- Levels of Verification:
- Block Level: Focuses on individual modules and sub-components.
- Integration Level: Focuses on interfaces and interactions between blocks.
- System Level: Full Design Under Test (DUT) executing concurrent operations.
- Verification Plan: A document derived from the hardware specification detailing features to exercise and techniques (random testing, assertions, formal proofs) to be used.
Methodology: Directed vs. Constrained-Random Testing
- Directed Testing: Incremental, manual creation of specific stimulus vectors. Pros: predictable progress early on. Cons: scales linearly with complexity (O(n)) and misses unanticipated bugs.
- Constrained-Random Testing (CRT): Automated generation of stimulus within defined constraints.
- Uses a Layered Testbench architecture.
- Functional Coverage acts as the feedback loop to identify "holes" in the testing space.
- Randomizing configuration, data, protocol exceptions, and delays creates high-pressure scenarios for the control logic.
Layered Testbench Structure
- Signal Layer: The physical wires and pin-level connections to the DUT.
- Command Layer: Drivers and monitors that translate signals into discrete commands (e.g., Read, Write).
- Functional Layer: Agents and transactors that manage higher-level transactions (e.g., a DMA transfer) and include scoreboards/checkers.
- Scenario Layer: Generators that orchestrate complex sequences of transactions.
- Test Layer: The highest level containing specific constraints and coverage goals.
DATA TYPES
Built-in and Logic Types
- logic: A 4-state type (0, 1, X, Z) that replaces Verilog’s
reg and wire (except for multi-driver nets). - 2-state Types: Optimized for performance and memory.
bit: Unsigned.byte, shortint, int, longint: Signed.
- $isunknown(expression): Returns 1 if any bit is X or Z.
Arrays and Storage
- Fixed-Size Arrays:
- Unpacked:
int array[8][4]; (Contiguous logic, fragmented memory storage). - Packed:
bit [3:0][7:0] bytes; (Stored as a single contiguous bit-vector).
- Dynamic Arrays: Declared with
[]. Space allocated at runtime via new[size]. Usage: dyn = new[20](dyn); (to resize and copy). - Queues: Declared with
[$]. Support efficient insertion/deletion at any position. - Methods:
push_front(), pop_back(), insert(index, val).
- Associative Arrays: Sparse storage declared with
[*]. Ideal for large, non-contiguous memory modeling. - Array Methods:
- Reduction:
.sum, .product, .and, .or, .xor. - Locator:
.find, .find_index, .min, .max, .unique.
User-Defined Types and Strings
- typedef: Creates new type aliases (e.g.,
typedef bit [31:0] uint;). - struct: Groups multiple variables.
- enum: Strong variable types with named constants. Methods:
.first, .last, .next, .prev, .name. - Strings: Dynamic length. Methods:
$psprintf (returns formatted string), .getc(N), .toupper().
PROCEDURAL STATEMENTS AND ROUTINES
Control Flow and Tasks/Functions
- Operators: Implementation of
++, --, +=, -=, *=, /=. - Loops:
break to exit, continue to skip iteration. do...while support. - Functions: Can return
void. Cannot consume time. - Tasks: Can consume time.
Routine Arguments
- Direction:
input, output, inout, and ref. - Pass-by-Reference (ref): Allows routines to modify original variables without copying. Required for arrays and large objects for performance.
- const ref: Pass by reference but prevent modification.
- Default values:
task t(int a=0); allows calling without parameters.
Storage and Timing
- Automatic Storage: Required for recursive routines and re-entrant tasks. Declared using the
automatic keyword in program or module headers. - Time Units:
timeunit 1ns; timeprecision 1ps; replaces ambiguous \timescale`.
BASIC OBJECT-ORIENTED PROGRAMMING (OOP)
Classes and Objects
- Class: The blueprint/template containing data and methods.
- Object: An instance of a class.
- Handle: A pointer/reference to an object. Initialized to
null. - Constructor: The
new() function. Allocates memory and initializes variables. Can be customized with arguments.
Static Variables and Methods
- Static Variables: Shared across all instances of a class (e.g., a packet counter).
- Static Methods: Can be called without an object instance (only access static members).
Scoping and Memory
- this: Resolves ambiguity between class properties and local variables.
- Garbage Collection: SystemVerilog automatically deallocates objects when no handles point to them.
- Shallow Copy:
dst = new src; copies properties but not nested objects. - Deep Copy: A custom method must be written to recursively copy nested object instances.
CONNECTING THE TESTBENCH AND DESIGN
The Interface Construct
- Definition: Bundles signals into a single named entity to simplify top-level connectivity.
- modport: Defines signal directions for specific components (e.g.,
DUT vs TEST). - Virtual Interface: A reference/pointer to an interface instance. Essential for use within classes to access physical design signals.
The Program Block
- Function: Houses testbench code.
- Scheduling: Executes in the Reactive region of the SystemVerilog Scheduler to eliminate race conditions with the design (which runs in the Active region).
- Implicit $exit: Simulation terminates when all
initial blocks in all program blocks finish.
Clocking Blocks
- Purpose: Synchronizes design signals with a clock.
- Skew: Defines when to sample (input) and drive (output). Default: Input sampled at
1step (before clock edge), Output driven at 0 (at clock edge). - Syntax:
##n (cycle delay).
RANDOMIZATION
Constrained Randomization
- rand/randc: Member variables for randomization.
randc (Random-Cyclic) ensures no repeats until all values are used. - Constraints: Declarative blocks that limit variable values.
inside {range}: Set membership.dist {val := weight, [r1:r2] :/ weight}: Weighted distributions.-> (Implication) and if-else: Conditional constraints.
- solve…before: Forces the solver to prioritize the distribution of one variable to influence another in implication constraints.
Randomization Methods
- obj.randomize(): Attempts to solve all constraints. Returns 1 for success, 0 for failure.
- obj.randomize() with {extra_constraints}: Inline constraints that exist for a single call.
- prerandomize() / postrandomize(): Built-in callbacks to perform calculations before/after the solver runs.
- rand_mode(1/0): Enable/disable randomization for specific variables.
- constraint_mode(1/0): Enable/disable specific constraint blocks.
THREADS AND IPC
Fork-Join Variants
- fork…join: Waits for all spawned threads to finish.
- fork…join_any: Waits for the first spawned thread to finish; others continue.
- fork…join_none: Parent thread continues immediately after spawning.
Interprocess Communication (IPC)
- Events: Edge-triggered synchronization via
-> and @ (or wait(e.triggered)). - Semaphores: Used for resource control (mutex). Methods:
get(keys), put(keys), try_get(keys). - Mailboxes: Synchronous FIFOs for passing data (handles) between transactors.
- Methods:
put(), get(), peek(), try_get(), num(). - Bounded mailboxes cause the producer to block if full.
ADVANCED OOP AND FUNCTIONAL COVERAGE
Inheritance and Polymorphism
- extends: Defines a subclass that inherits from a base class.
- virtual methods: Ensures the correct method is called based on the object type, not the handle type (Polymorphism).
- super: Calls methods from the parent class.
- $cast(handle, source): Dynamic type casting for down-casting from base to extended handles.
Callbacks
- Mechanism: Empty virtual methods in a transactor that are called at specific execution points.
- Application: Allows users to inject functional coverage, scoreboarding, or error injection into a generic transactor without modifying its source code.
Functional Coverage
- covergroup: A container for coverage measurement.
- coverpoint: Tracks specific variables or expressions.
- bins: Segments of the value range for a coverpoint.
bins b[] = {[0:7]}; individual bins.ignore_bins, illegal_bins.
- cross: Measures the Cartesian product/correlation of multiple coverpoints.
- Triggering: Can be explicit via
.sample() or event-based using a clocking block or assertion.