Sei sulla pagina 1di 218

SystemVerilog for Verification

Author: G. Syed Sheeraj

Day 1
Introduction Data Types Procedural Statements and Routines

Basic OOP Connecting the Testbench and Design Randomization

Threads and Interprocess Communication Advanced OOP concepts Advanced Interfaces

Functional Coverage Assertions

The Verification Process

What is the goal of verification? To make sure that the design is an accurate representation of the specification. Bugs are what you get when there is a discrepancy. Each bug found before tape-out is one fewer that ends up in the customers hand. Once you verified that the DUT performs its designated functions correctly, you need to see how it operates when there are errors.

Basic Testbench Functionality

The purpose of a testbench is to determine the correctness of the design under test (DUT). This is accomplished by the following steps. Generate stimulus Apply stimulus to the DUT Capture the response Check for correctness Measure progress against the overall verification goals

Directed Testing vs. Constrained Random Testing

100% Coverage Random Test Directed Test


Coverage convergence

Constrained Random tests

Many runs, different seeds

Add constraints

Directed testcase

Functional Coverage Identify holes

Minimal code modifications

What should you randomize?

Device Configuration Environment Configuration Input Data Protocol exceptions Delays Errors and violations

Layered Architecture SystemVerilog

Test Environment Functional Coverage Scenario Generator











Data Types

Data Types

Two-state : better performance, reduced memory usage Queues, dynamic and associative arrays and automatic storage: reduced memory usage, built-in support for searching and sorting Unions and packed structures: allows multiple views of same data Classes and structures: support for abstract data structures Strings: built-in string support Enumerated types: code is easier to write and understand

Built-in Data Types

Verilog provides reg, wire, integer, time, real logic type Can be driven by continuous assignments, gates and modules in addition to being a variable Two-state types bit Unsigned byte, shortint, int, longint Signed

Two-state Data Types Examples

bit b; bit [31:0] b32; int i; byte b8; shortint s; longint l;

// // // // // //

2-state, 2-state, 2-state, 2-state, 2-state, 2-state,

single bit 32-bit unsigned integer 32-bit signed integer 8-bit signed integer 16-bit signed integer 64-bit signed integer

// To get unsigned from signed types byte unsigned u_byte; // 2-state, 8-bit unsigned integer

Fixed-Size Arrays

SystemVerilog lets you use the shortcut of just giving the array size (C-Style)
int sv_mem[16]; // equivalent to int sv_mem[0:15];

Multidimensional arrays
int array2 [0:7] [0:3]; // verbose declaration int array3 [8] [4]; // compact declaration

Initializing an array
int ascend [4] = {0,1,2,3}; // initialize 4 elements

Basic array operations

initial begin bit [31:0] src[5], dst[5]; for (int i=0; i< $size(src); i++) src[i] = i; foreach (dst[j]) dst[j] = src[j] * 2; // dst doubles src values end

For multidimensional arrays, use

foreach (md[i,j])

Multi-dimensional Array

Initialize and step through multi dimensional array

$display("New value:"); int md [2] [3]; md = {{9, 8, 7}, 3{5}}; // Replicate last 3 values foreach (md[i,j]) // Yes, this is the right syntax $display("md[%0d][%0d] = %0d", i, j, md[i][j]);
New value: md[0][0] = 9 md[0][1] = 8 md[0][2] = 7 md[1][0] = 5 md[1][1] = 5 md[1][2] = 5

Basic array operations copy and compare

Without using loops
initial begin bit [31:0] src[5] = {0,1,2,3,4}, dst[5] = {5,4,3,2,1}; // Aggregate compare the two arrays if (src == dst) $display(src == dst); else $display(src != dst); // Aggregate copy all src values to dst dst = src ; end Comparison limited to

equality and inequality

Packed and Unpacked Arrays

Unpacked Array
bit [7:0] b_array [0:2] ;

Stored in three long words

b_array[0] b_array[1] b_array[2] Packed Array

Unused Space
Stored in one long word

bit [2:0] [7:0] b_array ;



Packed Arrays Usage Example

For some data types, we may want to access the entire value and also divide it into smaller elements A SystemVerilog packed array is treated as both an array and a single value Only Fixed arrays can be packed.
bit [3:0] [7:0] bytes; // 4 bytes packed into 32-bits bytes = 32hdead_beef; $displayh(bytes, // Show all 32-bits bytes[3], // most significant byte "de" bytes[3][7]); // most significant bit "1

Dynamic Arrays

Arrays which can grow and shrink Array size will be decided at run-time Declared using a empty word subscript [] Array is initially empty and space is allocated when new[] is called If a name of an array is passed to new[] operator, then the values in the array are copied

Dynamic Array - Example

int dyn[], d2[]; // Empty dynamic arrays initial begin // Allocate 5 elements dyn = new[5] ; foreach (dyn[j]) dyn[j] = j; // Initialize d2 = dyn; // copy a dynamic array dyn = new[20](dyn); // Expand and copy dyn = new[100]; // Allocate 100 new integers // old values are lost dyn.delete; // Delete all elements end


Queues can also grow and shrink similar to dynamic arrays But with a queue you can add and remove elements anywhere Search and sort can be done easily Declared using a $ word subscript i.e., [$]

Queue - Example
int j = 1, b[$] = {3,4}, q[$] = {0,2,5} ; // {0,2,5} Initial Queue initial begin q.insert(1,j); // {0,1,2,5} Insert 1 at 1 // {0,1,2,3,4,5} q.insert(3,b); // Insert whole b at 3 q.delete(1); // {0,2,3,4,5} // Delete #1 element q.push_front(6) // {6,0,2,3,4,5} j = q.pop_back; // {6,0,2,3,4} j = 5 end

Associative Arrays
Memory gets allocated only when an element is written

Stores entries in a sparse matrix Declared with wildcard syntax [*]

data index 0 3 42 1000



The memory used to store the above is far less than would be needed to store a fixed or dynamic array with 200,000 entrires You can use the function exists to check if an element exists. Ex : if (data.exists(4521))

Associative Array - Example

assoc is initialized at indices 1, 2, 4, 8, 16 etc initial begin logic [63:0] assoc[*], idx = 1; // Initialize widely scattered values repeat (64) begin assoc[idx] = idx ; idx = idx << 1; end // step through all index values with foreach foreach (assoc[i]) $display(assoc[%h] = %h,i, assoc[i]);

Associative Array Example (contd.)

// step through all index values with functions if (assoc.first(idx)) begin do $display(assoc[%h] = %h,idx,assoc[idx]); while (; end // Find and delete first element assoc.first(idx); assoc.delete(idx); end

Array Methods

Array reduction methods sum, product, and, or, xor Array locator methods min, max, unique find, find_index, find_first, find_first_index, find_last, find_last_index

Array Reduction Method - sum

bit on[10]; // Array of single bits int sum_int; initial begin foreach (on[i]) on[i] = i; // on[i] gets 0 or 1 // Print the single-bit sum $display("on.sum = %0d", on.sum); // on.sum = 1 // Sum the values using 32-bits as sum_int is 32-bits sum_int = on.sum; $display("sum_int = %0d", sum_int); // sum_int = 5 end

Adds together all the values in an array

Array Locator Methods

What is the largest value in an array? Does an array contain a certain value? Methods always return a queue
int f[6] = {1,6,2,6,8,6}; int q[$] = {1,3,5,7}, tq[$]; tq = q.min; // {1} tq = q.max; // {7} tq = f.unique; // {1,6,2,8} int d[] = {9,1,8,3,4,4}, tq[$]; // Find all elements greater than 3 tq = d.find with (item > 3); // {9,8,4,4} tq = d.find_index with (item > 3); // {0,2,4,5} tq = d.find_first with (item >99); // {}- none found tq= d.find_first_index with (item ==8); // {2} d[2] =8

