0% found this document useful (0 votes)
42 views

Lecture 4 - SystemVerilog

The document provides an overview of SystemVerilog for both design and verification. It describes many features of SystemVerilog including data types, object-oriented programming support, constrained random APIs, specification languages, coverage APIs, and more. It also discusses how SystemVerilog is used for both design and verification in industry.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
42 views

Lecture 4 - SystemVerilog

The document provides an overview of SystemVerilog for both design and verification. It describes many features of SystemVerilog including data types, object-oriented programming support, constrained random APIs, specification languages, coverage APIs, and more. It also discusses how SystemVerilog is used for both design and verification in industry.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

An Overview of SystemVerilog

for Design and V erification


Vighnesh Iyer, EECS 251B
Intention of this Lecture

● We use Chisel for all RTL written at Berkeley


○ Why bother with SystemVerilog?
● SystemVerilog is the de-facto industry standard
○ SV/UVM is used for (nearly) all industry verification
○ You will be asked about it in interviews
● Understand basic dynamic verification concepts
● Understand existing SystemVerilog code
● Inspire extensions to HDLs
SystemVerilog (SV) is an IEEE Standard 180 0
https://standards.ieee.org/project/180 0 .html

Universal Verification Methodology (UVM)


is a standard maintained by Accellera
https://www.accellera.org/downloads/standards/uvm
What is SystemVerilog

● IEEE 180 0 standard


● A massive extension of Verilog with new constructs for design and verification
○ New data types (for RTL and testbenches)
○ OOP support
○ Constrained random API
○ Specification language
○ Coverage specification API
● Fixing warts in Verilog
○ Synthesis - simulation mismatch
○ Verilog was initially developed as a simulation language; synthesis emerged later
SystemVerilog for Design
Ending the Wire vs. Reg Confusion
Verilog -2005 SystemVerilog
● wire for LHS of assign statements ● logic for LHS of assign statements
● reg for LHS of code inside always @ ● logic for LHS of code inside always @
blocks blocks

wire a; logic a, b, c;
reg b, c; assign a = ____;
assign a = ____; always @(* ) b = ____;
always @(* ) b = ____; always @(posedge clk) c <= ____;
always @(posedge clk) c <= ____;

Both: the containing statement determines if the net is the


direct output of a register or combinational logic
Signal Your Intent With Specific Always Blocks
Verilog -2005 SystemVerilog
always @(* ) begin always_comb begin
if (x) b = a if (x) b = a
else b = ! a; else b = ! a;
end end

always @(posedge clk) begin always_ff @(posedge clk) begin


if (x) c <= ! a; if (x) c <= ! a;
else c <= a; else c <= a;
end end

Coding style is used to verify that c infers New always_comb and always_ff
as a register and b as comb logic statements for safety
Autoconnect (Implicit Port Connections)

● How many times have you done this?

module mod ( input a, b, output c); endmodule

reg a, b; wire c;
mod x (.a(a), .b(b), .c(c));

● If the net names and their corresponding port names match, there’s a shortcut
mod x ( . a , . b, . c) ;

● In SystemVerilog, there’s a concise shortcut


mod x ( . * ) ;

● Implicit connections only work if port names and widths match


Use Enums Over localparams
Verilog -2005 SystemVerilog
localparam STATE_IDLE = 2’ b00; typedef enum logic [ 1: 0] {
localparam STATE_A = 2’ b01; STATE_IDLE, STATE_A, STATE_B
localparam STATE_B = 2’ b10; } state_t;
reg [ 1: 0] state; state_t state;

always @(posedge clk) begin always_ff @(posedge clk) begin


case (state) case (state)
STATE_IDLE: state <= STATE_A; STATE_IDLE: state <= STATE_A
STATE_A: state <= STATE_B; STATE_A: state <= STATE_B;
STATE_B: state <= STATE_IDLE; STATE_B: state <= STATE_IDLE;
endcase endcase
end end

Enums automatically check whether all values can fit. Can


