Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
29 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
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 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
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