Choosing a Storage Type

Choose the right storage type based on flexibility, memory usage, speed and sorting. Fixed size packets and accessed sequentially -> Fixed Size Array Variable size packets and accessed sequentially -> Dynamic Array Queues are great where the number of elements grow and shrink and you need search and sort functionalities. Ex : Scoreboard Content Addressable memories -> Associative Array Modeling very large memories -> Associative Array Command names and values from a file -> Associative Array using the command as a string index.

User-Defined Types

Create new types using the typedef statement

parameter OPSIZE = 8; typedef reg [OPSIZE-1:0] opreg_t; opreg_t op_a, op_b; // Creating a struct and a new type struct {bit [7:0] r,g,b;} pixel; typedef struct {bit [7:0] r,g,b;} pixel_s; pixel_s my_pixel;

Enumerated Types
Creates a strong variable type that is limited to a set of specified names. Ex : instruction opcode, state machine value
enum {RED, BLUE, GREEN} color; // Defining enumerated values typedef enum {INIT, DECODE=2,IDLE} fsm_type_e; color = color.first; do begin $display(Color = %0d/%0s,color,; color =; end while (color != color.first); // Done at wrap-around

Converting to and from Enumerated Types

typedef enum {RED, BLUE, GREEN} COLOR_E; COLOR_E color, c2; integer c; initial begin c = color; // Convert from enum to integer c++; // Increment integer if (!$cast(color, c)) // Cast integer back to enum $display("Cast failed for c=%0d", c); $display("Color is %0d / %0s", color,; c2 = COLOR_E(c); // No type checking done end

Constants and Strings

Const modifier allows you to make a variable that can be initialized in the declaration.
initial begin const byte colon = :; . end

SystemVerilog string type holds variable length strings. Unlike C, there is no null character at the end of the string. Strings use dynamic memory allocation.

String Methods
string s; initial begin s = SystemVerilog; $display(s.getc(0)); // Display : 83 // Display : SYSTEMVERILOG $display(s.toupper()); s = {s,3.1b}; // SystemVerilog3.1b s.putc(s.len()-1,a); // change b -> a $display(s.substr(2,5)); // Display : stem my_log( $psprintf(%s %5d,s,42)); end task my_log (string message); $display(@%0d: %s,$time,message); endtask

Expression Width
bit [7:0] b8; bit one = 1b1; // Single bit $displayb(one + one); // A: 1+1 = 0 b8 = one + one; // B: 1+1 = 2 $displayb(b8); $displayb(one + one + 2b0); // C: 1+1 = 2 with constant $displayb(2(one) + one); // D: 1+1 = 2 with cast

Procedural Statements and Routines

Procedural Statements

Can declare a variable inside a for loop Increment ++ and decrement -- operators are available The above operators in both pre and post forms Can put the same label on matching end and join Can put a label on statements like endmodule, endtask, endfunction etc break and continue statements to control the flow in loop structures

Procedural statements and operators - Example

initial begin : example integer array [10], sum, j; // Declare i inside for statement for (int i=0; i<10; i++) array[i] = i; // Add up values in the array sum = array[9] ; j = 8; do // do while loop sum += array[j]; // Accumulate while(j--); // Test if j==0 $display(Sum=%4d,sum); // %4d specify width end : example // End label

Tasks, Functions and Void Functions

Multiple statements without requiring begin..end block. Function can have output port, inout port. In SystemVerilog, if you want to call a function and ignore its return value, cast the result to void.
void (my_func(42));

