Connecting the Testbench & Design.
A testbench that wires into the DUT signal-by-signal is a testbench you will spend your career maintaining. Interfaces, modports, clocking blocks — these are SystemVerilog's answers to the wire-count problem and to the race-condition problem at once.
The interface construct
An interface is a bundle. It gathers related signals — address, data, read, write, ready — into a single named scope. Modules connect to the bundle instead of to each individual wire. A 40-signal bus becomes a 1-identifier port. Better still, the interface can carry its own timing (via clocking blocks), its own assertions, and its own monitor tasks — none of which have to be duplicated in each module that uses the bus.
Modports — one interface, many perspectives
A modport declares a directional view of the interface for a particular
kind of client. The master sees addr as an output; the slave sees
it as an input — but both views derive from one interface declaration.
Modports also scope which signals a client can touch, which prevents
a monitor from accidentally driving the bus.
Without a clocking block, a testbench reading a signal on the same edge the RTL writes it can race. The simulator may give you either value.
Basic Object-Oriented Programming.
OOP is the mechanism by which a verification environment survives its first project and reappears on the second. Classes gather state with behavior; inheritance extends behavior without editing shared code; handles let you pass transactions through queues without copying.
Classes, objects, handles
A class is a template. An object is an instance of that template allocated on the simulator's heap. A handle is a reference to an object. Unlike C++, SystemVerilog has no raw pointers; unlike Java, SystemVerilog never implicitly constructs objects. The two rules fit together into one workflow:
A class variable is an opaque reference. Uninitialized, it is null. You construct with new() — explicitly, always.
Randomization.
Randomization is the heart of the modern testbench. A single class — a handful
of rand fields, a few constraint blocks — can
generate stimulus that a directed test author would need a year to enumerate.
rand and randc
Mark a class member rand and it is randomized on every
.randomize() call, uniformly across its legal range. Mark it
randc (random-cyclic) and it instead runs through a random
permutation of its range before any repetition. randc
is perfect for narrow enums — opcode, bank id, port number — where you want
every value exercised before any is exercised twice.
Constraint blocks
A constraint block contains expressions, not assignments.
The solver picks values such that every expression evaluates to true. Use
== for equivalence, never =. Group alternatives
with { … }, not begin…end (which is procedural).