be used as a net type. Add semantic meaning to constants.
More on Enums

● Common to use enums for attaching semantic strings to values

typedef enum logic {


READ, WRITE
} mem_op_t;

module memory (
input [ 4: 0] addr,
input mem_op_t op,
input [ 31: 0] din,
output logic [ 31: 0] dout
);

● Note that input/output net types are by default ‘wire’, you can override them as
logic
Even More on Enums

● You can force enum values to be associated with a specific value


○ To help match up literals for a port that doesn’t use enums

typedef enum logic [ 1: 0] { STATE_IDLE=3, STATE_A=2, STATE_B=1 } state_t

● You can generate N enum values without typing them out


typedef enum logic [ 1: 0] { STATE[ 3] } s t a t e_ t
/ / S T AT E 0 = 0, S T AT E 1 = 1, S T AT E 2 = 2

● You can generate N enum values in a particular range


typedef enum logic [ 1: 0] { STATE[ 3:5 ] } s t a t e_ t
/ / S T AT E 3 = 0, S T AT E 4 = 1, S T AT E 5 = 2
Even More on Enums

● Enums are a first-class datatype in SystemVerilog


○ Enum instances have native functions defined on them
● next(): next value from current value
● prev(): previous value from current value
● num(): number of elements in enum
● name(): returns a string with the enum’s name (useful for printing using $display )
● They are weakly typechecked
○ You can’t assign a binary literal to a enum type net
● They show up in waveforms
○ No more confusion trying to correlate literals to a semantic name
Multidimensional Packed Arrays

● Packed dimensions are to the left of the variable name


○ Packed dimensions are contiguous (e.g. logic [7:0 ] a)
● Unpacked dimensions are to the right of the variable name
○ Unpacked dimensions are non-contiguous (e.g. logic a [8])

logic [ 31: 0] memory [ 32];


// memory[0] is 32 bits wide
// cannot represent more than 1 dimension in memory[0]
// can’t easily byte address the memory

logic [ 3: 0][ 7: 0] memory [ 32];


// memory[0] is 32 bits wide
// memory[0][0] is 8 bits wide
// memory[0][1] is 8 bits wide
Structs
● Similar to Bundle in Chisel
○ Allows designer to group nets together, helps encapsulation of signals, easy declaration
○ Can be used within a module or in a module’s ports
○ Structs themselves can’t be parameterized
● but can be created inside a parameterized module/interface

typedef struct packed {


logic [ 31: 0] din,
ram_cmd a;
logic [ 7: 0] addr,
always_ff @(posedge clk) begin
logic [ 3: 0] wen,
din <= ____
mem_op op
addr <= ____
} ram_cmd;
wen <= ____
op <= ____
module ram ( end
ram_cmd cmd,
logic [ 31: 0] dout
);
Interfaces

● Interfaces allow designers to group together ports


○ Can be parameterized
○ Can contain structs, initial blocks with assertions, and other verification collateral
○ Simplify connections between parent and child modules
module ram (
interface ram_if #( int addr_bits, data_bits) ram_if intf
( input clk); );
logic [addr_bits - 1: 0] addr; always_ff @(posedge intf.clk)
logic [data_bits - 1: 0] din; intf.dout <= ram[intf.addr];
logic [data_bits - 1: 0] dout; endmodule
mem_op op;
endinterface module top();
ram_if #(.addr_bits( 8), .data_bits( 32)) intf();
ram r (.intf(intf));
assign intf.din = ____
endmodule
Modports

● But I didn’t specify the direction (input/output) of the interface ports!


○ This can cause multi-driver issues with improper connections
● Solution: use modports
interface ram_if #( int addr_bits, data_bits)
( input clk);
modport slave (
input addr, din, op, clk, module ram (
output dout ram_if.slave intf
); );
always_ff @(posedge intf.clk)
modport master ( intf.dout <= ram[intf.addr];
output addr, din, op, endmodule
input dout, clk
);
endinterface
Typedefs (Type Aliases)