Any debug routine should be a void function rather than a task so that it can be called from any function or task
function void print_state (); $display(@%0d : state = %0s,$time,; endfunction

Tasks, Functions
Task and function can be defined inside following : modules, packages, interfaces, program blocks, class Task/function can be called inside following : program blocks, modules, procedural blocks

Routine Arguments

C-style routine arguments

task mytask1 (output logic [31:0] x, input logic y); endtask

Argument direction
task T3 (a,b,output bit [15:0] u, v);

The arguments a and b are input logic, 1 bit wide. The arguments u and v are 16-bit output bit types.

Advanced argument types

You can pass an array into a routine You can specify that an argument is passed by reference, rather than copying it If you dont want the routine to change the array values, use const ref type. With this the compiler checks that your routine does not modify the array.
function void print_sum (const ref int a[]); int sum = 0; for (int i = 0; i <a.size; i++) sum += a[i]; $display(The sum of the array is %d,sum); endfunction

Default argument values

In SystemVerilog you can specify a default value that is used if you leave out an argument in the call
function void print_sum (ref int a[], input int start = 0, input int last = -1); int sum = 0; if (last == -1 || last > a.size) last = a.size ; for (int i = start; i <last; i++) sum += a[i]; $display(The sum of the array is %d,sum); endfunction

Pass by reference - Example

module function_by_ref (); reg [7:0] data ; reg parity_out; reg [7:0] odata; function automatic reg parity (ref reg [7:0] idata); parity = 0; for (int i= 0; i < 8; i ++) begin parity = parity ^ idata[i]; end // We can modify the data passed through reference idata = idata + 1 ; endfunction : parity

initial begin parity_out = 0; data = 0;

Pass by reference Example cntd.

for (int i=250; i<256; i++) begin #5; data = i; $display(" BEFORE : Data = %b == %3d, Parity = %d", data, data, parity_out); parity_out = parity (data); $display (" AFTER : DATA = %b == %3d, Parity = %d", data, data, parity_out); end #30 $finish; end endmodule

Pass by reference Example Output

Output of above Example is :
BEFORE AFTER BEFORE AFTER BEFORE AFTER BEFORE AFTER BEFORE AFTER BEFORE AFTER : : : : : : : : : : : : Data DATA Data DATA Data DATA Data DATA Data DATA Data DATA = = = = = = = = = = = = 11111010 11111011 11111011 11111100 11111100 11111101 11111101 11111110 11111110 11111111 11111111 00000000 == == == == == == == == == == == == 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 0, Parity Parity Parity Parity Parity Parity Parity Parity Parity Parity Parity Parity = = = = = = = = = = = = 0 0 0 1 1 0 0 1 1 1 1 0

Local Data Storage

program automatic test ; task wait_for_mem (input [31:0] addr, expect_data, output success); while(bus.addr !== addr) @(bus.addr); success = ( == expect_data); endtask endprogram

You can call this task multiple times concurrently as the addr and expect_data arguments are stored separately for each call

Time Values

`timescale compiler directive You must compile the files in proper order to be sure all the delays use the proper scale and precision. The timeunit and timeprecision declarations eliminate this ambiguity. Put these in every module that has delay.
module timing; timeunit 1ns; timeprecision 1ps; endmodule

Day 1 Summary
logic, bit, byte, shortint, int, longint

Fixed size arrays, dynamic arrays, queues, associative arrays Packed arrays and unpacked arrays User defined types, structures, enumerated types, constants and strings Improvements in procedural statements and operators Arguments : C-style declarations, sticky direction, default values, pass by reference Automatic for calling multiple times Time values


Day 2 - Agenda

Basic OOP Connecting the Testbench and Design Randomization

Basic OOP


OOP Object Oriented Programming Lets you create complex data types and tie them together with routines that work with them When you work with transactions instead of signal transitions, you are more productive

Your first Class

class BusTran ; bit [31:0] addr, crc, data[8]; function void display; $display(BusTran: %h,addr); endfunction : display function void calc_crc; crc = addr ^ data.xor; endfunction : calc_crc endclass : BusTran

OOP Terminology

Class Basic building block containing routines and variables Object An instance of a class Handle A pointer to an object Property A variable that holds a data inside class declaration Method The procedural code that manipulates variables Prototype The header of a routine that shows the name, type and argument list

Creating new Objects

BusTran b; b = new ; // Declare a handle; initialized to null // Allocate a BusTran object, returns the // address where the object is stored

User defined new function

class BusTran; logic [31:0] addr, crc, data[8]; function new (logic [31:0] addr=3,d=5); this.addr = addr; foreach(data[i]) data[i] = d; endfunction endclass : BusTran

Object Deallocation
BusTran b; b = new ; b = new ; b = null ; // // // // create a handle Allocate a new BusTran Allocate a second one, free the first Deallocate the second

SystemVerilog performs automatic garbage collection when no more handles refer to an object

Static Variables vs Global Variables

class BusTran; static int count = 0; // Number of objects created int id ; // unique instance id function new ; id = count++; endfunction endclass : BusTran BusTran b1, b2; initial begin b1 = new ; // First instance, id=0 // Second instance, id=1 b2 = new ; $display(Second id=%d,count=%d,,b2.count); $display(First id=%d,count=%d,,b1.count); end // Display : Second id=1,count=2 // Display : First id=0,count=2

Class Routines
class BusTran; bit [31:0] addr, crc, data[8]; extern function void display(); endclass : BusTran function void BusTran::display(); $display(@%0d: BusTran addr=%h, crc=%h, addr,crc); $write(\tdata[0-7]=); foreach (data[i]) $write(%d ,data[i]); $display(); endfunction : display

Scoping Rules

A scope is a block of code such as a module, program, task, function, class, or begin-end block. The for and foreach loops automatically create a block so that an index variable can be declared or created local to the scope of the loop. If you forget to declare a variable, SystemVerilog looks up the higher scopes until it finds a match. Suggestion : Declare all your variables in the smallest scope that encloses all uses of the variable.

Name scope
int limit; // $root.limit program p; int limit, i; // $root.p.limit class Foo; int limit, array[]; // $root.p.Foo.limit task print (int limit); // $root.p.Foo.print.limit for (int i=0; i<limit;i++) $display(); endtask : print endclass : Foo initial begin int limit = $root.limit; // $root.p.$unnamed.limit end endprogram : p

Using one class inside another

A class can contain an instance of another class, using a handle to an object.

class BusTran; bit [31:0] addr, crc, data[8]; statistics stats; endclass : BusTran class Statistics; time startT, stopT; static int ntrans = 0; static time total_elapsed_time; endclass : Statistics

Understanding Dynamic Objects

Passing objects to routines

task generator; BusTran b; b = new ; transmit(b); endtask task transmit(BusTran b); endtask : transmit

Understanding Dynamic Objects

Modifying handle in a task

task create_packet (BusTran bt); bt = new ; bt.addr = 42; endtask BusTran b; initial begin create_packet(b); // Call bad routine $display(b.addr); // Fails because b=null end task create_packet (ref BusTran bt); // Good endtask : create_packet

Understanding Dynamic Objects

Modifying objects in flight

task generator_bad (int n); BusTran b ; b = new ; repeat(n) begin b.addr = $random(); $display(Sending addr = %h,b.addr); transmit(b); end endtask : generator_bad

Understanding Dynamic Objects

Modifying objects in flight

task generator_good (int n); BusTran b ; repeat(n) begin b = new ; b.addr = $random(); $display(Sending addr = %h,b.addr); transmit(b); end endtask : generator_good

Copying Objects

Copying an object with new

BusTran src, dst; initial begin src = new ; dst = new src ; end

The above is a shallow copy, similar to photocopy of the original. If the class contains handle to another class, only the top level object is copied by new, not the lower level one.

Copying Objects

Copying a complex class with new

class BusTran ; bit [31:0] addr, crc, data[8]; static int count = 0; int id ; Statistics stats; function new ; stats = new ; id = count++ ; endfunction endclass

Copying Objects

Copying a complex class with new

BusTran src, dst ; initial begin src = new ; src.stats.startT = 42; dst = new src ; dst.stats.startT = 84; src end

// Create first object // Copy src to dst // Changes stats for dst &

Complex class copy using new

src id=3 stats



id=3 stats


id=3 stats



id=3 stats

Copying Objects

Complex class with deep copy function

class BusTran ; function BusTran copy; copy = new ; // construct destination copy.addr = addr ; // Fill in data values copy.crc = crc ; = data ; copy.stats = stats.copy ; // Call copy for stats id = count++; endfunction : copy endclass

Complex class copy using copy function


id=3 stats



id=4 stats


Connecting the Testbench and Design

Separating the Testbench and Design

The testbench forms the real world around the design, mimicking the entire environment

request[1:0] reset Arbiter

grant[1:0] clk

Communication with ports

module arb_port (output logic [1:0] grant, input logic [1:0] request, input logic reset, input logic clk); always @ ( posedge clk or posedge reset) begin if (reset) grant <= 2b00; else end endmodule : arb_port

Communication with ports

module test (input logic [1:0] grant, output logic [1:0] request, output logic reset, input logic clk); initial begin @(posedge clk) request <= 2b01; $display(@%0d : Drove req=01,$time); repeat(2) @ (posedge clk); if (grant != 2b01) $display(@%0d: a1: grant != 2b01,$time); $finish; end endmodule : test

Top module without an interface

module top; logic [1:0] request, grant; logic reset; bit clk; always #5 clk = ~clk; arb_port a1 (grant, request, reset, clk); test t1 (grant, request, reset, clk); endmodule : top

The Interface Construct

interface arb_if (input bit clk); logic [1:0] grant, request; logic reset; endinterface : arb_if


Interface clk


Top module using simple arbiter interface

module top; bit clk; always #5 clk = ~clk; arb_if arbif(clk); arb a1 (arbif); test t1 (arbif); endmodule : top

Testbench using simple arbiter interface

module test (arb_if arbif); initial begin @(posedge arbif.clk) arbif.request <= 2b01; $display(@%0d : Drove req=01,$time); repeat(2) @ (posedge arbif.clk); if (arbif.grant != 2b01) $display(@%0d: a1: grant != 2b01,$time); $finish; end endmodule : test

Arbiter using interface

module arb (arb_if arbif); always @ ( posedge arbif.clk or posedge arbif.reset) begin if (arbif.reset) arbif.grant <= 2b00; else end endmodule : arb

Using modports

The modport construct in an interface lets you group signals and specify directions.
interface arb_if (input bit clk); logic [1:0] grant, request; logic reset; modport TEST (output request, reset, input grant, clk); modport DUT (input request, reset, clk, output grant); modport MONITOR (input request, grant, reset, clk); endinterface : arb_if

Using interface with modports

// Arbiter model with interface using modports module arb (arb_if.DUT arbif); endmodule // Testbench with interface using modports module test (arb_if.TEST arbif); endmodule

Clocking block
Synchronous signals can be bundled using clocking block.

interface mii_if; clocking mtx @(posedge tx_clk); output txd, tx_en, tx_err; endclocking : mtx clocking mrx @(posedge rx_clk); input rxd, rx_dv, rx_err; endclocking : mrx Clocking block name can be just used for waiting to its edge as follows

@ (this.sigs.mtx); this.sigs.mtx.txd <= nibble; The synchronous edge can be changed easily later.

Stimulus Timing (Driving and Sampling)

In SystemVerilog, testbench code is in a program block, which is similar to a module in that it can contain code and variables and be instantiated in other modules. A program cannot have any hierarchy such as instances of modules, interfaces or other programs. It can have objects or instances of classes. Program runs in the reactive region. Upon termination implicitly calls $exit Always blocks are not allowed inside program block

Program Module Interactions

The program block can read and write all signals in modules. But a module has no visibility into a program. A program can call a routine in a module to perform various actions. The routine can set values on internal signals, also known as backdoor load. For forcing a signal from a program block, you need to write a task in the module to do the force and then call from the program


Randomization in SV
class Packet; // The random variables rand bit [31:0] src, dst, data[8]; randc bit [7:0] kind; // Limit the values for src constraint c {src >10; src <15;} endclass : Packet Packet p; initial begin p = new; assert(p.randomize()); transmit(p); end

Constraint Details
// Set membership and inside operator rand int c; int lo, hi; constraint c_range { c inside {[lo:hi]}; // lo <= c and c <= hi } // Inverted random set constraint constraint c_range { !(c inside {[lo:hi]}); }

Weighted Distributions
// Weighted random distribution with dist rand int src, dst; constraint c_dist { src dist {0:=40, [1:3]:=60}; // src = 0, weight = 40/220 // src = 1, weight = 60/220 // src = 2, weight = 60/220 // src = 3, weight = 60/220 }

Conditional constraints
// Constraint block with implication operator constraint c_io { (io_space_mode) -> addr[31] == 1b1; } // Constraint block with if-else operator constraint c_len_rw { if (op == READ) len inside {[BYTE:LWRD]}; else len == LWRD; }

Choose the right arithmetic operator

// Expensive constraint with mod and unsized variable rand int [31:0] addr; constraint slow_near_page_boundary { addr % 4096 inside {[0:20], [4075:4095]}; } // Efficient constraint with bit extract rand bit [31:0] addr; constraint near_page_boundary { addr[11:0] inside {[0:20],[4075:4095]}; }

Guiding distribution with solvebefore

class SolveBefore; rand bit x; // 0 or 1 rand shortint unsigned len; constraint c_x_len { (x==0) -> len == 1; solve x before len; } endclass : SolveBefore

// 0 to 65536

Controlling Multiple Constraints

class Packet; rand int length ; constraint c_short {length inside {[1:32]}; } constraint c_long {length inside {[1000:1023]}; } endclass : Packet Packet p; initial begin p = new ; p.c_short.constraint_mode(0); assert (p.randomize()); end

In-line Constraints
class Transaction; rand bit [31:0] addr, data; constraint c1 { addr inside {[0:100],[1000:2000]};} endclass Transaction t ; initial begin t = new; // addr is 50-100, 1000-1500, data < 10 assert(t.randomize() with {addr >= 50;addr <=1500; data <10;}); driveBus(t); end

pre_randomize and post_randomize

Sometimes you need to perform an action immediately before every randomize call or immediately afterwards. Ex : set nonrandom class variables (such as limits) before randomization starts or calculate crc for random data payload pre_randomize and post_randomize helps for the above situations. They are automatically called special void functions.

rand_mode disables randomization of variables

class Packet; rand bit [7:0] length; rand bit [7:0] payload[]; constraint c_valid {length > 0; payload.size == length;} endclass Packet p; initial begin p = new; // Make length nonrandom then randomize packet p.length.rand_mode(0); p.length = 42; assert(p.randomize()); end

Iterative and Array Constraints

Array Size Constraining dynamic array size
class dyn_size; rand reg [31:0] d[]; constraint d_size {d.size inside {[1:10]};} endclass

Sum of elements
parameter MAX_TRANSFER_LEN=10; class StrobePat; rand bit strobe[MAX_TRANSFER_LEN]; constraint c_set_four {strobe.sum == 3h4;} endclass

Atomic Stimulus Generation vs Scenario Generation

// Command generator using randsequence initial begin for (int i=0;i<=15;i++) begin randsequence (stream) stream : cfg_read := 1 | io_read := 2 | mem_read := 5; cfg_read : ; io_read : ; mem_read : ; endsequence end // for end

Random Control

Use randcase to make weighted choice between several actions, without having to create a class and instance.
initial begin int len; randcase 1: len = 8: len = 1: len = endcase $display(len end

$urandom_range(0,2); // 10% : 0, 1 or 2 $urandom_range(3,5); // 80% : 3, 4 or 5 $urandom_range(6,7); // 10% : 6 or 7 = %0d,len);

Useful distribution functions

$dist_exponential Exponential decay $dist_normal Bell shaped distribution $dist_poisson Bell shaped distribution $dist_uniform Flat distribution $random Flat distribution, returning signed 32-bit random $urandom Flat distribution, returning unsigned 32-bit random $urandom_range Flat distribution over a range

Random Device Configuration Example : 4 port Ethernet Switch

class eth_cfg; rand bit [ 3:0] in_use; // Ports used in test rand bit [47:0] mac_addr[4]; // MAC addresses rand bit [ 3:0] is_100; // 100Mbps mode rand int run_for_n_frames; // #frames in test // Force some addr bit when running in unicast mode constraint local_unicast { foreach (mac_addr[i]) mac_addr[i][41:40] == 2b00; } constraint reasonable { run_for_n_frames inside {[1:100]}; } endclass : eth_cfg

Random Device Configuration Environment with eth_cfg

class Environment; eth_cfg cfg; eth_src gen[4]; eth_mii drv[4]; function new; cfg = new; // Construct the cfg endfunction function void gen_cfg; assert(cfg.randomize()); // Randomize the cfg endfunction

Random Device Configuration Environment with eth_cfg (contd.)

// Use random configuration to build the environment function void build; foreach (src[i]) if (cfg.in_use[i]) begin gen[i] = new(); drv[i] = new(); if(cfg.is_100[i]) drv[i].set_speed(100); end endfunction task run; // Start the testbench structure endtask endclass : Environment

Random Device Configuration Simple test with eth_cfg (contd.)

program test; Environment env; initial begin env = new; env.gen_cfg;;; env.wrapup; end endprogram

// // // // //

Construct environment Create random configuration Build the testbench environment Run the test Clean up after test & report

Random Device Configuration Simple test that overrides random cfg

program test; Environment env; initial begin env = new; // Construct environment env.gen_cfg; // Create random configuration // Override random in_use turn all 4 ports on env.cfg.in_use = 4b1111;; // Build the testbench environment; // Run the test env.wrapup; // Clean up after test & report end endprogram

Day 2 - Summary
: syntax, terminology, creation, deallocation Class Routines : Scoping rules, one inside another, understanding of dynamic objects, copying objects interface : syntax, modport program block : Timing, interaction with module rand, randc for randomization Constraints, weighted distribution, conditional constraints, randomize with, solve before,
class constraint_mode, rand_mode pre_randomize, post_randomize, randsequence, randcase

Random device configuration


Day 3 - Agenda

Threads and Interprocess Communication Advanced OOP concepts Advanced Interfaces

Threads and Interprocess Communication

Working with Threads

Verilog has initial, always, beginend, forkjoin and forever constructs for thread creation. SystemVerilog adds two new ways to create threads with the fork join_none and fork join_any statements. In testbench to communicate, synchronize and control the threads, verilog has constructs like event, @ event control, the wait and disable statements. SystemVerilog adds mailbox and semaphore for the above need.

fork . join blocks








SystemVerilog enhances the verilog event in several ways. An event is now a handle to a synchronization object that can be passed around to routines. In verilog, if the triggering thread executes before the blocking thread, the trigger is missed. SystemVerilog introduces triggered function that lets you check whether an event has been triggered.

Blocking on an event in verilog

event e1, e2; initial begin $display(@%0d: -> e1; @e2; $display(@%0d: end initial begin $display(@%0d: -> e2; @e1; $display(@%0d: end

@0: 1: before trigger @0: 2: before trigger @0: 1: after trigger

1: before trigger,$time);

1: after trigger,$time);

2: before trigger,$time);

2: after trigger,$time);

Waiting for an event trigger

@0: 1: @0: 2: event e1, e2; @0: 1: initial begin @0: 2: $display(@%0d: 1: before trigger,$time);
-> e1; wait(e2.triggered); $display(@%0d: 1: after trigger,$time); end initial begin $display(@%0d: 2: before trigger,$time); -> e2; wait(e1.triggered); $display(@%0d: 2: after trigger,$time); end

before trigger before trigger after trigger after trigger

Passing events
class Generator; event done; function new (event done); // Pass event from TB this.done = done; endfunction task run; fork begin -> done; // Tell that test is done end join_none endtask endclass : Generator

Passing events (cntd.)

program automatic test; event gen_done; Generator gen; initial begin gen = new(gen_done); //Instantiate the Generator; // Run the transactor wait(gen_done.triggered); // wait for finish end endprogram : test


A Semaphore allows you to control access to a resource Ex : A library book, A common car for family members Semaphore can be used in a testbench when you have a resource, such as bus, that may have multiple requestors from inside the testbench but as part of physical design, can only have one driver. In SystemVerilog, a thread that requests a key when one is not available always blocks. Multiple blocking threads are queued in FIFO order.

Semaphore operations

Create a semaphore with one or more keys using new method Get one or more keys with get method Return one or more keys with put method If you want to try to get a semaphore, but not block, use try_get function. It returns 1 if there are enough keys, and 0 if there are insufficient keys.

Semaphore operations
program automatic test; semaphore sem; // create semaphore initial begin sem = new(1); // Allocate with 1 key fork sequencer; // Spawn two threads that both sequencer; // do bus transactions join end task automatic sequencer; repeat($urandom%10) @bus.cb; // Random wait, 0-9 cycles sendTrans; // Execute the transaction endtask

Semaphore operations
task automatic sendTrans; sem.get(1); @bus.cb; bus.cb.addr <= t.addr; sem.put(1); endtask endprogram // Get the key to the bus // Drive signals onto bus // Release the key back


How do you pass information between two threads? Generator needs to create many transactions and pass them to a driver. A mailbox is just like a FIFO with a source and sink. Mailboxes can have a maximum size or can be unlimited. When source puts a value into a sized mailbox that is full, it blocks until data is removed. If sink tries to remove data from a mailbox that is empty, it blocks until data is available.

Mailboxes (cntd.)
A mailbox is an object and thus has to be instantiated by calling the new function. Put data into a mailbox using put method. put blocks if mailbox is full. Remove data from a mailbox using get. get blocks if mailbox is empty. peek task gets a copy of the data in the mailbox but does not remove it. If you dont want your code to block, use the try_get and try_peek functions.

generator mailbox driver

Mailbox in a testbench
program mailbox_example; class Generator; Transaction tr; mailbox mbx; function new(mailbox mbx); this.mbx = mbx; endfunction task run; repeat(10) begin tr = new; assert(tr.randomize()); mbx.put(tr); // Send out transaction end endtask : run endclass : Generator

Mailbox in a testbench (cntd.)

class Driver; Transaction tr; mailbox mbx; function new(mailbox mbx); this.mbx = mbx; endfunction task run; repeat(10) begin mbx.get(tr); // Fetch next transaction @(posedge busif.cb.ack); busif.cb.kind <= tr.kind; end endtask : run endclass : Driver

Mailbox in a testbench (cntd.)

mailbox mbx; // Mailbox connecting gen & drv Generator gen; Driver drv; initial begin mbx = new; gen = new(mbx); drv = new(mbx); fork;; join end endprogram : mailbox_example

Advanced OOP


Inheritance allows a new class to be derived from an existing one in order to share its variables and routines. The original class is known as the base or super class. The new one, since it extends the capability of the base class, is called the extended class. Inheritance provides reusability by adding features, such as error injection, to an existing class, the base transaction, without modifying the base class.

Base Transaction Class

class Transaction; rand bit [31:0] src, dst, data[8]; // Variables bit [31:0] crc; virtual function void calc_crc; crc = src ^ dst ^ data.xor; endfunction virtual function void display; $display(Tr: src=%h, dst=%h, crc=%h, src,dst,crc); endfunction endclass

Extended Transaction class

class BadTr extends Transaction; rand bit bad_crc; virtual function void calc_crc; super.calc_crc(); // Compute good crc if(bad_crc) crc = ~crc; // Corrupt crc bits endfunction virtual function void display; $write(BadTr: bad_crc=%b,bad_crc); super.display(); endfunction endclass : BadTr

Factory Patterns
Blueprint (from test)



Generated stream

Factory Patterns
Blueprint (from test)



Generated stream

Generator class using Factory Patterns

class Generator; mailbox gen2drv; Transaction blueprint; function new(mailbox gen2drv); this.gen2drv = gen2drv; endfunction function build; blueprint = new; endfunction

Generator class using Factory Patterns (cntd.)

task run; Transaction tr; forever begin assert(blueprint.randomize); tr = blueprint.copy; gen2drv.put(tr); end endtask endclass : Generator

Using the extended Transaction class

program automatic test; Environment env; initial begin env = new;; // Construct the blueprint BadTr bad; bad = new; // Create a bad transaction env.gen.blueprint = bad; // Replace the blueprint // with bad one; // Run the test env.wrap_up; // Cleanup afterwards end endprogram

To create a verification environment that you can use for all tests with no changes. The key requirement is that the testbench must provide a hook where the test program can inject new code without modifying the original classes. Driver may want to do: inject errors, drop the transaction, delay the transaction, synchronize this transaction with others, put the transaction in scoreboard, gather functional coverage data. Driver just needs to call back a routine defined in the top-level test. The beauty of this technique is that the callback routine can be defined differently in every test. The test can add new functionality to the driver using callbacks without editing Driver class.

Callback flow
task Driver::run; forever begin . <pre_callback> transmit(tr); <post_callback> . end endtask task pre_callback; . endtask

task post_callback; . endtask

Creating a Callback Base callback class

class Driver_cbs; // Driver callbacks virtual task pre_tx(Transaction tr, ref bit drop); // By default, callback does nothing endtask virtual task post_tx(Transaction tr); // By default, callback does nothing endtask endclass

Driver class with callbacks

class Driver; Driver_cbs cbs[$]; // Queue of callbacks task run; bit drop; Transaction tr; forever begin agt2drv.get(tr); foreach(cbs[i]) cbs[i].pre_tx(tr,drop); if(!drop) transmit(tr); foreach(cbs[i]) cbs[i].post_tx(tr); end endtask endclass

Using callback for randomly dropping a packet

class Driver_cbs_drop extends Driver_cbs; virtual task pre_tx(Transaction tr, ref bit drop); // Randomly drop 1 out of every 100 transactions drop = ( $urandom_range(0,99) == 0); endtask endclass : Driver_cbs_drop program automatic test; Driver_cbs_drop dcd; dcd = new;;; endprogram

Advanced Interfaces

Virtual Interfaces

In a network switch, a single Driver class may connect to many interfaces, one for each input channel of the DUT. Write a generic Driver, instantiate it N times and have it connect each of the N physical interfaces. You can do the above in SystemVerilog by using a virtual interface that is merely a handle to a physical interface.

Interface with clocking block

// Rx interface with modports and clocking block interface Rx_if (input logic rclk); logic [7:0] data; logic soc, en, clav; clocking cb @(posedge rclk); output data, soc, clav; input en; endclocking : cb modport TB (clocking cb); endinterface : Rx_if

Interface with clocking block (cntd.)

// Tx interface with modports and clocking block interface Tx_if (input logic tclk); logic [7:0] data; logic soc, en, clav; clocking cb @(posedge tclk); input data, soc, en; output clav; endclocking : cb modport TB (clocking cb); endinterface : Tx_if

Testbench using physical interfaces

program automatic test(Rx_if.TB Rx0, Rx1, Rx2, Rx3, Tx_if.TB Tx0, Tx1, Tx2, Tx3, input logic clk, output logic rst); bit [7:0] bytes[`ATM_SIZE]; initial begin rst <= 1; <= 0; receive_cell0; end

