Intermediate

Intermediate Topics

Object-oriented programming, randomization, interfaces.

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.

Plate IV.1 bus_if.sv
interface bus_if(input logic clk); logic [31:0] addr, data; logic read, write, ready; modport master (output addr, data, read, write, input ready); modport slave (input addr, data, read, write, output ready); modport monitor (input addr, data, read, write, ready); endinterface
Figure IV.1 — one interface, three modports. The monitor cannot drive; the compiler guarantees it.
The Race

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:

Plate V.1 basic_class.sv
class Transaction; bit [31:0] addr, data; int id; function new(bit[31:0] a=0, bit[31:0] d=0); this.addr = a; // 'this' disambiguates this.data = d; endfunction endclass Transaction t; // handle, null t = new(32'h4000, 32'hDEAD); // object allocated t.data = 32'hBEEF; // member access via handle
Figure V.1 — declare, construct, use. All three steps are explicit.
Handle, not Pointer

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).

Plate VI.1 constraints.sv
class Pkt; rand bit [15:0] len; rand bit [7:0] payload []; randc bit [2:0] priority; constraint c_len { len inside {[64:1518]}; } constraint c_size { payload.size() == len; } constraint c_dist { len dist {[64:127] := 5, [128:511] := 3, [512:1518] := 1}; } function void post_randomize(); $display("len=%0d prio=%0d", len, priority); endfunction endclass // Usage Pkt p = new(); assert(p.randomize() with { len > 100; });
Figure VI.1 — rand/randc fields, constraint blocks, and an inline with.
Let the solver drive

Constraints are declarative. The solver picks values; you describe the rules. There are no assignments inside a constraint.