● You probably saw ‘typedef’ everywhere


○ typedef is used to expose user-defined types
● J ust like with enums, they help attach semantic meaning to your design
● They are just type aliases

typedef signed logic [ 7: 0] sgn_byte;


typedef unsigned logic [ 3: 0] cache_idx;
Packages / Namespacing
● Verilog has a global namespace
○ Often naming conflicts in large projects
○ `include is hacky and requires `ifdef guards
● SystemVerilog allows you to encapsulate constructs in a package
○ modules, functions, structs, typedefs, classes

package my_pkg;
typedef enum logic [ 1: 0] { STATE[ 4] } state_t; import my_pkg::* ;
function show_vals();
state_t s = STATE0; module ex ( input clk);
for ( int i = 0; i < s.num; i = i + 1) begin state_t s;
$display (s.name()); always_ff @(posedge clk) begin
s = s.next(); s <= STATE0;
end end
endfunction endmodule
endpackage
SystemVerilog for Verification
Overview

● The SystemVerilog spec for verification is massive


○ We can’t cover everything in one lecture
● New data structures for writing testbenches
○ Parity with PHP
● OOP
● SystemVerilog Assertions
● Coverage API
● Constrained random
New Data Types

● bit, shortint, int, longint


○ 2-state types
● string
○ Now natively supported, some helper methods are defined on string (e.g. substr)
Dynamic Arrays

● Typical Verilog arrays are fixed length at compile time


bit [ 3: 0] arr [ 3]; // a 3 element array of 4 bit values
arr = ‘ { 12, 10, 3}; // a literal array assignment

● Dynamic arrays are sized at runtime


○ Useful for generating variable length stimulus

bit [ 3: 0] a r r [ ] ; / / a dy na mi c a r r a y of 4 bi t v a l ue s
initial begin
a r r = new[ 2] ; / / s i z e t he a r r a y f or 2 el ement s
a r r = ‘ { 12, 10} ; / / l i t e r a l a s s i gnme nt

a r r = new[ 10] ;
a r r [ 3] = 4;
end
Queues

● Similar to lists in Scala and Python


○ Useful for hardware modeling (FIFO, stack) - process transactions sequentially

bit [ 3: 0] data [ $]; // a queue of 4 - bit elements


bit [ 3: 0] data [ $] = ‘ { 1, 2, 3, 4}; // initialized queue
data[ 0] // first element
data[ $] // last element
data.insert( 1) // append element
data[ 1: $] // queue slice excluding first element
x = data.pop_front() // pops first element of queue and returns it
data = {} // clear the queue
Associative Arrays

● Similar to Python dicts or Scala Maps


○ Can be used to model a CAM or lookup testbench component settings

int fruits [ string ];


fruits = ‘ { “ apple ” : 4, “ orange ” : 10};

fruits[ “ apple ” ]
fruits.exists( “ lemon ” )
fruits.delete( “ orange ” )
Clocking Blocks

● There is often confusion when you should drive DUT inputs and sample DUT
outputs relative to the clock edge
○ Solution: encode the correct behavior in the interface by using clocking blocks

interface ram_if #( int addr_bits, data_bits)


( input clk);
logic [addr_bits - 1: 0] addr;
logic [data_bits - 1: 0] din;
logic [data_bits - 1: 0] dout;
mem_op op;

clocking ckb @( posedge clk)


default input #1step output negedge ;
input dout;
output din, dout, op;
endclocking
● Input/output is from the perspective of the testbench
endinterface ● Can use any delay value or edge event as skew
● intf.ckb.din = 32’d10 0 ; @(intf.ckb); x = intf.ckb.dout;
OOP in SystemVerilog

● SystemVerilog has your typical object-oriented programming (OOP) constructs


○ Classes, constructors, type generics, inheritance, virtual methods/classes, polymorphism