Testbench using virtual interfaces

program automatic test(Rx_if.TB Rx0, Rx1, Rx2, Rx3, Tx_if.TB Tx0, Tx1, Tx2, Tx3, input logic clk, output logic rst); Driver drv[4]; Monitor mon[4]; Scoreboard scb[4]; virtual Rx_if.TB vRx[4] = `{Rx0, Rx1, Rx2, Rx3}; virtual Tx_if.TB vTx[4] = `{Tx0, Tx1, Tx2, Tx3}; initial begin foreach(scb[i]) begin scb[i] = new(i); drv[i] = new(scb[i].exp_mbx, i, vRx[i]); mon[i] = new(scb[i].rcv_mbx, i, vTx[i]); end end endprogram

Driver class using virtual interfaces

class Driver; int stream_id; bit done = 0; mailbox exp_mbx; virtual Rx_if.TB Rx; function new(mailbox exp_mbx, int stream_id, virtual Rx_if.TB Rx); this.exp_mbx = exp_mbx; this.stream_id = stream_id; this.Rx = Rx; endfunction endclass : Driver

Multiple Design Configurations

A mesh design example A simple replicated component, an 8-bit counter. This resembles a DUT that has a device such as network chip or processor that is instantiated repeatedly in a mesh configuration. The key idea is that the top-level bench creates an array of interfaces and counters. Now the testbench can connect its array of virtual interfaces to the physical ones.

Interface for 8-bit counter

interface cntr_if (input logic clk); logic [7:0] din, dout; logic reset_l. load; clocking cb @(posedge clk); output din, load; input dout; endclocking always @cb $strobe(@%0d:%m: out=%0d, in=%0d, ld=%0d, r=%0d, $time, dout, din, load, reset_l); modport DUT (input clk, din, reset_l, load, output dout); modport TB (clocking cb, output reset_l); endinterface : cntr_if

Counter model using cntr_if interface

// Simple 8-bit counter with load and active low reset module DUT (cntr_if.DUT cif); logic [7:0] count; assign cif.dout = count; always @(posedge cif.clk or negedge cif.reset_l); begin if (cif.reset_l) count = 0; else if (cif.load) count = cif.din; else count++; end endmodule : DUT

Test top using an array of virtual interfaces

parameter NUM_CI = 2; // Number of design interfaces module top; bit clk; initial forever #20 clk=~clk; // Instantiate N interfaces cntr_if ci [NUM_CI] (clk); // Instantiate the testbench test tb(); // Generate N DUT instances generate for (genvar i=0; i<NUM_CI; i++) begin : dut DUT d (ci[i]); end endgenerate

Testbench using an array of virtual interfaces

program automatic test; virtual cntr_if.TB vci[NUM_CI]; // virtual ifc array Driver driver[]; initial begin // connect local virtual interfaces to top & // create N drivers vci =; driver = new[NUM_CI]; foreach (driver[i]) begin driver[i] = new(vci[i],i); driver[i].reset; end foreach (driver[i]) driver[i].load; end endprogram : test