class Message;
bit [ 31: 0] addr; initial begin
msg = new Message( 32’ d4,
bit [ 3: 0] wr_strobe;
4’ b1111);
bit [ 3: 0] burst_mode; $display (msg.burst_mode);
bit [ 31: 0] data [ 4]; end

function new ( bit [ 31: 0] addr, bit [ 3: 0] wr_strobe =


4’ d0);
this .addr = addr;
this .wr_mode = wr_mode;
this .burst_mode = 4’ b1010;
this .data = ‘ { 0, 0, 0, 0};
endfunction
endclass
More OOP

● You can extend a class as usual


○ class ALUMessage extends Message
○ call .super() to access superclass functions
○ Polymorphic dynamic dispatch works as usual
● You can declare classes and functions ‘virtual’
○ Forces subclasses to provide an implementation
○ Prevents instantiation of abstract parent class
● Class members can be declared ‘static’
○ The member is shared among all class instances
● OOP constructs are used to:
○ Model transactions
○ Model hardware components (hierarchically and compositionally)
Type Generic Classes

● Classes can have parameters, just like modules


○ They can be ints, strings, or types
○ Parameters concretize the class prototype; constructor binds each class member
○ Can’t define type bounds on T

class FIFO #( type T = int , int entries = 8);


T items [entries];
int ptr;

function void push( T entry);


function T pull();
endclass
SystemVerilog Assertions (SVA)
SystemVerilog Assertions (SVA)

● The most complex component of SystemVerilog


○ Entire books written on just this topic
● SVA: a temporal property specification language
○ Allows you to formally specify expected behavior of RTL
● You are already familiar with ‘assert’ (so-called ‘immediate assertions’)

module testbench();
dut d (.addr, .dout);
● But how do I express properties that involve
initial begin the uArch of the RTL?
addr = ‘ h40; ● Can I express these properties (e.g. req-ack)
assert (dout == ‘ hDEADBEEF);
end
in a concise way?
endmodule
Concurrent Assertions

● Concurrent assertions are constantly monitored by the RTL simulator


○ Often embedded in the DUT RTL or an interface

module cpu();
assert property @(posedge clk) mem_addr[ 1: 0] != 2’ d0 && load_word | - > unaligned_load
assert property @(posedge clk) opcode == 0 | - > take_exception
assert property @(posedge clk) mem_stall |=> $stable(pc)
endmodule

● Properties are evaluated on a clock edge


● | - >: same-cycle implication
● | =>: next-cycle implication
● These properties can also be formally verified
System Functions

● You can call a system function in an SVA expression to simplify checking


historical properties
○ $stable(x) : indicates if x was unchanged from the previous clock cycle
○ $r os e( x )
○ $f el l ( x )
○ $pa s t ( x ) : gives you the value of x from 1 cycle ago
■ r s 1_ mem == $pa s t ( r s 1_ex )
Sequences
● Properties are made up of sequences + an implication
○ Many interfaces come with sequence libraries you can use to build complex properties
module cpu();
sequence stall
mem_stall;
endsequence

sequence unchanged_pc
##1 $stable(pc);
endsequence

property stall_holds_pc
@(posedge clk) stall | - > unchanged_pc;
endproperty

assert property (stall_holds_pc);


endmodule
Sequence Combinators

● Sequences are the core of SVA: they describe temporal RTL behavior
● Sequences can be combined with temporal operators
a ## 1 b / / a t hen b on t he nex t c y c l e
a # # N b / / a t hen b on t he Nt h c y c l e
a # # [ 1: 4] b / / a t hen b on t he 1- 4t h s ubs equent c y c l e
a # # [ 2: $] b / / a t hen b a f t er 2 or mor e c y c l es

s 1 and s 2 / / s equenc e s 1 a nd s 2 s uc c ee d
s 1 intersect s 2 / / s equenc e s 1 a nd s 2 s uc c ee d a nd end a t t he s a me t i me
s 1 or s 2 / / s equenc e s 1 or s 2 s uc c ee ds