Driver class using virtual interface

class Driver; virtual cntr_if ci; int id; function new(virtual cntr_if.TB ci, int id); = ci; = id; endfunction task load; fork begin ##1 ci.cb.load <= 1; end join_none endtask endclass : Driver

Day 3 - Summary
fork join, fork_join_none, fork join_any Events, semaphores and mailboxes Inheritance, factory pattern generation and callbacks Interfaces to virtual interfaces


Day 4 - Agenda

Assertions Functional Coverage


An assertion specifies a behavior of the system. Assertions are primarily used to validate the behavior of a design. In addition, assertions can be used to provide functional coverage. Two types of Assertions: Immediate assertions and concurrent assertions. Immediate assertions : Follow simulation event semantics and executed like a statement in a procedural block. Concurrent assertions : Follow clock semantics and use sampled values of variables. (Also called as temporal assertions)

Immediate Assertions

Is a test of an expression performed when the statement is executed in a procedural block. (like an if statement) If expression evaluates to 0, X or Z then it is interpreted as false and assertion is said to fail. Else the expression is interpreted as true and assertion is said to pass.
assert_foo : assert(foo) $display(%m passed); else $display(%m failed);

Immediate Assertion - Example

time t; always @(posedge clk) if (state == REQ) assert (req1 || req2) else begin t = $time; #5 $error("assert failed at time %0t",t); end

Similar to $error, $fatal, $warning and $info are available.

Concurrent Assertions

Concurrent assertions describe behavior that spans over time. Concurrent assertion is only evaluated at clock tick. The values of the variable used in the expression are sampled values. It is important to ensure that the defined clock behavior is glitch-free. Otherwise wrong values can be sampled.
base_rule1 : assert property (cont_prop(rst,in1,in2)) pass_stat else fail_stat;

Delay ##1 indicates that the beginning of the sequence that follows is one clock tick later than the current clock tick. The sequence: req ##1 gnt ##1 !req specifies that req be true on the current clock tick, gnt shall be true on the first subsequent tick, and req shall be false on the next clock tick after that. req ##2 gnt This specifies that req shall be true on the current clock tick, and gnt shall be true on the second subsequent clock tick.

Sequences (cntd.)

req ##[4:32] gnt In the above case, signal req must be true at the current clock tick, and signal gnt must be true at some clock tick between the 4th and the 32nd clock tick after the current clock tick. The time window can extend to a finite, but unbounded, range by using $ as in the example below. req ##[4:$] gnt

Declaration of Sequences
sequence s1; // Simple Sequence a ##1 b ##1 c; endsequence sequence s20_1(data,en); // Sequence with arguments (!frame && (data==data_bus)) ##1 (c_be[0:3] == en); endsequence sequence rule; // Nested sequence @(posedge sysclk) trans ##1 start_trans ##1 s1 ##1 end_trans; endsequence

Declaring Properties
A property defines a behavior of the design. A property can be used for verification as an assumption, a checker, or a coverage specification. In order to use the behavior for verification, an assert, assume or cover statement must be used. A property declaration by itself does not produce any result. A property can be declared in a module an interface a program a clocking block a package a compilation-unit scope

Implication Properties
property data_end; // Overlapped Implication @(posedge mclk) data_phase |-> ((irdy==0) && ($fell(trdy) || $fell(stop))) ; endproperty For Non-overlapped implication, use |=>. Means after one clock tick. property data_end; // Non-Overlapped Implication @(posedge mclk) data_phase |=> ((irdy==0) && ($fell(trdy) || $fell(stop))) ; endproperty

Assert statement
The assert statement is used to enforce a property as a checker. When the property for the assert statement is evaluated to be true, the pass statements of the action block are executed. Otherwise, the fail statements of the action_block are executed. For example, property abc(a,b,c); disable iff (a==2) not @clk (b ##1 c); endproperty env_prop: assert property (abc(rst,in1,in2)) pass_stat else fail_stat; When no action is needed, a null statement (i.e.;) is specified. If no statement is specified for else, then $error is used as the statement when the assertion fails.

Assertions Example 1
module test; bit clk, req, gnt; always #5 clk = ~clk; initial begin repeat (2) @ (posedge clk); req <= 1'b1 ; @ (posedge clk); gnt <= 1'b1 ; @ (posedge clk); 1'b0 ; repeat (2) @ (posedge clk); $finish; end sequence s1; req ##1 gnt ##1 !req; endsequence : s1 property rule_s1 ; @ (posedge clk) s1; endproperty assert property (rule_s1); endmodule

req <=

req-gnt waveform Example 1

0 req











gnt $finish

Assertions Example 1 - Output

"", 18: test.unnamed$$_1: started at 5s failed at 5s Offending 'req' "", 18: test.unnamed$$_1: started at 15s failed at 15s Offending 'req' "", 18: test.unnamed$$_1: started at 45s failed at 45s Offending 'req' $finish at simulation time 55

Assertions Example 2
module test; bit clk, req, gnt; always #5 clk = ~clk; initial begin repeat (2) @ (posedge clk); req <= 1'b1 ; @ (posedge clk); gnt <= 1'b1 ; @ (posedge clk); 1'b0 ; repeat (2) @ (posedge clk); $finish; Overlapping end sequence s1; req ##1 gnt ##1 !req; endsequence : s1 property rule_s1 ; @ (posedge clk) req |-> s1; endproperty assert property (rule_s1); endmodule

req <=

req-gnt waveform Example 2

0 req











gnt $finish

Assertions Example 2 - Output

$finish at simulation time 55

Assertions Example 3
module test; bit clk, req, gnt; always #5 clk = ~clk; initial begin repeat (2) @ (posedge @ (posedge clk); gnt @ (posedge clk); req repeat (2) @ (posedge $finish; end

clk); req <= 1'b1 ; <= 1'b1 ; <= 1'b0 ; gnt <= 1b0; clk);


sequence s1; gnt ##1 !req; endsequence : s1 property rule_s1 ; @ (posedge clk) req |=> s1; endproperty assert property (rule_s1); endmodule

req-gnt waveform Example 3

0 req











gnt $finish
Failed at

Assertions Example 3 - Output

"", 19: test.unnamed$$_1: started at 35s failed at 45s Offending 'gnt' $finish at simulation time 55

Repetition in Sequences
a ##1 b ##1 b ##1 b ##1 c

Using the consecutive repetition operator [*3], which indicates 3 iterations, this sequential behavior is specified more succinctly:
a ##1 b [*3] ##1 c

[*N] is the repetition indicator. This repetition is called consecutive repetition.

b ##1 a[*0:1] ##2 c