● Sequences are combined with an implication to form a property


○ There’s a lot more to SVA
Coverage APIs
Coverage

● You’re probably familiar with software coverage tools


○ Track if a line of source code is hit by the unit tests
● Coverage is used to measure the thoroughness of the test suite
○ Are all the interesting cases in the code exercised?
● RTL coverage comes in two forms
○ Structural coverage: line, toggle, condition
○ Functional coverage: did a particular uArch feature specified by the DV engineer get
exercised?
● e.g. cache eviction, misaligned memory access, interrupt, all opcodes executed
Property Coverage

● Any SVA property can be tracked for coverage


○ Instead of ‘assert property’ use ‘cover property’

property req_ack;
req ##[ 1: 10] ack
endproperty
cover property (req_ack)

● Property covers are used in RTL to check that some multi -cycle uArch
behavior is exercised
○ e.g. did this req-ack handshake ever occur?
○ e.g. did a branch mispredict and predictor update happen?
Coverpoints and Covergroups

● Coverpoints track coverage of a single net


○ e.g. FSM state, control signals, data buses
● Covergroups group together coverpoints
○ Each coverpoint refers to a net whose value is tracked at every covergroup event
○ Can be used in RTL and in testbench code
module cpu ();
logic [ 5: 0] rs1, rs2; 15

logic [ 2: 0] funct3; 10 10 10

covergroup c @( posedge clk); 3 3 3


coverpoint rs1;
coverpoint funct3;
0 1 2 3 4 5 6 7 8
funct3 value
endgroup

endmodule
Coverpoint Bins

● Sometimes we don’t want to track each value a net can take on individually
○ Use the bins API to group some values together

module alu( input [ 31: 0] a, input [ 31: 0] b, input [ 3: 0] op, output [ 31: 0] out);
covergroup c();
coverpoint a{
bins zero = { 0};
bins max = { 32’ hffff_ffff};
// automatically allocate 100 uniformly sized bins for the remaining numbers
bins in_the_middle[ 100 ] = {[ 1: 32’ hffff_ffff - 1]};
}
endgroup
endmodule
Transaction-Level Modeling
Transactions
● Our testbenches are usually written at cycle-granularity
○ Leads to mixing of driving/monitoring protocols, timing details, golden modeling, and stimulus
○ Each of these concerns should be separated
● Model a single interaction with the DUT as a ‘transaction’
○ It can take multiple cycles
● We can build a stimulus generator and golden model at transaction-level

class MemReqTx();
bit [ 31: 0] addr;
bit [ 31: 0] wr_data; class Mem();
mem_op op; bit [ 31: 0] ram [];
endclass function MemRespTx processTx(MemReqTx tx);
endclass
class MemRespTx();
bit [ 31: 0] rd_data;
endclass
VIPs and Testbench Architecture

● Verification IPs consist of


tasks that encode Testbench
○ How to drive transactions into Mem VIP DUT (Mem)
Stimulus
an interface at cycle Driver Mem Interface
Transaction
Transaction
granularity MemReqTx
Monitor
○ How to translate cycle
granularity interface activity
into transactions Golden Model MemRespTx
● A testbench DUT Resps
○ Generates stimulus
○ Generates golden DUT MemRespTx Assert
Golden Resps Equals
behavior
○ Simulates actual DUT behavior
○ Checks correctness
Random Transaction Generation

● How do we generate transaction-level stimulus?


● SystemVerilog class members can be prefixed with the ‘rand’ keyword
○ These fields are marked as randomizable

class MemReqTx(); initial begin


rand bit [ 31: 0] addr; MemReqTx tx = new();
rand bit [ 31: 0] wr_data; tx.randomize();
rand mem_op op; end
endclass
Constrained Random

● You can constrain the random fields of a class inside or outside the class
○ You can add ad-hoc constraints when calling .randomize
class cls;
rand bit [ 7: 0] min, typ, max;

constraint range {
0 < min; typ < max; typ > min; max < 128 ;
}
extern constraint extra;
endclass