is equivalent to
(b ##2 c) or (b ##1 a ##2 c)

Repetition in Sequences (cntd.)

(a ##2 b) [*5]

This is the same as:

(a ##2 b ##1 a ##2 b ##1 a ##2 b ##1 a ##2 b ##1 a ##2 b)

To specify a finite, but unbounded, number of iterations, the dollar sign ($) is used.
a ##1 b [*1:$] ##1 c

matches over an interval of three or more consecutive clock ticks if a is true on the first clock tick, c is true on the last clock tick, and b is true at every clock tick strictly in between the first and the last.

Goto Repetition
Goto repetition specifies finitely many iterative matches of the operand boolean expression, with a delay of one or more clock ticks from one match of the operand to the next successive match and no match of the operand strictly in between. The overall repetition sequence matches at the last iterative match of the operand. Example :
a ##1 b [->2:10] ##1 c

is equivalent to
a ##1 ((!b[*0:$] ##1 b) [*2:10]) ##1 c

Go to Repetition Example

a c

a ##1 b[->3] ##1 c

Non-consecutive repetition
Non-consecutive repetition specifies finitely many iterative matches of the operand boolean expression, with a delay of one or more clock ticks from one match of the operand to the next successive match and no match of the operand strictly in between. The overall repetition sequence matches at or after the last iterative match of the operand, but before any later match of the operand. Example: a ##1 b [=2:10] ##1 c is equivalent to a ##1 ((!b [*0:$] ##1 b) [*2:10]) ##1 !b[*0:$] ##1 c

Nonconsecutive Repetition Example

a c

a ##1 b[=3] ##1 c

Sampled Value functions

Function $sampled returns the sampled value of the expression with respect to the last occurrence of the clocking event A $rose returns true if the least significant bit of the expression changed to 1. Otherwise, it returns false. A $fell returns true if the least significant bit of the expression changed to 0. Otherwise, it returns false. A $stable returns true if the value of the expression did not change. Otherwise, it returns false.

assert property (@(posedge clk) $rose(in) |=> detect);

assert property (@(posedge clk) enable == 0 |=> $stable(data));

Example : $fell (ack, clk); $rose (req); The clocking event if not specified will be inferred from the code where it is used.

Past function
The past values can be accessed with the $past function. $past( expression1 [, number_of_ticks] [, expression2] [, clocking_event]) The following three optional arguments are provided: expression2 is used as a gating expression for the clocking event number_of_ticks specifies the number of clock ticks in the past clocking_event specifies the clocking event for sampling expression1 The clocking event if not specified will be inferred from the code where it is used.

Past function (cntd.)

A $past can be used in any System Verilog expression. An example is shown below. always @(posedge clk) reg1 <= a & $past(b); always @(posedge clk) if (enable) q <= d; always @(posedge clk) assert (done |=> (out == $past(q,2,enable)) ;

and Operation
The two operands of and are sequences. The requirement for the match of the and operation is that both the operands must match. The operand sequences start at the same time. When one of the operand sequences matches, it waits for the other to match. The end time of the composite sequence is the end time of the operand sequence that completes last. Example : When te1 and te2 are sequences, then the composite sequence: te1 and te2

intersect, or Operations
Intersect : Same as and, but the lengths of two matches of the operand sequences must be the same. te1 intersect te2 The operator or is used when at least one of the two operand sequences is expected to match. The two operands of or are sequences. If the operands te1 and te2 are expressions, then te1 or te2 matches at any clock tick on which at least one of te1 and te2 evaluates to true.

first_match Operation
The first_match operator matches only the first of possibly multiple matches for an evaluation attempt of its operand sequence. This allows all subsequent matches to be discarded from consideration. sequence t1; te1 ## [2:5] te2; endsequence sequence ts1; first_match(te1 ## [2:5] te2); endsequence

throughout and within Operation

The composite sequence, exp throughout seq, matches along a finite interval of consecutive clock ticks provided seq matches along the interval and exp evaluates to true at each clock tick of the interval. The composite sequence seq1 within seq2 matches along a finite interval of consecutive clock ticks provided seq2 matches along the interval and seq1 matches along some sub-interval of consecutive clock ticks. That is, the matches of seq1 and seq2 must satisfy the following: The start point of the match of seq1 must be no earlier than the start point of the match of seq2. The end point of the match of seq1 must be no later than the end point of the match of seq2.

Detecting endpoint of a sequence

The end point of a sequence is reached whenever the ending clock tick of a match of the sequence is reached, regardless of the starting clock tick of the match. The reaching of the end point can be tested in any sequence by using the method ended.
sequence e1; @(posedge sysclk) $rose(ready) ##1 proc1 ##1 proc2 ; endsequence sequence rule; @(posedge sysclk) reset ##1 inst ##1 e1.ended ##1 branch_back; endsequence

Manipulating data in a sequence

For example, if in a ##1 b[->1] ##1 c[*2] it is desired to assign x = e at the match of b[->1], the sequence can be rewritten as a ##1 (b[->1], x = e) ##1 c[*2] The local variable can be reassigned later in the sequence, as in a ##1 (b[->1], x = e) ##1 (c[*2], x = x + 1) For every attempt, a new copy of the variable is created for the sequence. The variable value can be tested like any other SystemVerilog variable.

Manipulating data in a sequence (checking)

Variables can be used in sequences and can be checked for. sequence data_check; int x; a ##1 !a, x = data_in ##1 !b[*0:$] ##1 b && (data_out == x); endsequence

Manipulating data in a sequence (arguments) sequence sub_seq2(lv);

a ##1 !a, lv = data_in ##1 !b[*0:$] ##1 b && (data_out == lv); endsequence sequence seq2; int v1; c ##1 sub_seq2(v1) ##1 (do1 == v1); // v1 is now bound to lv endsequence

Declaring Properties - complex

property data_check_p; int x; a ##1 !a, x = data_in |=> !b[*0:$] ##1 b && (data_out == x); endproperty A disable iff clause can be attached to a property_expr to yield a property_spec disable iff (expression_or_dist) property_expr

Assume statement
The purpose of the assume statement is to allow properties to be considered as assumptions for formal analysis tools.

a1:assume property @(posedge clk) req dist {0:=40, 1:=60} ; property proto; @(posedge clk) req |-> req[*1:$] ##0 ack; endproperty

Cover statement

To monitor sequences and other behavioral aspects of the design for coverage, the same syntax is used with the cover statement.
cover property ( sequence_expr ) statement_or_null

Functional Coverage


Code coverage : how many lines of code have been executed. Functional coverage is a measure of which design features have been exercised by the tests. Code coverage : measures the coverage with respect to what has been implemented Function coverage: measures the coverage with respect to what has to be implemented to meet the functionality.

Simple Functional Coverage Example

To measure functional coverage, you begin with verification plan and write an executable version of it for simulation. In your SystemVerilog testbench, sample the values of variables and expressions. These sample locations are known as coverpoints. Multiple coverpoints that are sampled at the same time are placed together in a covergroup.

Simple Functional Coverage Example

program automatic test(busifc.TB ifc); class Transaction ; rand bit [31:0] data; rand bit [ 2:0] port; // Eight port numbers endclass : Transaction covergroup CovPort; coverpoint tr.port; // Measure coverage endgroup Transaction tr = new;

Simple Functional Coverage Example (cntd.)

initial begin CovPort ck = new; // Instantiate group repeat(32) begin // Run few cycles assert(tr.randomize()); // Create trans. ifc.cb.port <= tr.port; // and transmit <=; // onto interface ck.sample(); // Gather coverage @ifc.cb; // wait a cycle end end endprogram

Coverage report for the example

Coverpoint Coverage report CoverageGroup: CovPort Coverpoint: tr.port Summary Coverage: 87.50 Goal: 100 Number of Expected auto-bins: 8 Number of User defined Bins: 0 Number of Automatically Generated Bins: 7 Number of User Defined Transitions: 0

Coverage report for the example (cntd.)

Automatically Generated Bins Bin # hits at least auto[1] 7 1 auto[2] 7 1 auto[3] 1 1 auto[4] 5 1 auto[5] 4 1 auto[6] 2 1 auto[7] 6 1

Anatomy of a Cover Group

A covergroup is similar to a class. Define it once and instantiate it one or more times. It contains coverpoints, options, formal arguments and an optional trigger. A covergroup encampasses one or more data points, all of which are sampled at the same time. A covergroup can be defined in a class or at the program or module level. It can sample any visible variable such as program/module variables, signals from an interface, or any signal in the design.

Covergroup in a class
class Transactor; Transaction tr; mailbox mbx_in; covergroup CovPort; coverpoint tr.port; endgroup function new(mailbox mbx_in); CovPort = new; // Instantiate covergroup this.mbx_in = mbx_in; endfunction

Covergroup in a class (cntd.)

task main; forever begin tr = mbx_in.get; // Get the next trans. ifc.cb.port <= tr.port; // Send into DUT <=; CovPort.sample(); //Gather coverage end endtask : main endclass : Transactor

Triggering a Cover Group

// Covergroup with an event trigger event trans_ready; covergroup CovPort @(trans_ready); coverpoint ifc.cb.port; // Measure coverage Endgroup

Covergroups can be triggered by calling sample method from Callback functions. Covergroups can also be triggered on a SystemVerilog assertion.

Controlling the number of bins

SystemVerilog creates a number of bins to record how many times each value has been seen. These bins are basic unit of measurement for functional coverage.
// Limiting the number of automatic bins created covergroup CovPort; coverpoint tr.port {options.auto_bin_max = 2;} // divide into 2 bins endgroup // Using auto_bin_max for all cover points covergroup CovPort; options.auto_bin_max = 2; // Affects port & data coverpoint tr.port; coverpoint; endgroup

User defined bins

class Transaction; rand bit [2:0] hdr_len; // range: 0:7 rand bit [3:0] payload_len; // range: 0:15 endclass Transaction tr; covergroup CovLen; len: coverpoint (tr.hdr_len + tr.payload_len + 5b0) {bins len[] = {[0:22]};} endgroup

Naming the coverpoint bins

kind is a 4-bit variable that has 16 possible values.

// Specifying the bin names covergroup CovKind; coverpoint tr.kind { bins zero = {0}; bins lo = {[1:3]}; bins hi[] = {[8:$]}; bins misc = default; } endgroup

// // // //

A 1 8 1

bin for kind==0 bin for values 1:3 separate bins bin for all the rest

Report showing the bin names

Bin # hits at least =============================================== hi_8 0 1 hi_9 5 1 hi_a 3 1 hi_b 4 1 hi_c 2 1 hi_d 2 1 hi_e 9 1 hi_f 4 1 lo 16 1 misc 15 1 zero 1 1 ===============================================

Conditional coverage
// Disable during reset covergroup CovPort; // Dont gather coverage when reset == 1 coverpoint port iff (bus_if.reset); endgroup // Using start and stop functions initial begin CovPort ck = new; // Instantiate covergroup // Reset sequence stops collection of coverage data #1ns bus_if.reset = 1; ck.stop(); #100ns bus_if.reset = 0; ck.start(); end

Transition coverage
For example you can check if port ever went from 0 to 1,2 or 3.
covergroup CovPort; coverpoint port { bins t1 = (0 => 1), (0 => 2), (0 => 3); } endgroup

(0 => 1[*3] => 2) is equivalent to (0 => 1 => 1 => 1 => 2).

Ignoring bins
Use ignore_bins to tell which values to exclude from functional coverage calculation. bit [2:0] low_ports_0_5; // Only uses values 0-5 covergroup CovPort; coverpoint low_ports_0_5 { ignore_bins hi = {[6,7]}; //Ignore upper 2bins } endgroup

Cross Coverage

Example : hburst and hsize combinations Cross coverage measures what values were seen for two or more coverpoints at the same time.
covergroup CovPortKind; kind: coverpoint tr.kind; kind port: coverpoint tr.port; cross kind, port; endgroup // Create coverpoint // Create coverpoint port // Cross kind and port

Excluding cross coverage bins

covergroup CovPortKind; port: coverpoint tr.port {bins port[] = {[0:$]};} kind: coverpoint tr.kind {bins zero = {0}; bins lo = {[1:3]}; bins hi[] = {[8:$]}; bins misc = default;} cross kind, port { ignore_bins hi = binsof(port) intersect {7}; ignore_bins md = binsof(port) intersect {0} && binsof(kind) intersect {[9:10]}; ignore_bins lo = binsof(kind.lo); } endgroup

Day 4 - Summary
Immediate assertions, concurrent assertions Sequences, declaration of sequences Repetition specification (consecutive, goto and non-consecutive) Sampled value functions ($sampled, $rose, $fell, $stable, $past) and, intersect, or, first_match, throughout, within and ended operations Assigning variables in sequence specification and checking Property declaration : syntax, Implication properties assert, assume and cover statements Functional coverage example coverpoint, covergroup, triggering the covergroup Controlling the number of bins, naming the bins Conditional coverage, transition coverage Ignore_bins and cross coverage