constraint cls :: extra { min > 5; };


initial begin
cls = new();
cls.randomize() with { min == 10; };
end
Randomization of Variable Length Data Structures
class Packet;
rand bit [ 3: 0] data [];

constraint size { data.size() > 5; data.size < 10; }

constraint values {
foreach (data[i]) {
data[i] == i + 1;
data[i] inside {[ 0: 8]};
}
}
endclass

● Many things I haven’t discussed


○ Biasing and distributions, soft constraints, disables, solve before, implications, dynamic
constraint on/off
Mailboxes for Safe Inter-Thread Communication
● Mailboxes are like golang channels
○ Bounded queues that allow one thread to send data to another

module example;
mailbox #( int ) m = new( 100 );

initial begin
for ( int i = 0; i < 200 ; i ++)
#1 m.put(i);
end

initial begin
for ( int i = 0; i < 200 ; i ++) begin
int i; # 2 m.get(i);
$display (i, m.num());
end
end
endmodule
Testbench Example
Register Bank

● Let’s test a simple register bank


○ Works like a memory
○ Multi-cycle (potentially variable) read/write latency
○ Uses a ready signal to indicate when a new operation (read/write) can begin

interface reg_if ( input clk); module regbank (reg_if.slave if );


logic rst; // implementation
logic [ 7: 0] addr; endmodule
logic [ 15: 0] wdata;
logic [ 15: 0] rdata; // Regbank transaction
mem_op op; class regbank_tx;
logic en; rand bit [ 7: 0] addr;
logic ready; rand bit [ 15: 0] wdata;
// primary/secondary modports bit [ 15: 0] rdata;
// drv_cb/mon_cb clocking blocks rand bit wr;
endinterface endclass
VIP Implementation
class driver; class monitor;
virtual reg_if vif; virtual reg_if vif;
mailbox drv_mbx; mailbox mon_mbx;

taskrun(); taskrun();
@(vif.drv_cb); @(vif.mon_cb);
forever begin if (vif.en) begin
regbank_tx tx; regbank_tx tx = new();
drv_mbx.get(tx); tx.addr = vif.mon_cb.addr;
vif.drv_cb.en <= 1; // assign op and wdata
vif.drv_cb.addr <= tx.addr; if (vif.mon_cb.op == READ) begin
// assign op and wdata @(vif.mon_cb);
@(vif.drv_cb); tx.rdata = vif.mon_cb.rdata;
while ( ! vif.drv_cb.ready) end
@(vif.drv_cb) mon_mbx.put(tx);
end end
endtask endtask
endclass endclass
Top-Level
● A rough sketch of the testbench top

module tb();
regbank dut (. * );
initial begin
// initialize driver/monitor classes
regbank_tx stim [ 100 ];
stim.randomize();
fork
drv.run(); mon.run();
join_none
drv.drv_mbx.put(stim);
while (mon.mon_mbx.size < 100 )
@(dut.drv_cb);
// Pull tx from mon_mbx and check correctness
end
endmodule
Conclusion

● SystemVerilog makes design easier and clearer than plain Verilog


● SystemVerilog has many useful verification features not found in open-source
environments
○ SVA, coverpoints, constrained random
● I’ve only scratched the surface
○ UVM
○ Hardware modeling
○ IPC
● Play around: https://www.edaplayground.com/x/CK
○ https://en.wikipedia.org/wiki/SystemVerilog
References
https://en.wikipedia.org/wiki/SystemV erilog

https://verificationguide.com/systemverilog/systemverilog-tutorial/

https://www.chipverify.com/systemverilog/systemverilog-tutorial

https://www.doulos.com/knowhow/systemverilog/systemverilog-tutorials/systemverilog-assertions-tutorial/

https://www.systemverilog.io/sva-basics

Advanced notes on SystemV erilog covergroups: https://staging.doulos.com/media/160 0/dvclub_austin.pdf

You might also like