Sei sulla pagina 1di 285

System Verilog -Verification

-----System Verilog and Test Bench Environment


Course Objectives
 Learn how to build the class based verification environment using System Verilog TB
features.
 Learn how to generate the functional coverage.
Learn how to build the regression test suit

Couse Agenda:
 Introduction  Threads
 Data Types  Mailbox, Semaphore
 Struct and Enums  Events
 Memories (Arrays)
 Tasks and Functions  Build env components Generator
 Interfaces Driver(BFM)
 Clocking Block Monitor
 System Verilog Statified event queue Reference Model
 OOPs Basics Scoreboard
 Advanced OOP Environment
 TB Architecture Package
 Reusable TB Environment Basic Test case
 Virtual Methods Top level module
 Randomization  Functional coverage
 Constraints  Scoreboard and Coverage model
Regression Test case

2
Introduction:

What is System Verilog:??


 System Verilog is a hardware description and Verification language(HDVL)
 System Verilog is an extensive set of enhancements to IEEE 1364 Verilog-2001
standards

 It has features inherited from Verilog HDL,VHDL,C,C++.


 Adds extended features to verilog.

Verilog Vs System Verilog


 Verilog is weakly typed and more concise with efficient notation. It is
deterministic. All data types are predefined in Verilog and each has a bit-
level representation. Syntax is C-like.

3
 System Verilog includes a set of extensions to the Verilog HDL to help
engineers design and verify larger and more complex designs. In fact, many
industry watchers consider it the first Hardware Description and Verification
Language (HDVL), because it combines VHDL and Verilog features with
those of Hardware Verification Languages (HVLs) Vera and e, as well as C
and C++.
 HDL vs HVL
 Static consumes memory
 New Data types – String, 2-state Data types, logic, integer,
ultidimensional Arrays, Enumeration, Structures and Unions,
 Interfaces
 OOPs and Randomization
 Assertions, Coverage, Mailboxes, Semaphores
 Constrained Random Verification Environment
 A set of extensions to Verilog-2001.
 Randomization – constraints
 Functional Coverage
 Reusability – Reuse without breaking the already working code.
 A language which supports Object Oriented Programming. C & C++ - Data types
and OOP.
 PSL – Assertion Based Verification
 VHDL – enum, Packages
Importance of Verification:
◦ A primary purpose for verification is to detect failures
so that bugs can be identified and corrected before it gets shipped to costumer.
 Hardware Designs get more and more complex
 Hand-written stimulus (directed test) is difficult to write and maintain
 Corner cases are difficult to catch
 Visual inspection of waveforms in order to trace a bug is a tedious task
 High cost of faulty designs (loss of life, product recall)
 “The amount of time spent on verification now exceeds the
amount of time spent on design, comprising up to 70 percent of the
total development effort.”
High cost of design debug

4
 Designers (sometimes verification engineers = 2 * design engineers)
 Time-to-market

5
System Verilog VHDL:
 A set of extensions to Veriolg-2001
 Randomization – constraints
 Functional Coverage
 Reusability – Reuse without breaking the already working code.
 A language which supports Object Oriented Programming.
Language Evolution:
 C & C++ - Data types and OOP
 PSL – Assertion Based Verification
 VHDL – enum, packages
 2005- Synopsys donated Openvera Language(Verification
Functionality)
 VERA – Randomization and Constraints
 2002 – SV created from Superlog Language(DPI)
 2009- adopted as IEEE 1800-2009 standard by merging with base Verilog
(IEEE 1364-2005)
 2012 – IEEE 1800-2012 Standard Revision (current version)

 Data Types:
In this module, you will:
 Review Verilog 2001 data types
Learn how to use System Verilog data types and packages
See and discuss examples of the data types

Data Types in Verilog:2001


 Four values 0, 1, x and z
 Two main groups of data types – nets and registers
 Register data types – reg, integer, time and real
 Store a value from one assignment to the next
 Are assigned a value in a procedural block
(initial/always)

reg [15:0] databus; //16-bit 4-state unsigned reg type


integer count1, count2; //32-bit 4-state signed type
real real_var ; // real number

Verilog 2001 Data Type Rules:


 In Verilog 2001 – there are strict rules on where variable data types (reg, integer) and
net data types (wire) can be used:
◦ Registers are assigned values in procedural blocks
◦ Nets are assigned (“driven”) by continuous assignments, modules or
primitive instances
 Module inputs are always nets
 Module outputs are either nets or registers depending on the rules above.
 Instance inputs(ports on instances) are either nets or registers, depending on the
rules above.
 Instance ouputs (output ports on instances) are always nets.
 Bidirectional inout ports are always nets.
Data Types in System Verilog:
 System Verilog offers many  longint : 64-bit signed integer.
improved data structures  byte : 8-bit signed integer,
compared with Verilog. Some of can be used for storing
these were created for ASCII charater.
designers but are also useful  bit : User defined vector types.
for testbenches.  bit b;
 Two-state: better // 2-state unsigned values 0,1
performance, reduced
 int unsigned a ;//2-state 32-bit
memory usage „ unsigned integer
 Queues, dynamic and associative
arrays and automatic storage: reduced  int i; // 2-state, 32-bit signed integer
memory usage, built-in support for
searching and sorting
 byte b8; // 2-state, 8-bit signed integer
 Classes and structures:
support for abstract data
structures  shortint s; // 2-state, 16-bit signed integer
 Strings: built-in string support
 Enumerated types:  longint l; // 2-state, 64-bit signed integer
code is easier to write
and understand 4- State Value Integer Data Types
 logic : User defined vector types.
2- State value Integer Data Types:  reg : User defined vector types.
 wire : User defined vector types.
 shortint : 16-bit signed integer.  integer : 32-bit signed integer.
 int : 32-bit signed integer.
 time : 64-bit unsigned integer. String 2 s2 = Hi, Hellow World
 Logic [15:0] data; String 3 s3 = 128 bit b = 128
 Reg [15:0] databus; //16-bit 4-
state unsigned reg type  Syntax: string name;

Void Data Types:  Variable length – grows automatically


 The void data type represents
nonexistent data. This type can  Memory is dynamically allocated
be specified as the return type
of functions to indicate no
return value.  { } – used for concatenation

 void = function_call();

String Data Types:  Routines for String:


String Data type is a variable size, It is
dynamically allocated array of bytes.
toupper(),
{} is used for concatenation tolower(),
String s1=”Hello World” putc(),
String s2={“hi”,”s”,} getc(),
Bit b=128 substr()
string s3=b
Example:
module string_datatype;
//declaration
string s1 = "Hellow World";
string s2 = {"Hi,"," ",s1};
int b = 128;
string s3 = b; // sets 128 to s3
initial begin
//display values
$display("String 1 s1 = %0s",s1);
$display("String 2 s2 = %0s",s2);
$display("String 3 s3 = %0d bit b =
%0d",s3,b);
end
endmodule
Simulator Output
String 1 s1 = Hellow World
Ex:
string s;
initial begin
s = "IEEE ";
$display(s.getc(0)); // Display: IEEE
$display(s.tolower()); // Display: ieee
s.putc(s.len()-1, "-"); // change IEEE ->IEEE-
s = {s, "P1800"}; // "IEEE-P1800"
$display(s.substr(2, 5)); // Display: EE-P
Create temporary string, note format my_log_rtn($psprintf("%s %5d", s, 42));
end
task my_log(string message); // Print a message to a
log $display("@%0t: %s", $time, message); endtask

User Defined Data types:


 typedef
 Creates synonyms for users own type
 Helps configurable variable types – results code reuse
Ex: typedef bit [31:0] uint; typedef bit [5:0] bsix_t; // Define new type
bsix_t my_var ; // Create 6-bit variable
 User types can be dynamically converted to other types
Ex: typedef struct {bit [7:0] r, g, b;} pixel_s; pixel_s my_pixel;
 Converting one data type to the another data type.
 Conversion can be done in two ways
1) static casting :
-> (‘) is required for static casting.
 Ex: int i;
real r;
i = int '(10.0 - 0.1); // cast is optional r =
real'(42); // cast is optional
Disadv: If given value is out of bound value then error will not give.
2) Dynamic casting: using $cast oSyntax : $cast( singular dest_var,
singular source_exp ); oThe dynamic cast, $cast,
allows you to check for out-ofbounds values
 System Verilog allows user to define data

types. There are different ways to define user defined data types. They
are :
 Class
 Enumerations
 Struct
 Union
 Typedef
Enumeration/Enum Data Type

 An enumerated type defines a set of named values.


The simplest enumerated type declaration contains a list
of constant names and one or more variables.
 Syntax: enum [data_type] {named constants} enumVar;  Data type defaults to int.
 Variable initialized to ’0 if initial_value is not specified.
 Enum variables can be displayed as ASCII with name() function.
 Ex: // Create data type for values 0, 1, 2
typedef enum {INIT, DECODE, IDLE}
fsmstate_e; fsmstate_e pstate, nstate; //
declare typed variables initial begin case
(pstate)
IDLE : nstate = INIT; // data assignment
INIT : nstate =
DECODE; default
: nstate = IDLE;
endcase
$display("Next state is %s",
nstate.name()); // Display symbolic state name
 Defining Enumerated Values
 typedef enum {red = 2, green = 5, blue}color;

Enumerated types methods:

 first() -- > returns the value of the first member of the enumeration.

 last() -- > returns the value of the last member of the enumeration.

 next() -- > returns the value of next member of the enumeration.

 next(N) -- > returns the value of next Nth member of the enumeration.

 prev() -- > returns the value of previous member of the enumeration.

 prev(N) -- > returns the value of previous Nth member of the enumeration.
 num() -- > returns the number of elements in the given enumeration.
 name() -- > returns the string representation of the given enumeration value.
 In the following example, colors is defined to be variable of the unnamed
enumerated int type that includes

the members red, green, blue, yellow, white, black. enum { red, green,
blue, yellow, white, black } Colors;

Example:

module enum_datatype;
//declaration
enum { red, green, blue, yellow, white, black }
Colors; //display members of Colors
initial begin
Colors = Colors.first;
for(int i=0;i<6;i++) begin
$display("Colors :: Value of %0s \t is = %0d",Colors.name,Colors);
Colors = Colors.next;
end
end
endmodule
Simulator Output
Colors :: Value of red is = 0
Colors :: Value of green is = 1
Colors :: Value of blue is = 2

Colors :: Value of yellow is = 3


Colors :: Value of white is = 4
Colors :: Value of black is = 5
Example:

module enum_datatype;
//declaration
typedef enum { red=0, green, blue=4, yellow, white=10, black } colors;
enum { a, b, c, d, e, f, g } alphabets;
colors first_set;
colors second_set;
initial begin
first_set = first_set.first();
$display("first_set first color is \t %0s, \t Value = %0d", first_set.name(),first_set);
first_set = first_set.last();
$display("first_set last color is \t %0s, \t Value = %0d", first_set.name(),first_set);
second_set = first_set;
$display("second_set color is \t %0s, \t
Value = %0d",
second_set.name(),second_set);
second_set =second_set.prev(2);

$display("second_set color is \t %0s, \t


Value = %0d",
second_set.name(),second_set);
second_set =second_set.next(2);
$display("second_set color is \t %0s, \t Value =
%0d", second_set.name(),second_set);
$display("Number of members in alphabets is \t %0d",alphabets.num());
$display("Default First members in alphabets is \t %0s , \t value is
%0d",alphabets.name(),alphabets);
alphabets=alphabets.next;
$display("Next members in alphabets is \t %0s , \t value is
%0d",alphabets.name(),alphabets);
alphabets=alphabets.last;
$display("Last members in alphabets is \t %0s , \t value is
%0d",alphabets.name(),alphabets);
alphabets=alphabets.prev(3);
$display("3rd members from last in alphabets is \t %0s , \t value is
%0d",alphabets.name(),alphabets);
end
endmodule
Simulator Output
first_set first color is red, Value = 0 first_set last
color is black, Value = 11 second_set color is
black, Value = 11 second_set color is yellow, Value
= 5 second_set color is black, Value = 11 Number
of members in alphabets is 7
Default First members in alphabets is a and value is 0
Next members in alphabets is b , value is 1
Last members in alphabets is g , value is 6
3rd members from last in alphabets is d , value is 3
Struct Data Type:
 Struct - collection of different data types as members with separate memory.
 Structure is a method of packing data of different types.
Ex :
struct {int a;
byte b;
bit [7:0] c;
} my_data_struct;
Example:
`timescale 1ns/10ps
/ Type define a struct, and put it outside module
/ So that other modules can also use it
typedef struct { byte a;
reg b;
shortint unsigned c; } myStruct;
module struct_data ();
// Define a local struct.
struct { byte a; reg b; shortint unsigned c; }
myLocalStruct = '{11,1,101};
/ When defined typedef, we can use as new
data type myStruct object = '{10,0,100};
initial begin $display ("a = %b b = %b c = %h", object.a, object.b, object.c);
$display ("a = %b b = %b c = %h", myLocalStruct.a, myLocalStruct.b, myLocalStruct.c);
#1
$finish;
end
emd module
Simulator Output
a = 00001010
b=0
c = 0064
a = 00001011
b=1
c = 0065

Type Def types:


 A typedef declaration lets you define your own identifiers that can be used in
place of type specifiers such as int, byte, real.
 Let us see an example of creating data type "nibble".
typedef bit[3:0] nibble; // Defining nibble data type.
nibble a, b; // a and b are variables with nibble data types.
Class:
 A Class is a collection of data and a set of subroutines that operate on that data.
The data in a class are referred to as class properties, and its subroutines are called
methods. A Class is declared using the class...endclass keywords.
Example:
class packet;
// Properties
bit [31:0] address;
bit [31:0] data ;
$display("Inside new Function of
packet"); endfunction
endclass : packet
Packages:
Package provide ways to have common code
to be shared across multiple modules
 Packages can not contain any assign statement.

 Variable declaration assignments within the package shall occur before


any initial , always, always_comb, always_latch, or always_ff blocks
 Assign statement on any net type is not allowed
Example:
package msgPkg;
bit terminate_on_error = 0;
string msgName = "NULL";
task initMsgPkg (string mName, bit term);
terminate_on_error = term;
msgName = mName;

endtask
endpackage
----------------------------
include "msgPkg.sv“
import msgPkg::*;
module simple_package();
initial begin
msgPkg::initMsgPkg("PACKAGES",0);
end
endmodule

Memories (Arrays)
Arrays:
A collection of similar data items that can be
selected by indices computed at run-time.
Various usage scenarios with respect to
initializing, accessing the members and usage of physical memory.
Verilog Array:
 Verilog arrays support single dimension, multi-dimension arrays
-----------------------------

Examples:
reg [7:0] address; --- one dimension array containing one element
reg [7:0] memory [0:255]; -- One dimension array containing 256 elements
reg [15:0] array [0:255] [0:255]; -- Two dimensional array with 256
elements in two directions and width 16.

SV Packed Array:
 Packed Array Represents a Contiguous set of bits.
 It is treated as a Single Vector.
 It allows only bit, logic and reg type declaration.
 It does not allow Integer, Int, Byte type ofdeclaration.
Examples:
logic [2:0] pack_array ;
logic [1:0][2:3] pack_array_2d ;
Logic [3:0] [7:0] data_reg ;

Dimension of Packed &Un packed Arrays.:


 4D Unpacked Array
logic v3 [2:1] [2:0] [1:0] [4:1];
 3D Unpacked Array of 1D Packed Arrays
logic [4:1] v3 [2:1] [2:0] [1:0] ;
 2D Unpacked Array of 2D Packed
Array logic [1:0] [4:1] v3 [2:1] [2:0];
 1D Unpacked Array of 3D Packed Arrays
logic [2:0] [1:0] [4:1] v3 [2:1];
 4D Packed Array
logic [2:1] [2:0] [1:0] [4:1] v3;
Array Examples:
module array_demo();
bit [1:0][7:0] array [0:1][0:1];
Initial begin
array = '{'{{8'h22,8'h11}, {8'h44,8'h33}}, '{{8'h66,8'h55},
{8'h88,8'h77}}};
$display (" ");

$display ("array[0][0] = %h",array[0][0]);

$display ("array[0][1] = %h",array[0][1]);

$display ("array[1][0] = %h",array[1][0]);

$display ("array[1][1] = %h",array[1][1]);


$display ("array[0][0][0] = %h",array[0][0][0]); // Location : 000
$display ("array[0][0][1] = %h",array[0][0][1]); // Location : 001
$display ("array[0][1][0] = %h",array[0][1][0]); // Location : 010
$display ("array[0][1][1] = %h",array[0][1][1]); // Location : 011
$display ("array[1][0][0] = %h",array[1][0][0]); // Location : 100
$display ("array[1][0][1] = %h",array[1][0][1]); // Location : 101
$display ("array[1][1][0] = %h",array[1][1][0]); // Location : 110
$display ("array[1][1][1] = %h",array[1][1][1]); // Location : 111

$display ("array[0][0][0][0] = %h",array[0][0]


[0][0]);
$display ("array[0][0][1][1] = %h",array[0][0]
[1][1]); $display (" ");
end
endmodule
array[0][0] = 2211

array[0][1] = 4433

array[1][0] = 6655

array[1][1] = 8877
array[0][0][0] = 11

array[0][0][1] = 22

array[0][1][0] = 33

array[0][1][1] = 44

array[1][0][0] = 55

array[1][0][1] = 66

array[1][1][0] = 77

array[1][1][1] = 88

array[0][0][0][0] = 1
array[0][0][1][1] = 1

Types of Arrays:
 Hold a fixed number of equally-sized data elements
 Individual elements are accessed by index
 Types :
 Static Array
 Fixed size
 Dynamic array
 Size is given in run time
 Associative Array
 Sparse Matrix Array
Fixed Size Array:
◦ Size should be given during compilation time.

◦ Time require to access any element is less.

◦ if not all elements used by the application, then memory is wasted.

◦ Not good for sparse memory or when the size changes.

◦ Good for contagious data.

◦ Types:
Packed,
Unpacked and
Multidimentional

Ex: int lo_hi[0:15]; // 16 ints [0]..[15]

int c_style[16]; // 16 ints [0]..[15]

Dynamic Array:
 Dynamic Array is 1 – dimensional Unpacked Array
 Use a dynamic array when the array size must change during the
simulation.
Example :
Data_type array_name [] ;
bit [3:0] nibble[]; // Dynamic array of 4-bit vectors integer mem[];
// Dynamic array of integers
Dynamic Array Methods:
new() allocates the storage and initializes the newly allocated

array elements either to their default value or to the value

provided by the optional argument

size() Returns the current size of a dynamic array

delete() De-allocates the memory of the array

Dynamic Array Examples:


module dynamic_array();
bit [7:0] dyn_arr1[] ;
integer dyn_arr2[] ;
int dyn_arr3[] = new[10] ;
bit [7:0] dyn_arr4[] ;
int dyn_arr5[] ;
parameter arrlen = 20 ;
initial
begin
dyn_arr1 = new[100] ;
dyn_arr2 = new[dyn_arr1.size() + 10] ;
$display("Size of DYNAMIC ARRAY 1 = %d",
dyn_arr1.size() ); $display("Size of DYNAMIC
ARRAY 2 = %d", dyn_arr2.size() );
$display("Size of DYNAMIC ARRAY 3 = %d",
dyn_arr3.size() );
dyn_arr3.delete;
$display("Size of DYN ARR 3 after deletion = %d", dyn_arr3.size() );
dyn_arr4 = new[dyn_arr1.size() * 4] ;
$display("Size of DYNAMIC ARRAY 4 = %d",
dyn_arr4.size() );
dyn_arr5 = new[arrlen] ;
$display("Size of DYNAMIC ARRAY 5 = %d", dyn_arr5.size() );
end

endmodule
Results

Size of DYNAMIC ARRAY 1 = 100


Size of DYNAMIC ARRAY 2 = 110
Size of DYNAMIC ARRAY 3 = 10
Size of DYN ARR 3 after deletion = 0
Size of DYNAMIC ARRAY 4 = 400
Size of DYNAMIC ARRAY 5 = 20

Associative Array:
Use associative arrays when the data space is unbounded or sparsely
populated.
Declare the array by specifying a type instead of a size for its one dimension.
Accessed with integer, or string index
Array elements do not exist until you assign to the
Array elements are “pairs” of associated key and data values.

Example :

bit [3:0] aa1[int] ; // Associative array of 4-bit 2-state index type int.

logic [7:0] aa2 [integer] ; // Associative array of 8-bit logic index type integer.

integer i_array [*] ; /// Associative array of integer (wildcard index type).
num() Returns the number of entries in an associative array

delete() Removes a single entry if an index is specified


removes all entries if no index specified

exists() Returns 1 if an element exists at the indexed


location , else returns 0

first() Assigns the value of the first (smallest ) index in the


array to ref_index , Returns 0 if array empty, otherwise 1

last() Assigns the value of the last ( largest ) index of the


array to ref_index

next() Assigns the next value to ref_index

prev() Assigns the previous value to ref_index


 num()-Returns number of allocated elements; exists()- to check specific
index is available
 Out-of-bounds read returns ’0 for 2state, ’x for
4state arrays
Standard Array Associative Array

Example :
module integer_associative_array ();
integer as_mem [integer];
integer i;
initial begin
/ A
d
d
e
l
e
m
e
n
t
a
r
r
a
y
a
s
_
m
e
m
[
1
0
0
]
=
1
0
1
;
$di
sp
la
y
("
va
lu
e
st
or
ed
in
1
0
0
is
%
d"
,
1
0
1)
;
as
_
m
e
m
[1
]
=
1
0
0;
$di
sp
la
y
("
va
lu
e
st
or
ed
in
1
is
%
d"
,
1
0
0)
;
as
_
m
e
m
[5
0]
=
9
9;
$
d
i
s
p
l
a
y
(
"
v
a
l
u
e
s
t
o
r
e
d
i
n
5
0
i
s
%
d
"
,
9
9
)
;
a
s
_
m
e
m
[
2
5
6
]
=
7
7
;
$display ("value stored in 256 is %d", 77);
/ Print the size of array
/ $display ("size of array is %d", as_mem.num());

// Check if index 100 exists


$display ("index 100 exists %d", as_mem.exists(100));
/ V
a
l
u
e

s
t
o
r
e
d
i
n

f
i
r
s
t

i
n
d
e
x

i
f

(
a
s
_
m
e
m
.
f
i
r
s
t
(
i
)
)
begin
$display
("value at
first index
%d value
%d", i,
as_mem[i
]); end

/ Value stored in last index


if (as_mem.last(i))
begin
$display ("value at last index %d value %d", i, as_mem[i]);
end
/ D
e
l
e
t
e

t
h
e

f
i
r
s
t

i
n
d
e
x

a
s
_
m
e
m
.
d
e
l
e
t
e
(
1
0
0
)
;

d
i
s
p
l
a
y

(
"
D
e
l
e
t
e
d

i
n
d
e
x

1
0
0
"
)
;
V
a
l
u
e

s
t
o
r
e
d

i
n

f
i
r
s
t

i
n
d
e
x

if (as_mem.first(i))
begin
$display ("value at first index %d value %d", i, as_mem[i]);
end
#1 $finish;
end
endmodule
Results:
value stored in 100 is 101
value stored in 1 is 100
value stored in 50 is 99
value stored in 256 is 77
size of array is 4
index 100 exists 1
value at first index 1 value 100
value at last index 256 value 77
Deleted index 100
value at first index 1 value 100

Queues:
Use a Queue “array” where insertion and extraction order
are important.
Declare a Queue by using $ as the size of its onedimension:
Example :
int q_int [ $ ];
int q_int [ $: 200 ]; // Optionally limit the queue size.

 Use $ to reference the end of the Queue :


Example :
myint = q_int [ $ ]

integer q_integer [ $ ] ; // Queue of integers.

logic [ 15:0 ] q_logic [ $ ] ; // Queue of 16-bit logic.

// Queue of int – max


int q_int [ $ : 2000 ] ; size of 2000.

// Queue of time – max


time q_time [ $ : 10 ] ; size of 10.

Queues Access Methods:


size() Returns the size of the Queue.
insert() Inserts an item at the index location.
delete() Removes item at the specified index.
pop_front() Removes an item in the front of the
Queue (0).
pop_back() Removes an item in the front of the
Queue ($).
push_front() Places an item at the front of the Queue
(0).
push_back() Places an item at the front of the Queue
($+1).
Example:

module queue_data();
/
Q
u
e
u
e
i
s
d
e
c
l
a
t
e
d
w
it
h
$
i
n
a
r
r
a
y
s
i
z
e
i
n
t
e
g
e
r
q
u
e
u
e
[
$
]
=

{
0
,
1
,
2
,
3
,
4
}
;
i
n
t
e
g
e
r
i
;

initial begin
$
d
i
s
p
l
a
y
(
"
I
n
i
t
i
a
l
v
a
l
u
e
o
f
q
u
e
u
e
"
)
;
p
r
i
n
t
_
q
u
e
u
e
;
/ I
n
s
e
r
t

n
e
w

e
l
e
m
e
n
t

a
t

b
e
g
i
n

o
f

q
u
e
u
e

q
u
e
u
e

{
5
,

q
u
e
u
e
}
;
/
$display
("new
element
added
using
concate"
);
print_qu
eue;
/
I
n
s
e
r
t
u
s
i
n
g
m
e
t
h
o
d
a
t
b
e
g
i
n
i
n
g
q
u
e
u
e
.
p
u
s
h
_
f
r
o
n
t
(
6
)
;
$display
("new
element
added using
push_front")
;
print_queue
;
/ Insert using method at end

queue.push_back(7);
$display ("new element added using push_back");
print_queue;
/ Using insert to insert, here 4 is index

/ and 8 is value

queue.insert(4,8);
$display ("new element added using insert(index,value)");
print_queue;
// get first queue element method at beginning
i = queue.pop_front();
$display ("element poped using pop_front"); print_queue;
/ get last queue
element method at
end i =
queue.pop_back()
;

$display ("element
poped using
pop_end");
print_queue;
/ Use delete method to delete element at index 4 in queue queue.delete(4);
/ $display ("deleted element at index 4");

p
r
i
n
t
_
q
u
e
u
e
;
#
1
$
f
i
n
i
s
h
;
E
n
d
t
a
s
k
p
r
i
n
t
_
q
u
e
u
e
;
i
n
t
e
g
e
r
i
;
$
w
r
i
t
e
(
"
Q
u
e
u
e
c
o
n
t
a
i
n
s
"
)
;
f
o
r
(
i
=
0
;
i
<
q
u
e
u
e
.
s
i
z
e
(
)
;
i
+
+
)
b
e
g
i
n
$
w
r
i
t
e
(
"
%
g
"
,
q
u
e
u
e
[
i
]
)
;
e
n
d
$write("\n"); endtask endmodule
Results:
Initial value of queue
Queue contains 0 1 2 3 4
new element added using concate
Queue contains 5 0 1 2 3 4
new element added
using push_front
Queue contains 6 5 0
1234
new element added
using push_back
Queue contains 6 5
012347
new element added using insert(index,value)
Queue contains 6 5 0 1 8 2 3 4 7 element poped using pop_front
Queue contains 5 0 1 8 2 3 4 7 element poped using pop_end
Queue contains 5 0 1 8 2 3 4 deleted element at index 4
Queue contains 5 0 1 8 3 4
Queue Indexing:
Example :
module another_array();
int q_int[$] ; // Queue declaration syntax
int data ;
initial
begin
q_int = {0, q_int} ;// { 0 }
q_int = {q_int, 1} ; // { 0, 1 }
q_int = {2, q_int} ; // { 2, 0, 1 }

q_int = {q_int[0], 3, q_int[1:$]} ; // { 2, 3, 0, 1 }


q_int = {q_int[0:2], 4, q_int[3]} ; // { 2, 3, 0, 4, 1 }
q_int = {q_int[0:1], q_int[3:4]} ; // { 2, 3, 4, 1 }
q_int = {q_int[0:1], 5 , q_int[2:3]} ; // { 2, 3, 5, 4, 1 }
foreach(q_int[i]) begin
data = q_int[i] ;
$display("Value of Daa = %d", data) ;
end
$display("------------------") ;

q_int = q_int[0:$-1] ;
$display("------------------") ;
foreach(q_int[i])
begin
$display("Value of Q_INT = %d", q_int[i]) ;
end
end
Endmodule
Tasks and Functions
Agenda:
 Understanding of Tasks & Functions
 Differences between tasks and functions
 Features of System Verilog tasks and functions
 Conclusion
List features of Functions in Verilog:
 Functions will have only 1 return value
 Functions cant call tasks
 Functions are zero time.\Functions need begin..end to define the functionality in it
 Applications
 Computations calculations
List Features of Tasks in Verilog:
 Tasks can have many variables.
 Tasks can call tasks or functions.
 Tasks can be either zero simulation time or can have wait statements.
 Applications
 BFM creation
Verilog Tasks and Functions:

Enhancements in Sv:
 System Verilog adds the following enhancements to verilog 2001 tasks and
funtions :
 Multiple statements in task/function don’t require begin/end block.
 Function output and inout ports.
 Functions can return a “void” type.
 Returning from a task/function before reaching the end
(return).
 Passing arguments by reference (ref) and by value.
 Task/Function arguments passed by name instead of order.
 Default task and function arguments values are allowed
 Multiple statements in a task or function without requiring a begin...end or
fork...join block.
 Function is having output and inout ports along with input ports.
 Returning from a task or function before reaching the end of the task or function.

 Function can return a “Void” type.


 Passing arguments by reference (ref) and by value.
 Binding argument values by name instead of by position.
 Default task and function argument values are allowed.
 //return statement in task task load_array(int len, ref int array[]); if (len <= 0)
begin $display("Bad len");

return;
end
/ Code for the rest of the task
...
endtask
//return
statement in
function
function bit
transmit(...);
// Send transaction
System Verilog Functions:
Functions will return many variables
Functions can call tasks by using fork..join_none
Functions are zero time.simulation
Functions support call by value and call by reference.

Functions support input, output and inout ports.

Functions support default values for input variables.

No need of begin end


All input ports can have default values specified.
endfunction :function_name

Different Styles in Functions:


function int fun( int j = 1, string s = "no" );...
endfunction
The fun function can be called as follows:
fun( .j(2), .s("yes") ); // fun( 2,
"yes" ); fun( .s("yes") ); // fun( 1,
"yes" );
fun( , "yes" ); // fun( 1, "yes" );
fun( .j(2) ); // fun( 2, "no" );
fun( .s("yes"), .j(2) ); // fun( 2 , "yes"
); fun( .s(), .j() ); // fun( 1 , "no" );
fun( 2 ); / fun( 2, "no" );
fun( ); // fun( 1, "no" );
fun( .s("yes"), 2 ); // illegal
System Verilog Tasks:
Tasks will return many variables
Tasks can call another tasks
Tasks support call by value and call by reference.

Tasks support input, output and inoutports.

Tasks support default values for input variables.


No need of begin end

All input ports can have default values specified.

endtask :task_name

task mytask1 (output int x, input logic y);


...endtask
task mytask2;
output x;
input y;
int x;
logic y;
...
endtask
Verilog-2001 allows tasks to be declared as automatic, so that all
formal arguments and local variables are stored on the stack

Discarding Function Return Values:


void’(some_function());

When the formal argument is declared as a “const”


ref, the subroutine cannot alter the variable, and an
attempt to do so shall generate a compiler error.

input // copy value in at beginning


output // copy value out at end
inout // copy in at beginning and out at end
ref // pass reference

Different Function Calls


Pass By Value
function int crc( byte packet [1000:1] );
for( int j= 1; j <= 1000; j++ ) begin
crc ^= packet[j];
end
enfunction
Pass By Reference
function int crc( ref byte packet [1000:1] );
for( int j= 1; j <= 1000; j++ ) begin
crc ^= packet[j];
end
endfunction
Conclusion:

SystemVerilog functions and task have one and only Difference


Functions are zero time.Tasks have notion of time
Interfaces:
Topics Covered:
 What is an Interface?
Verilog
Interface Signals
System Verilog
Interfaces
 Interface Declaration
 Interface References
 Virtual Interface
 Advantages of Interface
Concept of Modports

Interfaces Definition:
What is an Interface?
 Named bundle of nets and variables
 Enables communication between blocks
 Signals can be referenced by interface
 Encapsulates functionality as well as connectivity
Enables high level of re-usability
 Interfaces is the window between the DUT & BFM

 Can be instantiated in parallel to DUT & BFM (wrt


above example)

 Encapsulates communication between hardware blocks.


 Bundle of signals.
 Reduce the amount of code and promote reuse.
 Supports always, initial, task, function, assertion, covergroup.
 Synthesizable.
 Syntax: interface i_name [(interface_ports)];
[parameters]
interface_definitions; endinterface
[: i_name]
CLK
ADDR
DIN
BFM DUT
DOUT
SEL
ENB
WRITE

CLK

BFM
BUS DUT

Design on Spec HDL Design

Complex Signals:
SV Design
• Hard to add signals

• Error prone

Communication
encapsulated:
Reduces error, easier
Interface Syntax: to
modifySignificant
code reduction–
savestime
 Syntax: interface i_name [(interface_ports)];
[parameters]
interface_definitions;
endinterface [: i_name]
interface mem_bus (clk); parameter dbus=32,abus=8; input clk;
wire direction;

wire [dbus-1:0] data; wire [abus-1:0] address;


endinterface : mem_bus
//port of the interfacre
/ parameters
/ input to the interface
/ interface objects

An Interface can contain anything that


can be include in the module except
other Model definition and instantiation

Allows Structuring the information flow between the blocks


Interface Declaration:
interface apb_if;
Can declare parameters in Interface parameter DATA_WIDTH = 32;
parameter ADDR_WIDTH = 12;
/ List of Signals

logic sel,enb,write;
logic [ADDR_WIDTH-1:0] addr; logic [DATA_WIDTH-1:0]dout;
logic [DATA_WIDTH-1:0] din;
endinterface
// DUT Module
module dut (input clk, apb_if bus);
endmodule
// BFM Module
module bfm (input clk, apb_if bus);
endmodule

// TOP Module
module top;
logic clk ;
apb_if #(64,32) bus(); // Instantiate interface
//Overwrite parameters
bfm u_bfm (clk, bus); // Pass interface as
/ reference
dut u_dut (clk, bus); // Pass interface
// reference
OR
bfm u_bfm (.*); // If the signals are of the // same name
OR
dut u_dut (.clk(clk), // If the design has port list
.sel(bus.sel),
………..);
end module

Interface Port References:


module bfm (input clk, apb_if bus);
reg [31:0] data;
reg [31:0] addr;
assign bus.sel = 1’b1; assign bus.enb = 1’b1;

always @(posedge clk)

if(bus.write)
begin
bus.dout = data;
bus.addr = addr ;

end
endmodule
Concepts of Modports:
 Specifies direction for interface signals
 Reduces error from Verification Environment as well as DUT
 Provides error when direction is violated
interface apb_if;
logic sel,enb,write;
logic [11:0] addr;
logic [31:0]dout;
logic [31:0] din

wire sig1;Specify signals only with


modport master ( input dout,
output addr, din, sel, enb, write );
modport slave (output dout
,input addr, din , sel, enb, write);
endinterface
Usage of Modports:
Advantages of Interface:
• Can be instantiated like a module and re-used
• Can have parameters, constants and variables
• Can contain tasks and functions
• Can contain processes (always / initial) & continuous assignments used for system-level
modeling & test-bench applications
• Can contain Functional Coverage recording & reporting
• Can contain Protocol Checking Assertions
• Can be declared as virtual interface which saves memory as there is no physical allocation for
virtual interface

CLOCKING BLOCK
 Input Skew– TB samples DUT outputs before the clock edge.
One clock cycle delay from DUT output to TB input.
 Output Skew – TB drives the DUT inputs at the clock edge.

No delay from test bench output to DUT input.


 Enforce signal access & direction using modport .
 interface dut_intf(input bit clock); logic rst ;

clocking cb @(posedge
clock); default input #1ns
output
#1ns; output rst; output

signal sampled here signal driven herehhhe

input output
skew skew

in1;

endclocking
modport DUT(input rst, in1,in2,mode, output dout, rslt);
modport TB(clocking cb);
endinterface: dut_intf
 program automatic test(dut_intf.TB rtr_io);
Initial rtr_io.cb.rst <=
1’b0; rtr_io.cb.in1 <= ’1;
endprogram: test
module dut( dut_intf.DUT intf); …
endmodule: dut
 interface dut_intf(input bit clock); logic rst ; logic [3:0] in1 ; logic [3:0] in2 ; Mode type mode; logic
[7:0] rslt ; clocking cb @(posedge clock); default input #1 output #0; output rst; output in1; output
in2;
out
pu
t
m
od
e;
in
pu
t
rsl
t;
 endclocking modport
 TB(clocking cb);
endinterface
 Parameterized Skew Value

 Skew Value (or values) in a Clocking Block can be Parameterized

Example : output b1;

clocking clock1 @(posedge clk1); endclocking

parameter INPUT_SKEW = 2; clocking clock2 @(posedge cp2);

parameter OUTPUT_SKEW = 3; input a3; output b2;

default input #INPUT_SKEW endclocking

output #OUTPUT_SKEW;  ## Delay

input #1step a1;  The '##‘ Operator Defines Delay in Terms of


Number of Clock Cycles
input a2;
Example :
output #5ns b1;
##5 b2 = a2;
endclocking
 Default Clocking
 Multiple Clocking
 A Clocking Block can be Assigned as a
Example : Default Clocking inside a Module, Program
or
clocking clock1 @(posedge cp1);
Interface
input a1, a2;

Example :

module my_testbench;

default clocking clock1 @(posedge cp1); input a1, a2; output b1;

endclocking

clocking clock2 @(posedge cp2); input a3; output b2;

endclocking

##5 b2 = a2;

endmodule
OOPS Basics:
 OOP breaks a Testbench into blocks that work together to accomplish the verification
goal

Why Oops:
• Highly abstract system level modeling
• Classes are intended for verification
• Classes are easily reused and extended
• Data security
• Classes are dynamic in nature
• Easy debugging, one class at a time

OOP Program Constructs:


Building System Verilog OOP structure is similar to building Verilog
RTL structure: RTL OOP

Block definition Module class

Block instance Instance Object

Block name Instance name Object handle

Data types Reg and wires Variables

Functionality Tasks, Tasks, functions

Functions,

Behavioural
blocks

(always, initial)

Module vs Class:
Why use class ?

System verilog RTL OOP


Block definition Module Class
Block instance Instance Object
Block name Instance name Object handle
Data types Reg and wires Variables
Functionality Tasks, functions, Tasks, functions
Behavioral blocks
(always, initial )

 Objects are dynamic, modules are static


 Objects are created and destroyed as needed
 Instances of classes are objects
 A handle points to an object (class instance)
 Objects handles can be passed as arguments
 Object memory can be copied or compared

 Instances of modules can not be passed, copied or compared


 Classes can be inherited, modules can not
 Classes can be modified via inheritance without impacting existing users

 Modifications to modules will impact all existing users

Overview of Classes:
System Verilog provides an object-oriented class data abstraction.
Objects can be dynamically created, deleted, assigned and accessed by object handles
Object handles provide a pointer-like mechanism
 Objects are dynamic, modules are static
 Objects are created and destroyed as needed
 Instances of classes are objects
 A handle points to an object (class instance)
 Object handles can be passed as arguments
 Object memory can be copied or compared
 Instances of modules cannot be passed, copied or compared
 Classes can be inherited, modules can not
 Classes can be modified via inheritance without impacting existing users Modifications to
modules will impact all existing users
 Class objects are created with the new method(function).
Every class has a default constructor new method.
 Constructor – new function Allocates memory.
Returns the address of the memory to handle.
Initializes all the properties with default values.
 OOP objects are created from class definitions:
 Similar to instance creation from module definition
 Object memory is constructed by calling new()
 Handle used to refer to object
 class Driver; bit[3:0] sa, da; byte payload[]; task send();
...
endclass: Driver
Driver drvr1 = new(); Driver drvr2,
drvr3; initial begin drvr3 = new();
...
end
 Object memory is created via a call to new()
 Object members are accessed via the object handle:
OOPs Concepts:
Class Encapsulation
Object Abstraction
Inheritance
Polymorphism
Class Assignment Example:
 Object will be created only after doing new to an
class handle,
packet pkt_1;
pkt_1 = new();
packet pkt_2;
pkt_2 = pkt_1;
Here object is created only for pkt_1, pkt_2 is just an handle
to the packet.
 pkt_1 is assigned to the pkt_2.
 so only one object has been created, pkt_1 and
pkt_2 are two handles both are pointing to the same object.as both the
handles are pointing to the same object any changes made with-respect to
pkt_1 will reflect on pkt_2 .

Example-Class Constructor:
Null-Object Handle
Accessing members:

this Keyword:
this keyword
An object’s handle to itself Unambiguously refers to non-static class properties and
methods of the current instance (object).
More readable – allows method arguments to have same name as class variables.
module automatic test;
class Packet;
bit[3:0] sa, da;
bit[7:0] payload[];
function new(bit[3:0] sa, da, int payload_size);
this.sa = sa;
this.da = da;
this.payload = new[payload_size];
endfunction: new
endclass: Packet …
endmodule: test

Creating an Object:
 OOP objects are created from class definitions:
 Similar to instance creation from module definition
 Object memory is constructed by calling new()
 Handle used to refer to object

Objects:
Object Assignment:

Copying an Object:
Shallow Copy:
 Shallow copy – only properties of parent class are copied, not the objects of
subclass
 Shallow copy allocates the memory, copies the variable values and returns the
memory handle.
 In shallow copy All of the variables are copied across: integers, strings,
instance handles, etc
 Note: Objects will not be copied, only their handles will be copied.
 To perform full or deep copy, custom method can be added.

Example:
Object will be created only after doing new to an class handle,
packet pkt_1;
pkt_1 = new();
packet pkt_2;
pkt_2 = new pkt_1;
 In the last statement pkt_2 is created and class properties were copied
from pkt_1 to pkt_2. this is called as "shallow copy".
Deep Copy:
 Deep copy – both data and objects in the parent class are copied.
 User defined copy method should be used.
 In shallow copy, Objects will not be copied, only their handles will be copied.
 To perform full or deep copy, custom method can be added.
 In the custom method new object is created and all the class properties will be copied to
new handle and new handle will be returned
Advanced Oops:
Inheritance:
 New classes can be created based on existing classes.

 A derived class by default inherits the properties and methods of its parent or
 base class.
base class or parent class -> existing class.
derived class or child class -> New class.
 The derived class may add new properties and methods, or modify the inherited
properties and methods.
 Reusability – existing classes from previous project.
◦ Accelerates verification
◦ TB uses existing classes which have been used extensively and validated
– less Debug.
 TB methodologies – OVM, UVM, VMM
◦ Base class library – Common code
◦ Customization/Implementation – derived classes defined by user.
 New versions of TB components
◦ Driver injects errors into DUV.
◦ -----------------------------------------
Parent class properties is accessed using child class handle, i.e child class will
have(inherit) parent class properties and methods
class parent_class;
bit [31:0] addr;
endclass
class child_class extends parent_class;
bit [31:0] data;
endclass

module inheritence;

initial begin

child_class c = new();

c.addr = 10;

c.data = 20;

$display("Value of addr = %0d data =


%0d",c.addr,c.data); end

endmodule

Simulator Output

Value of addr = 10 data = 20

Overriding Class Members:


 Base class or parent class properties and methods can be overridden in the
child class or extended class.
 Parent class method display is overridden in the child class. calling
c.display will call display of child class not the parent class.

Example:
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass
class child_class extends parent_class;
bit [31:0] data;
function display();
$display("Data = %0d",data);
endfunction
endclass
module inheritence;
initial begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule
Simulator Output
Data = 20

Super:
 The super keyword is used in a derived class to refer to members of the
parent class.
 It is necessary to use super to access members of a parent class when those
members are overridden by the derived class.
 In child class, method with the same name of parent class will override the
method.
 By using super keyword parent class method can be accessed from child
class.

Example:
Parent class method display is overridden in the child class, by calling super.display()
from child class display method of parent class can be accessed.
class parent_class; bit [31:0] addr;

function display();

$display("Addr = %0d",addr);

endfunction

endclass

class child_class extends parent_class;

bit [31:0] data; function display();

super.display();

$display("Data = %0d",data);

endfunction

endclass

module inheritence;

initial begin child_class c=new(); c.addr = 10;

c.data = 20;

c.display(); end

endmodule

Simulator Output Addr = 10

Data = 20

Super.new:
 The super keyword is used within a derived class to refer to overridden
members of parent class as shown in this example:
 class Base1;
int var;
function new(input int var); // Has argument
this.var = var;
endfunction
endclass
class Extended extends Base1;
function new(input int var); // Needs
argument
super.new(var); // Must be first
line of new
// Other
constructor
actions

endfunction
endclass

Passing by Ref:
$Cast:
 By default all the members and methods
of a class are accessible from
anywhere using the object handle,
sometimes this could corrupt the class
members values, which should not be
touched at all.

 Access control rules that restrict the


members of a class from being used
outside the class, this is achieved by
prefixing the class members with the
keywords,

local
protected

 local:-

External access to the class members can


be avoided
 By default all the members and methods
of a class are accessible from
anywhere using the object handle,
sometimes this could corrupt the class
members values, which should not be
touched at all.

 Access control rules that restrict the


members of a class from being used
outside the class, this is achieved by
prefixing the class members with the
keywords,

local
protected

 local:-

External access to the class members can


be avoided

by declaring
members as local.
Any violation could result in compilation
error.

Syntax:
local integer x;
p_c.
displ
ay();
end
class parent_class; endmodule
local bit [31:0] tmp_addr; Simulator Output
function new(bit [31:0] r_addr); Error- Illegal class variable access
tmp_addr = r_addr + 10; testbench.sv,
endfunction
function display();
$display("tmp_addr =
%0d",tmp_addr);
endfunction
Local
endclass member
'tmp_addr' of
// module
class
module encapsulation; 'parent_class'
is not visible
initial begin to scope
'encapsulatio
parent_class p_c = new(5); n'.
p_c.t Example 2:Accesing local variable
mp_ within the class(Allowed)
addr
= class parent_class;
20; //
Acce local bit [31:0] tmp_addr;
ssing
function new(bit [31:0] r_addr);
local
varia tmp_addr = r_addr + 10;
ble
outsi endfunction
de
the function display();
class
$display("tmp_addr = parent_class p_c = new(5);
%0d",tmp_addr);
p_c.display();
endfunction
end
endclass
endmodule
// module
Simulator Output
module encapsulation;
Addr = 5
initial begin
protected :-
 In some use cases it is requires to access the class members only by the
derived class's, this can be done by prefixing the class members with
protected keyword.
Any violation could result in compilation error.
Syntax:
protected integer x;
Example 1: Accessing protected variable outside the class(Not Allowed)
class parent_class;
protected bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass

class child_class extends parent_class;


function new(bit [31:0] r_addr);
super.new(r_addr);
endfunction

function void incr_addr();


tmp_addr++;
endfunction
endclass

// module
module encapsulation;
initial begin
parent_class p_c = new(5);
child_class c_c = new(10);
/ variable declared as protected cannot be
accessed outside the class p_c.tmp_addr = 10;
p_c.display();
c_c.incr_addr(); //Accessing protected
variable in extended class c_c.display();
end
endmodule

Simulator Output
Error- Illegal class variable access
testbench.sv,

Protected member 'tmp_addr' of class 'parent_class' is not


visible to scope 'encapsulation'.

Example 2: Accessing protected variable in the extended class(allowed)


class parent_class;
protected bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr =
%0d",tmp_addr); endfunction
endclass
class child_class extends
parent_class; function new(bit
[31:0] r_addr);
super.new(r_addr);
endfunction

function void incr_addr();


tmp_addr++;
endfunction
endclass

// module
module encapsulation;
initial begin
child_class c_c = new(10);
c_c.incr_addr(); //Accessing protected variable in extended
class c_c.display();
end
endmodule

Simulator Output
Addr = 21

Virtual Classes:
 Create code that can be shared across
multiple projects.
 Build test benches that have a common look and
extended
feel. from virtual class can
 Two constructs that help build a be instantiated only if all virtual
shareable base class methods have bodies.
 Virtual class – can be extended but not  Pure virtual methods can be
instantiated directly.
 Pure virtual methods – declared only in abstract class .
prototype without a body.A class
Randomization
Randomization and its Advantages:

• Randomization – is a method of
• Generating Stimuli for the Purpose of Verification of a
DUT
Constrain the values of the random variables
 To create legal stimulus.
 To explore areas of interest.
 To conditionally switch between modes of operation.
Injecting Randomness Exposes Corner Cases
 Run longer simulations with more stimulus

 We can spend more of our time on Directed tests.


Use randcase to randomly select a statement to execute.
Use randsequence to execute a random sequence of statements.
 Conditionally randomize using if .. Else.
Requirement for Random Stimulus
 Functional coverage to measure verification progress.
 An automated way to predict the results, generally a scoreboard or
reference model.
 System Verilog provides a new set of constructs for generating and
constraining random variables.
 Random testing allows generation of many data sets with minimal code.
 Constraints restrict the data set to meaningful data.
 Random stimulus is very effective to capture design bugs.

Requirement of Randomization:

A directed test finds the bugs you expect to be in the design, While a random test
can find bugs you never anticipated.
O The last few bugs may only be found with directed tests, but the vast majority
of bugs will be found with random test.
www.technospirit.net
 Post_randomize is called after objects are randomized.
 Pre/post_randomize can be overridden. function void pre_randomize(); function void
post_randomize();
 class transaction; rand bit [0:7] data; rand bit [0:1] ch; function void post_randomize;
$display(“data is %b”, data);
$display(“channel is %b”,
ch); endfunction initial begin int success;
transaction t_h = new(); success =
t_h.randomize();

Random Variables.
 Class variables can be declared random using the rand and randc type
modifier keywords.
 Arrays, Dynamic & Associative can be declared rand/randc, in which case all
of their member elements are treated as rand/randc.
The size of a dynamic array declared as rand/randc can also be constrained. Then, the
array shall be resized according to the size constraint, and then all the array elements shall
be randomized

Random Modifiers:
 Rand modifier

 Variables declared with the rand keyword are standard random variables.

 Their values are uniformly distributed over their range.

Example:
rand bit [7:0] y;

 Randc modifier
 These are random-cyclic variables that cycle through all the values
in a random permutation of their declared range.
 Random-cyclic variables can only be of type bit or enumerated types, and
can be limited to a maximum size.

Example:
randc bit [1:0] y;
 When the iteration finishes, a new iteration automatically
starts.
Randc variable is solved before rand
Constraint Blocks
 The values of random variables are determined using constraint expressions that
are declared using constraint blocks.
 Operators with side effects, such as ++ and – - are not
allowed.
 Randc variables cannot be specified in ordering constraints( solve …
before).
 Constraint block names must be unique within a class.

Constraint Block Types:

 External constraint blocks


 Inheritance constraint blocks
 Set membership
 Distribution
 Implication
 If…else constraints
 Iterative constraints
 Global constraints
 Variable ordering
 Static constraints blocks
 Function in constraints
 Constraint guards

constraint XYPair::c {x < y} Body


Declaration
Constraints follow the same general rules
for inheritance as class
variables, task, and functions.
A constraint in a derived class that
uses the same name as a constraint
in its parent classes overrides the
base class constraints.
Example:

class A;

r
Constraints follow the same general rules
for inheritance as class
variables, task, and functions.
A constraint in a derived class that
uses the same name as a constraint
in its parent classes overrides the
base class constraints.
Example:

class A;

;
c

o
Constraints support integer value sets and the set
n
membership operator.
s
Absent any other constraints,
t all values have an
equal probability of being
r

chosen by the inside


a operator.
Example: i

n
rand integer
t
x, y, z;
constraint c1
c
{ x inside { 3, 5, [9 :
{ 15],
[24 : 32],
x
[y : 2*y ],
< z };
}
0

;
rand integer a, b, c;
constraint
} c2 { a inside
{n b, c }; }
e

d
c
integer fives[0:3]
l
a
= { 5,s
s
10, 15,
20 c};
l
rand
a
integer
Example: v; s
s

constraint
mode == smallc3 {B v< 10;
-> len mode == large -> len >

100; inside
fives;e }
x
Constraints support sets of weighted values called
t
Distributions. e

If no weight is specified
n for an item, the
default weight is :=
d 1

s
The := operator assigns the specified weight to the
item, or if the item is a
A
range, to every value in the range.
;
Example 1:

x dist {100c := 1, 200 := 2, 300 :=


5} o

x != 200;
s

t
x dist
r {100 := 1, 200 := 2,

300 a:= 5}
i

n
Example 2:
t

x dist { [100:102] := 1,
c
{
If...else style constraint declarations are
Limitations - A dist operation shall not be applied to
equivalent to implications.
randc variables x

>
Example:
0
i
;

f
}
e
n
( d
c
l
m
a
s
o s

m
l
Example:
l
class C;
)
rand byte A[] ;

constraint C1 { foreach ( A [ i ] ) A[i] inside {2,4,8,16}; }

constraint C2 { foreach ( A [ j ] ) A[j] > 2 * j; }

Endclass
l
When an object member of a class is
declared
e rand, all of its constraints and
randomn
variables are randomized
simultaneously
along with the other class variables and
constraints.
<

Constraint expressions involving


random
1 variables from other objects
are called global constraints.
0
Example:
;

e
class B;
i
rand bit s;
f
rand bit [31:0] d; constraint c { s -> d
== 0;
}
(
constraint order { solve s
beforemd; }

endclasso
d

)
A constraint block can be defined as static
by including the static
l
keyword in its definition.
e
If a constraint block is declared as static,
n to constraint_mode() shall affect
then calls
all instances of the specified constraint in
all objects.
>
Thus, if a static constraint is set to OFF, it
is off for all instances of
1
that particular class.
0
Syntax :
0
constraint_declaration
; ::= [ static ]
constraint
constraint_identifier
constraint_block
To handle these common cases, System Verilog allows
constraint expressions to include function calls, but it
imposes certain semantic restrictions.
Functions that appear in constraint expressions cannot
contain output or ref
arguments.
Functions that appear in constraint expressions should
be automatic.
Functions that appear in constraints cannot modify
the constraints, for example, calling rand_mode or
constraint_mode methods.
Circular dependencies created by the implicit variable
ordering shall result in an error. Within each
prioritised set of constraints, cyclical (randc) variables
are solved first.
Example:

function int count_ones ( bit [9:0] w );

for( count_ones = 0; w !=0; w = w >> 1 )

count_ones += w & 1’b1;

endfunction

constraint C1 { length =
= count_ones( v ) ; }
Constraint guards are predicate
expressions that function as guards
against the creation of constraints, and not
as logical relations to be satisfied by the
solver.

Treating these predicate expressions as


constraint guards prevents the solver from
generating evaluation errors, thereby failing
on some seemingly correct constraints.
Example:
class SList;

r
a
n
d

i
n
t

n
;
r
a
randomize()
n
Variablesdin an object are
randomized using the
S
randomize() class method.
L
Every class
i has a built-in
randomize()
s virtual method,
declared
t as:
virtual function int randomize();
n
The randomize()
e
method is a virtual
function that
x generates random
values fort all the active random
variables ; in the object, subject to the
active constraints.
c The randomize()
methodonreturns 1 if it successfully
str
sets all the random variables and
ai
objects to valid values
nt
here. endfunction
so
rt
{n
<
ne
xt.
By using the randomize()...with construct,
n;
users can declare in-line constraints at the
}
point where the randomize() method is
en
called. These additional constraints are
dc
applied along
la with the object constraints.
Example:
ss
class SimpleSum
c
on
rand
strbit [7:0] x, y, z;
ai
constraint
nt c {z == x + y;}
so
endclass
rt
{
Task
if(
n
InlineConstraintDemo(Si
e
mpleSum
x t p);
!=
int success;
nu
ll )
success =
n
<
p.randomize(
n
ex
) with {x <
t.
n;
y;}; endtask
}
The rand_mode() method can be used to
control whether a
random variable is active or inactive.

When a random variable is inactive, it is


treated the same as if it had not been
declared rand or randc.

Inactive variables are not randomized by


the randomize() method, and their values
are treated as state variables by the
solver.
All random variables are initially active.
The syntax for the rand_mode() method is
:

task
object[.random_variable]::rand
_mode(bit on_off);
or

function int
object.rando
m_variable::r
and_mode();
The constraint_mode() method can be used to
control whether a constraint is active or
inactive. When a constraint is inactive, it is not
considered by the randomize() method. All
constraints are initially active.
The constraint_mode() method is built-in and
cannot be overridden.
Example:
function integer toggle_rand( Packet p );

.
c

The randomize()
o method can be used to
temporarily
n control the set of random and
s
state variables within a class instance or
t
object.
r
When athe randomize method is called with no
arguments,
i it assigns new values to all random
variables
n in an object—those declared as rand
or randc
t —such that all of the constraints are
satisfied.
_

m
All other variables in the object are
o
considered state variables.
d

f
i

l
The scope randomize function,
t
std::randomize(), randomizes data in the
e scope, without the need to define a
current
class ror instantiate a class object.
1
The scope randomize function behaves
.
exactly the same as a class randomize
c
method, except that it operates on the
o
variables of the current scope instead of
class nmember variables.
s
Example:
t
module stim;
r
bit [15:0] addr;
b a
i
t i

[
n
3
t
1
: _
0
] m

d
o
a
d
t
a e
;
(

f
0
u
n
c ;
t
 i srandom() - Allows manually seeding the Random Number Generator
(RNG) of objects or threads.
Syntax o:
n void srandom( int seed );
function
e
The keyword randcase introduces a case
b
i l
statement
t that randomly
s
selects
g one of its branches.
e
e
n
_
s
t
i p
m
(
.
)
f
;
bi
i
tl

t
s
ue
c
cr
e
s1
s
,
.

c
r
do
_
wn
r
;s
st
r
c
ca
randsequence block.
e
i
s
s
n
=t

_
r
am
no
d
d
o
me
i
(
z
1
e
()
a
;
d
d
r
,
t
d
ao
t
g
a
g
,
rl
d
e
_
w_
rr
;
n
If … else Production Statements - A production
rd
can bee made conditional by means of an if...else
production
t statement.
=
Example:
u
r
randsequence()
n
p ...

P
r.
P
d
r _
_
P
wa
O
rn
;
d :
en
df o
un i
cti m f
on
...
i
(
endm z
odule
e d

e
$urandom_range()
( - Returns an unsigned
p
integer within a specified range.
) t
Syntax : hfunction int unsigned $urandom_range( int
;
unsigned maxval, int unsigned minval = 0 );
e
Example: <
val =n$urandom_range(7,0);
d 2
Example:
f
u val = $urandom_range(0,7);
)
n
t U
i S
Interleaving
o Productions - rand join
H
n
The rand join production control is used to
randomlye interleave two or more production
l
sequences while maintaining the relative order
s
of each sequence.
e

Example: P

O
randsequence( TOP )
P
TOP : rand join S1 S2 ;
S1 : A B ;
;
S2 : C D ;
endsequence
P
The generator will randomly produce the following
U
sequences:
S

H
ABCD
ACBD
ACDB :
CDAB
CABD
CADB {

;
d

o
Aborting Productions - break & return
_
The break statement terminates the sequence generation.
p
Example:
u
randsequence()
s
WRITE : SETUP DATA ;
h
SETUP : { if( fifo_length >= max_length ) break; } COMMAND ;
(
DATA
) : ...
endsequence
;

next_statement : ...
The return statement
} aborts
the generation
; of the
current production.
P
Example: O
P
randsequence()
:
TOP : P1
P2 ; P1{ : A B
C;
-
P2 : A- { if( flag == 1 ) return; }BC;
d
A : {e$display( "A" ); } ;
B : { if( flag p== 2 )
t
$display( "B" h); } ;
C : { $display(; "C" ); } ;
endsequence d
o
_
p
o
p
(
)
;

}
e
n

Value Passing Between Productions


d
s
e
Data can be passed down to a production
q
u

about to be generated, and generated


e
n
c
productions can return data to the non
e

terminals
Repeat that triggered
Production their
Statements generation.
- The repeat
production statement is used to iterate over a
Example:
production a specified number of times.
Example: r
randsequence()
a
...

PUSH_
n
OPER d
: s
repeat
e
( $ura

ndom q
_rang u
e( 2, 6
e
))

PUSH
n
; c
PUSH
e
: ...
endsequen
(
ce

m
a
i
n

Program Block
Topics Covered: )
 Program Blocks
 Definition & Declaration
 Accessing Program Blocks
 Relation between Program Block & Module
 Advantages of Program Blocks
m
Program Block a
• A program block is a recognition of the differences in goals of writing a design
and testbench. i
• A program block is defined within the n
“program - endprogram” keyword pair.

:
• It serves purposes :
• Provides an entry point to the execution of the testbenches.
• Acts as a scope for data defined within thisfprogram block.
 A program block is enclosed within the keywords i
“program – endprogram”.
Example : r
program my_prog ( input clk , input reset , input s
[7:0] in_pkt , output [7:0] out_pkt ); initial
begin t
out_pkt = 8'h0;
end s
endprogram
e
 A program module can be defined independently similar to a
module and then instantiated within a module. c

 Can be defined within a module itself - Implicit o

Definition.
n
Example : d

g
n
module my_mod(...); k
program my_prog(...);
endprogram
;
endmodule
In such a case a module variable will also be accessible from the program.

f
Program Block &Module- Similarities & Differences:
 Similarities
i
 A program block may have zero or more inputs, outputs and inout ports.
r
 A program block can contain zero or more initial blocks, assignment
statements and timeunit declarations etc.
s
 Type and data declarations, functions and tasks can be defined within a
program
block similar to what you can do in a module.
t
 A hierarchy can contain any number of Program blocks. These program
blocks may interact through their ports or may be isolated from one
other.
 Differences
 A program block is always a leaf level node : in a hierarchy.
 A program block can not contain any always statement, UDP,
module, interface or other program.
a
 A program definition can occur within a module but a module cannot be
defined this way.
d
• A program can call a task or function in modules or other Programs.
But a module cannot call a task or function in a Program.

• The way variable assignments are handled in procedurald


blocks.Avoiding Race
Condition using Programs
• All variables local to a Program can only be
assigned using blocking assignment.
|
• All other variables must be assigned using non-blocking assignments.
• Blocking Tasks

a Program, but a Program can call a task or d


• A module cannot call tasks or functions defined within
function


defined within a module or another Program.
Program Control Tasks e
c
• In addition to the normal simulation control tasks
($stop and $finish), a program can use the $exit
control task.

• The syntax for the $exit system task is:


;
s
• task $exit(); e
• c they execute their last
When all initial blocks in a Program finish (i.e.,

o
n
d

p
o
p

p
u
s
h

a
d
d
:

g
24
9 e
n
Verilog includes most of( the
statement types of C, except
" for
a
do...while, break, continue
d
and goto
d
Verilog has the repeat and
"
disable statement which ) C
does not.
;
The use of the Verilog
d disable to
e
carry out the functionality of
c
break and continue requires the
user to invent block names.
:

SystemVerilog adds C-like


g break,
e
continue and return functionality,
n
which do not require the
( use of
block names "
d
e
Procedural assignment evaluation can be The = toke
modeled as: c
assignmen
"
)

;
p
Procedural assignment evaluation can be
o
modeled as: p
Blocking
:
Non-blocking
g
Procedural assignment execution can be
e
modeled as:
n
Sequential (
"
Concurrent
p
Procedural assignment timing
o controls can
be modeled as: p
"
Delayed evaluations
)
Delayed assignments
;
p
u
s
h

g
e
n
(
"
The = token represents a blocking procedural
p
assignment u
s
Evaluated and assigned in a single step

Execution flow within the procedurehis blocked until the


assignment is completed "
)
Evaluations of concurrent statements in the same time step
are blocked until the assignment is completed
;
g
e
n
(

s
t
r
i
n
g

Representing Stimulation Time as Queues:


s
 ➤ Each Verilog simulation time step is divided into 4 queues
Time 0:
=

"
d
o
n
Q1 — (in any order) : e
"
Evaluate RHS of all non-blocking assignments
Evaluate RHS and change LHS of all blocking assignments
Evaluate RHS and change LHS of all continuous assignments
)
Evaluate inputs and change outputs of all primitives
Evaluate and print output from $display and $write
Q2 — (in any order) : :
Change LHS of all non-blocking assignments
Q3 — (in any order) : {
Evaluate and print output
from $monitor and $strobe $d
Call PLI with
reason_synchronize i
Q4 : Call PLI with reason_rosynchronize s
Time 1: p
A <= 1; l
#5 B <= A + 1; a
5) y
end (
always @(posedge clk)
#5 A <= A + 1; s
always @(posedge clk) #5 B <= A + 1;

What values do A and B contain after 10 }time units?


;
always @(instructor_input) The value of
if (morning) after 8 clock

understand }
= instructor_input;
else if (afternoon)
u
;
nderstand en
s
e
always @(instructor_input)
q
if (morning) u
understand e
= instructor_input;
n
else if (afternoon)
u c
nderstand e
= #5
instructor_i
nput;
else if
(lunch_time)
u
nderstand
= wait (!
lunch_time)
instructor_i
nput;

The value of IN is evaluated when it changes, but is not assigned to OUT until
after 8 clock cycles

Loops:
 System Verilog supports the following loop structures :
 for
 foreach (is used for iteration over the elements of an array)
 while
 repeat
 forever
 do.. While (new in SV)

for
(var_dec_or_
assn; expr;
var_assn)
begin
statemen
t_or_null
end

E
x
a
m
p
le
:
m
o
d
u
l
e

f
o
r
_
l
o
o
p

(
)
;

i
n

i
foreach (expr)
t
begin i
statement_or_null
a end
l
Example b :
e
module
g
foreach_loop ();
i byte a [10] =
n
{0,6,7,4,5,66,77,9
9,22,11};
fo
initial
begin r
fore
ach(
(a[i])
i beg
in n
t$display ( "Value of a is ‰g" ,i);
end
#1i $finish ;
end
=
endmodule
Example :
0
module for_each ; initial begin
int src[2][3] ; ;
$display("Initial value:"); foreach (src[i,j])
i
$display("src[%0d][%0d] = %0d", i, j, src[i][j]);
$display("New value:"); src = '{'{9, 8, 7},'{5,3,1}};
<
foreach (src[i,j])
$display("src[%0d][%0d] = %0d", i,4 j, src[i][j]); end
;
endmodule

+
+
)

b
while (expr) e do
begin g begin
statement_or_null i s
n
end t
#1 $display ( "First ->
a of i = ‰g" , i);
Current value
Example : end t
m f
e
o o
m
d r
u ( e
l i n
e n
t
t
w i _
h = o
i r
l 4
_
e ;
_ i n
l > u
o l
o 0
l
p ;
i
( -
) - e
; ) n
b
d
e
b g w
y i h
t n i
e # l
1 e
a
$ (
= d e
x
0 s p
; p r
i l )
n a ;
i y
t E
Jump Statements:
i
 System Verilog
(
xa
has following jump statements :
a
l
 break àOut of Loop " mp
b S
 continue àSkip To End Of The Loop le :
e e
 return m
g Expression àExit Fromca Function
o
i àExit From a Task Or o
 return Void Function
d
n n
u
w d
Jump Statement- Break l
h
e
Example: i -
l >
 module break_loop (); d
 initial begin e o
 for ( int i = 0 ; i < 10; i ++ ) C
_
 begin ( u
 #1 $display ( "Current value of i = ‰g" l
a r , i);
 if ( i == 5) begin o
 $display ( "Coming out of for loop" ); r break ;
o
 end end < e
 #1 $finish ; p
n
 end endmodule1
t
Jump Ststement- Continue (
0
Example )
) v
module continue_loop (); ;
a
initial begin
for ( int i = 0 ; i < 10; i ++ ) l
begin b u
if ((i >= 5) &&e (i < 8)) begin b
e
$display ( " Continue with next iteration" ); continue ; y
end g
t
i
#1 $display ( "Current o , i);
value of i = ‰g"
end e
n f
#1 $finish ;
end endmodule $
a
Jump Statementd– Return i
Example i
=
s =
module return_function (); initial
p
0
l ‰
;
a g
y "
i
( ,
n
i
" i
t
C )
i
begin u ;
printI(); a
r e
#1 $finish ; endr l
n
function void printI; b
e d
begin e
for ( int i = 0 ; in< 10; i ++ ) begin
for ( int
g
if (i >= 5) begint j = 4,k=0 ;
i
return ; j > 0; j
n
end v --,k++ )
$display ( "Current value of i = ‰g" , i); d
a begin
end end o
l #1
endfunction endmodule
u $display
Jump Ststement- Return Expression:
Example : e ("
b
Current
module return_function (); e
initial o value of
g
begin f k=
$display ( "Value returned i
‰g" , from
k); function = ‰g" , printI());
#1 $finish ; n
end a end
$
function int printI; #1
begin = d
$finish;
for ( int i = 0 ; i < 10; i ++ ) i
begin e
s
‰(i n
if >= 5)
begin p
g d
return i; // Value Returned end l
"
$display ( "Current value of i = ‰g" , i);
end a
end y
,
endfunction e
endmodule n
(
Final Block: a d
 The final block is like an) initial block, defining a procedural block of
m
statements. "
 It occurs at the end of; simulation
o time
C
 Executes without adelays. d
u simulation
 A final block is typically used to display statistical information about the
u
Example : r
program final_block; + l
r
+ e
initial e
begin n
#40; ;
t
e
n
v
d
a
u
#
e
1

o
$
$finish; f
end f
i
final begin a
n
$display("End of the simulation time =%g\n",$time); end
i
endprogram =
s
h Processes

Topics Covered: g
;
 Introduction "
 Combinational Logic procedural block - always_comb
Latched Logic procedural block - always_latch
,
e
 Sequential Logic procedural block - always_ffa);
n
Continuous assignments a
d
Fork–join Introduction
e
Fork–Join n, Fork–join_any , Fork–Join_none with examples +
+
d
Dynamic Process Control consrutucts - Wait Fork and Disable Fork
m
Introduction: ;
o
 Verilog -2001 e
d has always and initial which are static processes
 SystemVerilog n
u has both static processes, introduced by always, initial or fork, and
d
dynamic processes, introduced by built-in fork...join_any and fork...join_none.
l
 S V creates a thread of execution for each initial or always , for w each parallel
e
statement in a fork –join and for each dynamic processhand each continuous
assignment
repeat (expr) end i
l
begin
e
statement_or_null
end (
a

Example :
<
module repeat_loop ();
always@(a,b);b
// sensitivity list
1
y
beginIntroduction-
t Verilog always block 0
)
e
Sum= a+b; ;

a
Diff = a-b; Prod =a*b;
#
end
0
$
;
f
i
 Verilog has general purpose always procedural block
n
 Always can represent
i any type of logic (comb/latch/seq) ,used to model algorithmic
i
logic behavior at higher level of abstraction , used in TB to model clock oscillators ,
n
perform tasks repetitive in nature in verification process s
 i design intent from the procedural block’s contents – effort on the
Tools must infer
h
part of synthesis/formal
t tools to deduce the type of hardware
 Synthesis guidelines for always procedural blocks to be followed (IEEE 1364.1)
 i modeling guidelines cannot be enforced for a general
But unfortunately ;
purpose alwaysa procedural block
e
 Comes the needl for Specialized procedural blocks which are synthesizable and
unambiguous for the Simulation/Synthesis/Formal/LintingnEDA tools
d

Introduction-SV
b Specialized always block
 Comes the neede for Specialized procedural blocks which are synthesizable and
e
unambiguous gfor the Simulation/Synthesis/Formal/Linting EDA tools
 System improved upon general purpose always proceduralnblocks of Verilog and
i
introduced specialized always procedural blocks d
 Advantages : n m
 Are synthesizable r
 Documents the design intent and are better modeling choice o than general purpose
e
always procedural block d
p and other EDA tools to check for the synthesis compiler
 Require the simulators u
rules e
 Unambiguous for the Simulation/Synthesis/Formal/Lintingl EDA tools
a
 Helps eliminate potential e
modeling errors early in design process before the models
t
are ready for synthesis
always_comb
 SystemVerilog provides a special always_comb procedure for modeling
(
combinational logic behavior.
 There is an inferred1 sensitivity list that includes the expressions defined.
0 on the left-hand side of assignments shall not be written to by
 The variables written
any other process.
)
 Non-ambiguous design intent
 The procedure is automatically triggered once at time zero, after all initial and always
blocks have been started so that the outputs of the procedure are consistent with the
inputs
b
Example:
e
always_comb g
a=b&c; i
n
$
d
i
s
p
l
y

reg " parity;


C
initial begin
u
$monitor ("@%g
r a = %h b = %h sum = %h parity = %b", #1 a = 1;
r
#1 b = 1;
e
#5 a = 10; n
t
#1 $finish; end

always_comb v

begin : ADDER asum = b + a;


l
parity = ^sum;
u
e
end endmodule

o
Differnce between always_comb and always@(*)
f
 always_comb get executed once at time 0, always @* waits till a change
occurs on a signal in the inferred sensitivity list
a
 Statement within always_comb can't have blocking timing, event control, or fork-
join statement. No such restriction of always @*
 EDA tool perform additional
= checks to warn if the behavior
within always_comb procedure doesn't represent combinatorial
logic
‰ side of assignments within an always_comb
 Variables on the left-hand
procedure, including variables
g
from the contents of a called function, shall not be written to by any other processes,
"
whereas always @* permits multiple processes to write to the same variable.
always_comb is sensitive to changes within content of a function, whereas always @* is only
sensitive to changes to the, arguments to the function

my_xor(a); always
a
@*
)
my_or(a); endmodule
;
a

+
+

;
e
n
d
#
1

$
always_combf// infers @state –block automatically
//executes i once at time zero , even if not triggered
n
case (state)
i
WAIT : nextstate
s = LOAD ; LOAD : nextstate = STORE
h
; STORE : nextstate = WAIT ;

endcase ;

always_latch:
e
 System Verilog also provides a special always_latch procedure for modeling
n behavior.
latched logic
d
 The always_latch procedure determines its sensitivity and executes identically to the
e
always_comb procedure.
n
Example :
d
always_latch
m
begin
if ( ck o)
q <= d; d
end u
l
Example:
e
module always_latch_process();
reg [7:0] sum,a,b;
reg parity;
reg enable = 0;
initial begin
$monitor ("@%g a = %h b = %h sum = %h parity = %b", $time, a, b, sum, parity); #2
a = 1; #2 b = 1; #2 a = 10; #2
$finish;
end
always #1 enable = ~enable;
always_latch
begin : ADDER
if (enable) begin
sum <= b + a;
parity <= ^(b + a);
end
end
endmodule

always_ff:
 The System Verilog always_ff procedure can be used to model synthesizable
sequential logic behavior.
 Variables on the left-hand side of assignments within an always_ff procedure,
including variables from the contents of a called function, shall not be written to by any
other process.

Example :
always_ff@ ( posedge clock iff ( reset == 0) or posedge reset) begin
r1<= reset ? 0 : r2 + 1;
end
always #1 clk ++;

// use of iff makes sure that block does not get

// triggered due to posedge of clk when rst == 1

always_ff @(posedge clk iff rst == 0 or posedge rst)

begin : ADDER

if (rst) begin

sum <= 0;

parity <= 0;

$display ("Reset is asserted BLOCK 1");

end else begin

sum <= b + a; parity <=

^(b + a);

end

end

Continuous Assignments:
 In Verilog only net data types can be driven by continuous assignment,
 In systemVerilog this restriction is removed and any data type can be driven
using continuous assignment.
 Nets can be driven by multiple continuous assignments or by a mixture of primitives and
continuous assignments.
 Variables can only be driven by one continuous assignment or one primitive
output
Example : assign binary_out = ( ! enable) ? 0 : (encoder_in ==
16'bxxxx_xxxx_xxxx_xxx1) ;
Fork-Join Introduction:
 Fork-join statement has come from Verilog
 It is used for forking out parallel processes in test bench.
 SV substantially improved fork/join construct to have much more controllability in
process creation, destruction, and waiting for end of the process
 There are 3 different kind of join keyword in SV, each specifying a different way of
waiting for completion of the threads/process created by the fork.
 join : waits for completion of all of the threads
 join_any : waits for the completion of the 1st thread, then comes out
of fork loop, but lets the other process/thread execute as usual
 join_none : doesn't wait for completion of any thread, just starts then and immediately
exits fork loop.

fork/join | join_none | join_any create threads.


 disable label; terminate just the named block
 disable fork; terminate all child threads below the current
context level
Note – may stop more threads than you wanted! Wait fork – waits until all the fork
processes are complete

Fork Join Syntax:


fork
begin : First thread
// Code for 1st thread end
begin : Second thread
// Code for 2nd thread end
begin : Third thread
// Code for 3rd thread end
// 4th thread
// 5th thread
join // Can be join_any, join_none in SV
task automatic print_value; input [7:0]

value;
input [7:0] delay; begin
#(delay) $display("@%g
Passed Value %d Delay %d“,
$time, value, delay);

end endtask
begin

#30 $display ( “@%0d : sequential after #30 “ ,


$time );

#10 $display ( “@%0d : sequential after #10 “ ,


$time );

end join

$display("@%0d Came out of fork- join", $time);

#80 $display("@%0d final after #80 ",


$time);

end

endmodule
Simulator output

@0 : start fork … join

example @10:

sequential after #10

@10 : parallel start

@20 : parallel after

#10 @40 :

sequential after

#30 @50 :

sequential after

#10 @60 : parallel

after #50 @60

:after join
@140 : final after
#80
endmodule

#30 $display ( “@%0d : sequential after #30 “ ,


$time );

#10 $display ( “@%0d : sequential after #10 “ ,


$time );

end Join_any

$display("@%0dCame out of fork- join", $time);

#80 $display("@%0d final after #80 ", $time);

end

endmodule
@0 : start fork … join –any example

@10 : sequential after #10 @10 :

parallel start
@10 :after join_any @20 : parallel
after #10
@40 : sequential after #30 @50 :
sequential after #10
@60 : parallel after #50
@90 : final after #80
begin

#30 $display ( “@%0d : sequential after


#30 “ , $time );

#10 $display ( “@%0d : sequential after #10 “ ,


$time );

end Join_none

$display("@%0dCame out
of fork-join",
$time);

#80 $display("@%0d final


after #80 ",
$time);

end endmodule
Simulator output
@0 : start fork … join
_none example

@10 :

sequential after

#10 @10 :after

join_none
@10 : parallel
start

@20 : parallel

after #10 @40

sequential after

#30

@50 :

sequential after
parallel after

#50 @90 : final


Process Control:
 System Verilog provides constructs that allow one process to terminate or wait for the
after #80
completion of other processes. The wait fork construct waits for the completion of
processes. The disable fork construct stops the execution of processes.
 wait fork
 disable fork

Wait Fork:
Now, suppose you have exited the fork loop by join_none or
join_any and after some steps, you want to wait till completion of
all the threads spanned by the previous fork loop. SV uses "wait
fork" for the same

 The wait fork statement is used to ensure that all child processes (processes
created by the calling process) have completed their execution

Example :
fork a;b;c;
join_any
wait fork; // wait for children to complete

#10;
$exit
initial begin

fork

#10 print_value
(10,7);

#20 print_value
(8,5);

#30 print_value
(4,2);

join_none

#50;

endmodule
endmodule
wait fork; endtask
initial begin

fork

#10 getvalue (11, 8);

#20 getvalue (4, 6);

#30 getvalue (8, 5);

join_any

#5 disable fork;
...
end
you have exited fork loop by join_none or join_any and after
some steps, you want to kill just one thread (out of many). The
solution, have named begin end block and call "disable ". (For
example, in the last example if you want to kill only the 2nd
thread after exiting the loop via join_any/join_none, then add
"disable Second_thread;" at the point where you want to disable
the second thread.

fork
begin : First_thread

Co

de

fo

1s

th

re

ad

en

d
eg
in
:
Se
co
nd
_t
hr
ea
d
/
/

C
o
d
e

Inter Process Synchronization


f
Topics Covered:
 o Mailbox
 Mailbox Methods
 rTypeless Mailbox
 Semaphore
 Semaphore Methods
 2Non-Blocking Event Trigger
 Persistent Event Trigger
 nEvent Sequencing


dEvent Variables
Mailbox and Events

t
Mailbox:
 Mailbox resembles a FIFO in its operation
 hMailbox can have a maximum (bounded) size or can be
unlimited (unbounded)
r
 Mailbox has in-built methods for access to its contents
 eMailbox has to be instantiated before using
 Mailbox can take a mix of different data types as its messages or items
a
d
e
nd
join

_any //

can be
Mailbox Declaration:
join_no

ne// Inside a module


module mbx_test;
disable
mailbox mbx;
second
endmodule
_thread

; // Inside a program

program mbx_test;

mailbox mbx;

endprogram
// Inside a class
class mbx_class;
mailbox mbx;
endclass
new( ) : Mailbox
constructor which can optionally specify
maximum size. Default size is unlimited.
put (): Puts an item into the Mailbox, blocks if
Mailbox is full
class gen;
r
a
n
d

b
y
t
e

p
a
c
k
e
t
;

e
n
d
c
l
a
s
s
g
e
getn() : The get() method retrieves a
message from a mailbox. Process
g blocks if the Mailbox is empty.
f e
o n
r _
k h
1
;
b
e
g i
i n
n i
tr
i e
a p
l e
ba
e t
g
i (
n
4
g
)
e
n
b
_
e
h
g
i
n
=

 num( ) : Returns the number of messages currently in the


n
Mailbox.
g
e
e
w
n
;
_
h
1
m
.
b
r
x
a
n
=
d
o
n
m
e
i
w
z
(
e
5
(
)
)
;
;
m
b
r x
e .
p p
e u
a t
t (
g
(
n
1
_
0 h
) 1
i
n .
p
i
b a
t
e c
i
g k
a e
i
l t
n
)
g
;
b e
e /n
g /_
h
i
P1
n
u.
mbx =
tr
new(30);
a
repeat
tn
(10)
hd
begin
eo
#m
10;
ii
gen
tz
_h1.
ee
ran
m(
dom )
e
ize()
;
n
; $
d
$display
d ($time," Number of items in mailbox
",mbx.num)
i ; mbx.put(gen_h1.packet);
end
end p
n
l
d
a
y
try_put( ) : Tries to put an element into Mailbox. If
element
b is
(
e $ successfully put, it returns ‘1’ otherwise
g t returns ‘0’ if
i i
Mailbox is full.
n m
b e
r
i e ,
t p "

e
N
s a
u
t
u m
c b
(
c e
4
e) r
s
o
s b
f
; e
g
i
i
t
n
i e
#
n m
i s
1
t 5
i
i ;
n
a m
l b
m
x
b i
g
e l
e
g b
t
i o
try_get( ) : Tries to retrieve an element from the
(
x
n g
Mailbox. If element is successfully retrieved and
-e
type match " is found, it returns ‘1’. Returns ‘-1’ if
- n
there is a ,type mismatch. Returns ‘0’ if Mailbox is
- _
empty.
- h
m
- 2
bi
- .
x
n p
.
m
ba
t xc n
u
k
m
=e
s )
t
u ;
n)
c m
e;
c b
w
e x
(/
s .
2/
s p
)
; u
;G
et
rep
t(
i ge
n a
te
i
hn t
t
e_
i
h(
a
i11
l
t. 0
b
ep )
c $
g b
d k
i e
i e
n
t g
s
try_peek( ) : Tries
m to copy an element from the
i
b If
Mailbox. p)element is successfully copied and
x l ; isnfound, it returns ‘1’. Returns ‘-1’ if
type match
e aatype mismatch. Returns ‘0’ if Mailbox is
there is
empty =
n . y
i dn ( g
n e $ e
w t n
t
e( i _
2
n m
s ) h
d; e 1
u ,
. f
c P
u o
r
c
t r"
a
es k
G n
s
o d
sf
i bt o
;
v e m
e gt i
ih
z
it ne
e
ni repeat (2)
m (
---statements for
i
i
e t ) into mailbox end
putting
ts ;
e begin
i repeat (10) begin
m
aa success =
n mbx.try_get(gen_h
l f #1.packet);
d
r 3 $display($time, "
bw o ;####Got the
ea suc
i mailbox####
g c
t m ",gen_h1.packet,
i esuccess );
s a
e s
n i
peek( ) : Attempts to copy an element from the Mailbox. If
n s
a m lis successfully copied and type match is found, it
element
d
s b ‘1’.
returns b If there is a type mismatch then run-time error
=
is generated. Process blocks if Mailbox is empty.
x o
m x
bit
j m
a
o
i = s b
i "
l u x
n ,
b .
c
en g
o
n ec
t
x e
d r
w
e n
_ y
i (
s
s 2 h _
s; 2 p
)
f . u
u ;
i p t
l a
n (
l c
it g
.
f e k
ia e
Wo n
l _ t
a r
i kb h
) 1
t e
b
s ; .
eg
ep
g
f ni a
i
o dn c
n
r e k (2) ---statements for
mbx repeat
n = e
m putting into mailbox end
d t
a new(
begin
)
;
l
success =
j b fork $di
mbx.try_peek(gen_h1.pa
o o begin s
cket);
i x p
repeat (2) ---statements for putting
$display($time, " ####Copied the number from
Mailbox Typeless:l
n mailbox####
into mailbox end
",gen_h1.packet, success);
i a
Typeless Mailbox : Mailbox can hold messages of different types.
t e begin
bit bit_type; y
int int_type;
e n
initial begin ( repeat (10) begin
md
mbx = new(2);
mbx.peek(gen_h2.packet);
mbx.put (bit_type); $
s
mbx.put(int_type); t $display($time, " ####Copied the number from
end
mailbox####
Parameterized i : Holds
Mailboxes ",gen_h2.packet, success);
messages of that and equivalent
types. t
: e m
Example o e
e = new ( );
Mailbox #(string) smbox
Mailbox.putn(string_var);
n
Mailbox #(pkt) pmbox, = new ( );
b
Mailbox.putd(pkt);
d
e
Semaphore: "
 Semaphores are a key-based synchronization mechanism.
t
 Semaphores are typically used for mutual exclusion, access
j
control toashared # and for basic synchronization.
e resources
 Semaphore is a Built-In Class and has to be constructed by
k o #
new() before usen
Semaphore e Declaration:
i #
Example n d
n #
program semaphore_test; semaphore sem_key;
endprogram p
e
o methods:
Semaphore u
n
u
d j t
t
. o
t
i
h
n e

n
u
m
b
e
r
t
o

new( ) : Creates
m a Semaphore with a specified
number
a of keys. Default number of keys
is zero.
i

semaphorel key;
b
i o
n x
#
i #
t #
#
i
a "
,
l g
e
n
_
b h
e 1
.
g p
i a
c
n
k
k e
e t
,
y suc
c
e
s
s
n
e : Returns
put()
)
one or more keys into the bucket.
;
w You can put more keys than taken or even not
end
( taken any key.
end
i
2
n
)
i
;
t
i
/
a
/l

n
eb
we
(g
i
)
n
#
o 1
r 6
;
n k
e e
w y
( .
0 g
)
t
(
. 2
key.get(2);
. ) #5;
print(5,8);
. ;
key.put(1); end
z
begin.
e .
#10;
r .
key.get(2);
o .
print(5,9);
.
key.put(2);
end .
k
begin #15;
.
e
key.get(1);
.
y .
print(5,1);
end s .
. .
. .
. .
. .
.
.
k
.
e
.
y
.
.
. p
. u
. t
. (
.
. )
;
get( e) : Obtains one or more keys from the bucket
Examples : blocks if
n if available, requested number of
d keys are not available.
module events_blocking; event e1, e2;
initial
t begin
initial
begin r
$display("@%0d: 2: before trigger",
y
$time);
#16;
-> e2;
_
key.get(2);
@e1;g
$display("@%0d:
e 2: after trigger",
..............
t $time);
end
(
initial begin
)
$display("@%0d: 1: before trigger",
$time);
:
-> e1;
@e2;
T
$display("@%0d: 1: after trigger",
r
$time); end
i
endmodule
e
s
A process can wait for triggered property of the
event.
t
Examples :
o

o
b
t
a
i
n
o
n
e

Examples :
o
r
module event_nb(); event e1,e2; always
wait_order(e1.triggered,e2)
m
o
$info(“events in order”);
r
end emodule

Example
K :
e
y
s

w
Example :
i
fork
t
begin
h
repeat (5) begin
o
gen_h1.randomiz
u
e(); #3;
t mbx.put(gen_h1.
packet);
b $display($time," Put the number into mailbox
l ",gen_h1.packet ); @wait_for_receive_permit;
oend
c
end
k
i
begin
n
repeat (5) begin
g
mbx.get(gen_h2.pack
.
T
a
k
e

Example :
s

t
h
e

k
e
y
s

i
f

a
v
a
i
l
b
a
l
e

a
n
d

r
e
t
u
r
n
s
Example :
fork

begin
1
repeat (5) begin

gen_h1.randomiz
e(); #3;
e
mbx.put(gen_h1.
l
packet);
s
$display($time," Put the number into mailbox
e
",gen_h1.packet ); @wait_for_receive_permit;
end
end r
e
t
begin
u
repeat (5) begin
r
mbx.get(gen_h2.pack
n
et); # 14;
s
$display($time," Got
the number from

mailbox ",
0
gen_h2.packet);
-> ’
.
wait_for_receive_permit;
i end
nend
join
i
t
i
a
l
e
g
i
n
. #
1
6
;
k
e Coverage:
Topics Covered:
y
 Introduction to Coverage
. Coverage Model - Cover group
 Defining
 Using Cover Groups in Classes
t Coverage Points
 Defining
 Defining Cross Coverage
r
 Specifying Coverage Options
 Pre - Defined Coverage Methods
y
 PreDefined Coverage Syst em Tasks & Functions
_
g
e
t
(
3
)
;

$display (“This statement is executed even if 3


avail
keys are not able”);
...............

The “covergroup - endgroup” construct
encapsulates the specification
of a coverage model.
Each “covergroup” spec consists of the
following components.

A Clocking Event that Synchronizes the


Sampling of Coverage Points
A Set of Coverage Points – Sample
Points known as Cover Points
Cross Coverage between Coverage
Points or variables.
Optional Formal Arguments
Coverage Options

A System Verilog coverage group


creates a database of "bins" that store a
histogram of values of an associated
variable.
Label : coverpoint coverpoint_name;
Label : cross variable with variable or
coverpoints

option

option_name =

expression;

endgroup
covergro
up_name

covergroup_inst
= new;
The “covergroup” construct is a user-defined
type. The type definition is written once, and
multiple instances of that type can be created in
different contexts. Similar to a class, once
defined, a covergroup instance can be created via
the new() operator.
A covergroup can be defined in a “module”,
“program”, “interface” or
“class”.
ACCUM_COVERAGE
DATA_COVERAGE

endgroup

coveralu c1 = new;
 You do this by creating coverpoint
bins:
bins a[]= {0,2, [8:20]};

 A coverpoint bin is used to define the


range of values
that will be stored while a simulation is
running.
 A bin has a name, a count
and a set of values
name- acts as the identifier for the set of values
to be tracked
set of values – are the identified set of values to track
count- during the simulation run, if a variable for which the
coverpoint defined matches the values in
the set of values, it is incremented by 1
Specify a set of values or range of values:

{ [0:5],10 } - defines values 0-5 and 10


{ [0:5], [9:14] }- defines 0-5 and 9-14
{‘h1, ‘h2, ‘hF }- define three values 1, 2, 14
{ [1:9], [7:12] } – two ranges, overlap is allowed

34
0
Cover_cross ::= cross list_of_coverpoints

sel_bins_or_empty List_of_coverpoints ::=


cross_item, cross_item {, cross_item} Cross_item
::= coverpoint_id | variable_id
Sel_bins_or_empty :=
{ { bins_or_options;

}}

; Bins_or_options ::=
cross_auto_bin_max

 Currently only automatic bins are


available for crosses in IUS
 Cross point labels are optional :
Cross AB: cross a,b,c, ……;

Labels are generated automatically for unlabelled


cross points.
System Verilog Assertions( SVA)

Agenda
 Introduction
 Types of assertions
◦ Immediate
◦ Concurrent
 Writing properties  Sequences
 Sequence composition
◦ And, or, intersect
 Advanced SVA features
◦ Expect, binding
 Assertion Coverage
 An assertion specifies a behavior of the system.
 Assertions are primarily used to validate the behavior of a design. ("Is it working
correctly?“)
 Assertions can be used to capture the information about various level of properties.
 assertions can be used to provide functional coverage and generate input stimulus for
validation.

What is an Assertion? Assertion Based Verification (ABV)


 Assertions increase the observability and the controllability of a design.
 Catch bugs at or near the source of problem.
 An embedded check in RTL code – Executable specification
 During simulation, assertions monitor whether: ◦ A specific condition occurs, or
◦ A specific sequence of events ocuurs.
 Assertions produce warnings or errors when:
◦ A specified condition fails, or
◦ A specified sequence does not complete properly.
 Assertions monitor and report :
◦ Expected behaviour
◦ Forbidden behaviour
◦ Signal protocols
Assertions are primarily used to validate the behavior of a design. An assertion is a check
embedded in, or bound to a design unit during simulation.Warnings or errors are generated on
failure of specific condition or sequence of events.
Assertions are used to,
 Check the occurrence of specific condition or sequence of events.
 Provide functional coverage.
There are two kinds of assertions:
 Immediate Assertions
 Concurrent Assertions
Performance of ABV:
 Assertions depend on the quality of stimulus.
 Formal verification ABV – more coverage and quality.
 ssertions based verification flow provides
 Find bugs faster with assertions.
 Find more bugs with verification hot spots.
 Reduce simulation cycles.
Advantages with Assertions- Issues with Assertions
 Improving Observability.
 Reduces the debug time.
 Bugs can be found earlier and are more isolated.
 Controllable severity level.
 Can interact with C functions.
 Describe the Documentation and Specification of the design.
 A test bench and test stimulus are still necessary.
 Quality of test stimulus is still critical.
 ◦Assertions are only useful if exercised by the test stimulus.
 Assertions must be defined carefully.
 Incorrect assertions can give misleading results.
 Debugging an assertion can be difficult.
 How do we know when enough assertions have been written?
 Simulation overhead.
Types of Assertions:
 SystemVerilog Assertions can be classified into two types:
 Immediate Assertions : Follow simulation event semantics, like code in always block.
 Concurrent Assertions : Based on clock semantics, like always block with clock.
Immediate Assertions:
Immediate assertions check for a condition at the current simulation time.

An immediate assertion is same as an if..else statement, but with assertion control.


Immediate assertions have to be placed in a procedural block definition.

A simple immediate assertion is shown below,


The always block executes if either signal "a" or signal "b" changes.
always a: assert (a && b ) ;

assert can be used as below,

assert(condition) $display(“Condition is True”); else $display(“Condition is False”);

assert(condition) $display(“Condition is True”);

assert(condition) $(“Condition is True”); else $fatal(“Condition is False”);

assert(condition)
else begin
…….
…….
$fatal(“Condition is False”);
end

assert(condition) else $warning(“Condition is False”);

label: assert(condition) else $warning(“Condition is False”);


If an assertion fails and no else clause is specified, the tool shall, by default call $error

 The immediate assertion statement is a test of an expression performed when the statement is
executed in the procedural code.
 Works like an if statement.
 if the expression evaluates to X, Z or 0, then it is interpreted as being false and the assertion
is said to fail.
 Ex: assertion_label : assert (expression) pass block code;
else fail block code;
Where:
 assertion_label : User defined assertion label.
 assert : SystemVerilog reserve word, used for assertion.
 expression : Any valid verilog expression.
 pass block code : Code that gets executed when assertion passes.
 else : Optional syntax to specify failed code.
 fail block code : Code that gets executed when assertion fails.
Severity levels- Limitations of Immediate Assertions :
 SystemVerilog provides various assertions levels for reporting messages as listed below.
 $fatal is a run-time fatal.
 $error is a run-time error.
 $warning is a run-time warning, which can be suppressed in a tool-specific manner.
 $info indicates that the assertion failure carries no specific severity.

 One can use $display, or any regular verilog code, like triggering a event, or incrementing a
counter, or calling function in pass/fail block code.
 Instantaneous boolean check only.
 xtra code required around assertion for complex or temporal checks.
 Complexity becomes comparable to that of design.
 Requires as much debugging as design.
 Poor readability as design intent is not obvious.
Example:
 Ex: always@(posedge clk) case(state)
`state1: …
`state2:

default:
adef: assert(1’b0)
$warning(“case default”);

endcase
Assertion just used to report problem.

Concurrent Assertions: Concurrent assertions check the sequence of events spread over
multiple clock cycles.

 Concurrent assertion is evaluated only at the occurrence of a clock tick.


 Test expression is evaluated at clock edges based on the sampled values of the variables
involved..
 Can be placed in a procedural block, a module, an interface or a program definition.

c_assert: assert property(@(posedge clk) not(a &&


b));

The Keyword differentiates the immediate assertion from the concurrent assertion is "property."
 Concurrent assertion is evaluated only at the occurrence of a clock tick.
 The keyword property distinguishes a concurrent assertion from an immediate assertion.
 The values of variables used in assertions are sampled in the Preponed region of a time slot
and the assertions are evaluated during the Observe region.
 Concurrent assertions may also be used as statements in initial or always blocks.
A concurrent assertion in an initial block is only tested on the first clock tick

 Design behavior:
◦ Signals Read and Write are never both high at the positive edge of clock.

Building blocks of SVA


Boolean expressions:

The functionality is represented by the combination of multiple logical events. These events
could be simple Boolean expressions.
Sequence:

Boolean expression events that evaluate over a period of time involving single/multiple clock
cycles. SVA provides a key word to represent these events called "sequence."

syntax:

sequence name_of_sequence;

……

endsequence
Property:

A number of sequences can be combined logically or sequentially to create more complex


sequences. SVA provides a key word to represent these complex sequential behaviors called
"property."

syntax :

property name_of_property;

test expression or

complex sequence expressions

endproperty
Assert:

The property is the one that is verified during a simulation. It has to be asserted to take effect
during a simulation. SVA provides a key word called "assert" to check the property.

syntax:

assertion_ name: assert_property( property_name );


The steps involved in the creation of a SVA checker,
SystemVerilog Assertions

SystemVerilog assertion Sequence


SVA Sequence

Sequence seq_1 checks that the signal "a" is high on every positive edge of the clock. If signal
"a" is not high on any positive clock edge, the assertion will fail.

sequence seq_1;

@(posedge clk) a==1;

endsequence

Sequence with logical relationship:

Below sequence seq_2 checks that on every positive edge of the clock, either signal "a" or signal
"b" is high. If both the signals are low, the assertion will fail.

sequence seq_2;
@(posedge clk) a || b;
endsequence
Sequence Expressions:
By defining arguments in a sequence definition, the same sequence can be re-used for the similar
behavior.

For example, we can define a sequence as follows.

sequence seq_lib (a, b)


a || b ;
endsequence

this seq can be used as,

sequence s_lib_inst
seq_lib(req1,req2);
endsequence

Sequences with timing relationship:

In SVA, clock cycle delays are represented by a "##" sign. For example, ##2 means 2 clock
cycles.

Below sequence checks for the signal "a" being high on a given positive edge of the clock. If
signal "a" is not high, then the sequence fails. If signal "a" is high on any given positive edge of
clock, then signal "b" should be high 2 clock cycles after that. If signal "b" is not asserted after 2
clock cycles, the assertion fails.

sequence seq;
@(posedge clk) a ##2 b;
Endsequence
Note :: sequence begins when signal "a" is high on a positive edge of the clock.

A clock can be specified in a sequence, in a property or even in an assert statement.


clock defined in the sequence definition:

Clock defined in the property definition:

sequence seq;
a ##2 b;
endsequence
property p;
@(posedge clk) seq;
endproperty
a_1 : assert property(p);
In general, it is a good idea to define the clocks in property definitions and keep the sequences
independent of the clocks. This will help increase the re-use of the basic sequence definitions.
A separate property definition is not needed to assert a sequence. the expression to be checked
can be
called from the assert statement directly as shown below.

Forbidding a property
In all the examples shown so far, the property is checking for a true condition. we expect the
property to be false always. If the property is true, the assertion fails.

sequence seq;
a ##2 b;
endsequence
a_2: assert property(@(posedge clk) seq);
Calling a property with a clock definition from within the assert statement is not allowed.
a_3: assert property(@(posedge clk) p) ; //Not allowed

Below sequence checks that if signal "a" is high on a given positive edge of the clock, then after
2 clock cycles, signal "b" shall not be high. The keyword "not" is used to specify that the
property should never be true.

sequence seq;
@(posedge clk) a ##2 b;
endsequence

property p;
not seq;
endproperty
a_1: assert property(p);
SystemVerilog Implication operator
Implication Operator

sequence seq;

@(posedge clk) a ##2 b;

endsequence
In the above sequence we can observe that, sequence starts on every positive edge of the clock
and it looks for "a" to be high on every positive clock edge. If signal "a" is not high on any
given positive clock edge, an error is issued by the checker.

If we want sequence to be checked only after “a” is high, this can achieved by using implication
operator.

Implication is equivalent to an if-then structure. The left hand side of the implication is called
the "antecedent" and the right hand side is called the "consequent." The antecedent is the gating
condition. If the antecedent succeeds, then the consequent is evaluated.

The implication construct can be used only with property definitions. It cannot be used in
sequences.

There are 2 types of implication:

 Overlapped implication
 Non-overlapped implication
Overlapped implication

Overlapped implication is denoted by the symbol |->.

If there is a match on the antecedent, then the consequent expression is evaluated in the same
clock cycle.
Below property checks that, if signal "a" is high on a given positive clock edge, then signal "b"
should also be high on the same clock edge.

property p;

@(posedge clk) a |-> b;

endproperty

a: assert property(p);
Non-overlapped implication

Non-Overlapped implication is denoted by the symbol |=>.

If there is a match on the antecedent, then the consequent expression is evaluated in the next
clock cycle.
Below property checks that, if signal "a" is high on a given positive clock edge, then signal "b"
should be high on the next clock edge.

property p;

@(posedge clk) a |=> b;

endproperty

a: assert property(p);
Implication with a fixed delay on the consequent :

Below property checks that, if signal "a" is high on a given positive clock edge, then signal "b"
should be high after 2 clock cycles.
property p;

@(posedge clk) a |-> ##2 b;

endproperty

a: assert property(p);
Implication with a sequence as an antecedent :
Below property checks that, if the sequence seq_1 is true on a given positive edge of the clock,
then starts checking the seq_2 (“d” should be low, 2 clock cycles after seq_1 is true ) .

sequence seq_1;
(a && b) ##1 c;
endsequence

sequence seq_2;
##2 !d;
endsequence

property p;
@(posedge clk) seq_1 |-> seq_2;
endpeoperty
a: assert property(p);

Timing windows in SVA Checkers:

Below property checks that, if signal "a" is high on a given positive clock edge, then within 1 to
4 clock cycles, the signal "b" should be high.

property p;
@(posedge clk) a |-> ##[1:4] b;
endproperty
a: assert property(p);

Overlapping timing window:

Below property checks that, if signal "a" is high on a given positive clock edge,then signal "b"
should be high in the same clock cycle or within 4 clock cycles.

property p;
@(posedge clk) a |-> ##[0:4] b;
endproperty
a: assert property(p);

Indefinite timing window:


The upper limit of the timing window specified in the right hand side can be defined with a "$"
sign which implies that there is no upper bound for timing. This is called the "eventuality"
operator. The checker will keep checking for a match until the end of simulation.

Below property checks that, if signal "a" is high on a given positive clock edge, then signal "b"
will be high eventually starting from the next clock cycle.

property p;
@(posedge clk) a |-> ##[1:$] b;
endproperty
a: assert property(p);
SystemVerilog Repetition operators
Repetition Operators

property p;

@(posedge clk) a |-> ##1 b ##1 b ##1 b;

endproperty

a: assert property(p);
The above property checks that, if the signal “a” is high on given posedge of clock, then signal
“b” should be high for 3 consecutive clock cycles.

The Consecutive repetition operator is used to specify that a signal or a sequence will match
continuously for the number of clocks specified.

Syntax
signal [*n] or sequence [*n]
"n" is the number of repetitions.
with repetition operator above sequence can be re-written as,

property p;

@(posedge clk) a |-> ##1 b[*3];

endproperty

a: assert property(p);
go-to repetition

The go-to repetition operator is used to specify that a signal will match the number of times
specified not necessarily on continuous clock cycles.

Signal [->n]
property p;

@(posedge clk) a |-> ##1 b[->3] ##1 c;

endproperty

a: assert property(p);
the above property checks that, if the signal “a” is high on given posedge of clock, then signal
“b” should be high for 3 clock cycles followed by “c” should be high after ”b” is high for third
time.

Non-consecutive repetition

This is very similar to "go to" repetition except that it does not require that the last match on the
signal repetition happen in the clock cycle before the end the entire sequence matching.

Signal [=n]

Only expressions are allowed to repeat in "go to" and "nonconsecutive"


repetitions. Sequences are not allowed.

SystemVerilog SVA built in methods


SVA Methods

$rose

$rose(boolean expression or signalname)

returns true if the least significant bit of the expression changed to 1. Otherwise, it returns false.

sequence seq_rose;

@(posedge clk) $rose(a);

endsequence
Sequence seq_rose checks that the signal "a" transitions to a value of 1 on every positive edge of
the clock. If the transition does not occur, the assertion will fail.

$fell

$fell( boolean expression or signalname) returns true if the least significant bit of the expression
changed to 0. Otherwise, it returns false.

sequence seq_fell;

@(posedge clk) $fell(a);

endsequence

Sequence seq_fell checks that the signal "a" transitions to a value of 0 on every positive edge of
the clock. If the transition does not occur, the assertion will fail.
$stable

$stable(boolean expression or signalname) returns true if the value of the expression did not
change. Otherwise, it returns false.

sequence seq_stable;

@(posedge clk) $stable(a);

endsequence
Sequence seq_stable checks that the signal "a" is stable on every positive edge of the clock. If the
there is any transition occur, the assertion will fail.

$past

$past(signal_name, number of clock cycles) provides the value of the signal from the previous
clock cycle.

Below Property checks that, in the given positive clock edge, if the “b” is high, then 2 cycles
before that, a was high.

property p;

@(posedge clk) b |-> ($past(a,2) == 1);

endproperty

a: assert property(p);
$past construct with clock gating :
The $past construct can be used with a gating signal. on a given clock edge, the gating signal has
to be true even before checking for the consequent condition.

Spast (signal_name, number of clock cycles, gating signal)

Below Property checks that, in the given positive clock edge, if the “b” is high, then 2 cycles
before that, a was high only if the gating signal "c' is valid on any given positive edge of the
clock.

Property p;
@(posedge clk) b |-> ($past(a,2,c) == 1);
endproperty
a: assert property(p);
Built-in system functions

$onehot(expression)
- checks that only one bit of the expression can be high on any given clock edge.

$onehot0(expression)
- checks only one bit of the expression can be high or none of the bits can be high on any given
clock edge.

$isunknown(expresslon)
- checks if any bit of the expression is X or Z.

$countones(expression)
- counts the number of bits that are high in a vector.

a_1: assert property( @(posedge clk) $onehot(state) );


a_2: assert property( @(posedge clk) $onehot0(state) );
a_3: assert property( @(posedge clk) $isunknown(bus) ) ;
a_4: assert property( @(posedge clk) $countones(bus)> 1 );

Assert statement a_1 checks that the bit vector "state" is one-hot.
Assert statement a_2 checks that the bit vector "state" is zero one-hot.
Assert statement a_3 checks if any bit of the vector "bus" is X or Z.
Assert statement a_4 checks that the number of ones in the vector "bus" is greater than one.
SystemVerilog disable iff and ended construct
disable iff

In certain design conditions, we don't want to proceed with the check if some condition is true.
this can be achieved by using disable iff.

Below property checks that, if the signal “a” is high on given posedge of clock, then signal “b”
should be high for 3 clock cycles followed by “c” should be high after ”b” is high for third time.
During this entire sequence, if reset is detected high at any point, the checker will stop.

property p;

@(posedge clk)

disable iff (reset) a |-> ##1 b[->3] ##1 c;

endproperty

a: assert property(p);
ended

while concatenating the sequences, ending point of the sequence can used as a synchronization
point.

This is expressed by attaching the keyword "ended" to a sequence name.

sequence seq_1;

(a && b) ##1 c;

endsequence

sequence seq_2;

d ##[4:6] e;

endsequence

property p;

@(posedge clk) seq_1.ended |-> ##2 seq_2.ended;

endpeoperty

a: assert property(p);
Above property checks that, sequence seq_1 and SEQ_2 match with a delay of 2 clock cycle in
between them. the end point of the sequences does the synchronization.
Simple Assertions

SVA Layers
Steps Involved in writing Assertions:

Create Boolean Expressions


Create Sequence Expressions
Create Property

20
5

Assert Property
System Verlog Scheduling Semantics:

This section gives an overview of the interactions and behavior of SystemVerilog elements,
especially with respect to the scheduling and execution of events.

Updates to IEEE STD 1800-20051 divide the SystemVerilog time slot into 17 ordered
regions, nine ordered regions for execution of SystemVerilog statements and eight ordered
regions for execution of PLI code. The purpose of dividing a time slot into these ordered
regions is to provide predictable interactions between the design and testbench code.

Use of Event scheduling

Every change in state of a net or variable in the system description being simulated is
considered an update event. When an update event is executed, all the processes that are
sensitive to those events are considered for evaluation known as an evaluation event.
Example of processes include, initial, always, always_comb, always_latch, and always_ff
procedural blocks, continuous assignments, asynchronous tasks, and procedural
assignment statements.

20
1
A single time slot is divided into multiple regions where events can be scheduled. This event
scheduling supports in obtaining a clear and predictable interactions that provide for an
ordering of particular types of execution.

This allows properties and checkers to sample data when the design under test is in a
stable state. Property expressions can be safely evaluated, and testbenches can react to
both properties and checkers with zero delay, all in a predictable manner.

This same mechanism also allows for non-zero delays in the design, clock propagation,
and/or stimulus and response code to be mixed freely and consistently with cycle accurate
descriptions.

Note:
1. The term simulation time is used to refer to the time value maintained by the simulator to
model the actual time it would take for the system description being simulated.

2. A time slot include all simulation activity that is processed in the event regions for each
simulation time SystemVerilog event Regions The new SystemVerilog event regions are
developed to support new SystemVerilog constructs and also to prevent race conditions
being created between the RTL design and the new verification constructs.
These new regions guarantee predictability and consistency between design, testbenches,
and assertions

Preponed region

The values of variables that are used in concurrent assertions are sampled in Preponed
region. (Evaluation is done at observed region). Preponed region is executed only once in
each time slot, immediately after advancing simulation time.

Pre-active region

The Pre-active region is specifically for a PLI callback control point that allows for user code
to read and write values and create events before events in the Active region are evaluated

Active region

The Active region holds current events being evaluated and can be processed in any order.

 Execute all module blocking assignments.


 Evaluate the Right-Hand-Side (RHS) of all nonblocking assignments and schedule
updates into the NBA region.
 Execute all module continuous assignments
 Evaluate inputs and update outputs of Verilog primitives.
 Execute the $display and $finish commands.

Inactive region
The Inactive region holds the events to be evaluated after all the active events are
processed. In this region #0 blocking assignments are scheduled.

Pre-NBA region

The Pre-NBA region is specifically for a PLI callback control point that allows for user code
to read and write values and create events before the events in the NBA region are
evaluated

Non-blocking Assignment Events region (NBA)

The principal function of this region is to execute the updates to the Left-Hand-Side (LHS)
variables that were scheduled in the Active region for all currently executing nonblocking
assignments.

Post-NBA region

The Post-NBA region is specifically for a PLI callback control point that allows for user code
to read and write values and create events after the events in the NBA region are evaluated

Observed region

The principal function of this region is to evaluate the concurrent assertions using the values
sampled in the Preponed region. A criterion behind this decision is that the property
evaluations must only occur once in any clock triggering time slot. During the property
evaluation, pass/fail code shall be scheduled in the Reactive region of the current time slot.

Post-observed region

The Post-observed region is specifically for a PLI callback control point that allows for user
code to read values after properties are evaluated (in Observed or earlier region).

Reactive region

Code specified in the program block, and pass/fail code from property expressions, are
scheduled in the Reactive region. The principal function of this region is to evaluate and
execute all current program activity in any order
 Execute all program blocking assignments.
 Execute the pass/fail code from concurrent assertions.
 Evaluate the Right-Hand-Side (RHS) of all program nonblocking assignments and
schedule
 Execute all program continuous assignments
 Execute the $exit and implicit $exit commands

Re-Inactive Events region


In this region #0 blocking assignments in a program process are scheduled.

Postponed Region

The principal function of this region is to execute the $strobe and $monitor commands that
will show the final updated values for the current time slot. This region is also used to collect
functional coverage for items that use strobe sampling.
System Verilog Environment/Test Bench
Verification Environment or Testbench is used to check the functional correctness of
the Design Under Test (DUT) by generating and driving a predefined input sequence to a
design, capturing the design output and comparing with-respect-to expected output.

Verification environment is group of class's performing specific operation. i.e, generating


stimulus, driving, monitoring etc. and those class will be named based on the operation.

Components of typical Verification Environment / TestBench

Name Type Description

Defines the pin level activity generated by agent (to drive to DUT through
the driver) or the activity has to be observed by agent (Placeholder for
transaction class
the activity monitored by monitor on DUT signals).

Generates the stimulus (create and randomize the transaction class) and
generator class
send it to Driver.
Receives the stimulus (transaction) from generator and drives the packet
driver class
level data inside transaction into pin level (to DUT).
Observes pin level activity on interface signals and converts into packet
monitor class
level which is sent to the components such as scoreboard.
Agent is container class, which groups the class’s (generator, driver and
agent class
monitor) specific to an interface or protocol.

Receives data items from monitors and compares with expected values.
scoreboard class Expected values can be either golden reference values or generated from
reference model.

The environment is a container class for grouping higher level


environment class
components like agent's and scoreboard.
Test is responsible for,
 Configuring the testbench.
test program
 Initiate the testbench components construction process.
 Initiate the stimulus driving.
This is the top most file, which connects the DUT and TestBench. It
testbench_top module consists of DUT, Test and interface instances, interface connects the DUT
and TestBench.

Test Bench Hierarchy:


System Verilog TestBench Example – Adder

Lets Write the SystemVerilog TestBench for the simple design "ADDER".

Before writing the SystemVerilog TestBench, will look into the design specification.

ADDER:
Below is the block diagram of ADDER.

"Adder" Design block diagram


Adder is,
 fed with the inputs clock, reset, a, b and valid.
 has output is c.

Valid signal indicates the valid value on the a and b, On valid signal adder will add the a and b,
drives the result in next clock on c.

Adder add/Sum the 4bit values 'a' and 'b', and drives the result on c in the next clock.

waveform diagram:

"Adder" Waveform
Transaction Class
class transaction;

//declaring the transaction items

rand bit [3:0] a;

rand bit [3:0] b;

bit [6:0] c;

function void display(string name);

$display("-------------------------");

$display("- %s ",name);

$display("-------------------------");

$display("- a = %0d, b = %0d",a,b);

$display("- c = %0d",c);

$display("-------------------------");

endfunction

endclass

INPUT GENERATOR:
class generator;

//declaring transaction class

rand transaction trans;

//repeat count, to specify number of items to generate

int repeat_count;

//mailbox, to generate and send the packet to driver

mailbox gen2driv;

//event, to indicate the end of transaction generation

event ended;
//constructor

function new(mailbox gen2driv);

//getting the mailbox handle from env, in order to share the transaction packet between the
generator and driver, the same mailbox is shared between both.

this.gen2driv = gen2driv;

endfunction

//main task, generates(create and randomizes) the repeat_count number of transaction packets
and puts into mailbox

task main();

repeat(repeat_count) begin

trans = new();

if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");

trans.display("[ Generator ]");

gen2driv.put(trans);

end

-> ended; //triggering indicatesthe end of generation

endtask

endclass

DRIVER :
class driver;

//used to count the number of transactions

int no_transactions;

//creating virtual interface handle

virtual intf vif;

//creating mailbox handle


mailbox gen2driv;

//constructor

function new(virtual intf vif,mailbox gen2driv);

//getting the interface

this.vif = vif;

//getting the mailbox handles from environment

this.gen2driv = gen2driv;

endfunction

//Reset task, Reset the Interface signals to default/initial values

task reset;

wait(vif.reset);

$display("[ DRIVER ] ----- Reset Started -----");

vif.a <= 0;

vif.b <= 0;

vif.valid <= 0;

wait(!vif.reset);

$display("[ DRIVER ] ----- Reset Ended -----");

endtask

//drivers the transaction items to interface signals

task main;

forever begin

transaction trans;

gen2driv.get(trans);

@(posedge vif.clk);

vif.valid <= 1;
vif.a <= trans.a;

vif.b <= trans.b;

@(posedge vif.clk);

vif.valid <= 0;

trans.c = vif.c;

@(posedge vif.clk);

trans.display("[ Driver ]");

no_transactions++;

end

endtask

endclass

MONITOR:
class monitor;

//creating virtual interface handle

virtual intf vif;

//creating mailbox handle

mailbox mon2scb;

//constructor

function new(virtual intf vif,mailbox mon2scb);

//getting the interface

this.vif = vif;

//getting the mailbox handles from environment

this.mon2scb = mon2scb;

endfunction

//Samples the interface signal and send the sample packet to scoreboard
task main;

forever begin

transaction trans;

trans = new();

@(posedge vif.clk);

wait(vif.valid);

trans.a = vif.a;

trans.b = vif.b;

@(posedge vif.clk);

trans.c = vif.c;

@(posedge vif.clk);

mon2scb.put(trans);

trans.display("[ Monitor ]");

end

endtask

endclass

SCOREBOARD:
class scoreboard;

//creating mailbox handle

mailbox mon2scb;

//used to count the number of transactions

int no_transactions;

//constructor

function new(mailbox mon2scb);


//getting the mailbox handles from environment

this.mon2scb = mon2scb;

endfunction

//Compares the Actual result with the expected result

task main;

transaction trans;

forever begin

mon2scb.get(trans);

if((trans.a+trans.b) == trans.c)

$display("Result is as Expected");

else

$error("Wrong Result.\n\tExpeced: %0d Actual: %0d",(trans.a+trans.b),trans.c);

no_transactions++;

trans.display("[ Scoreboard ]");

end

endtask

endclass

ENVIRONMENT:
`include "transaction.sv"

`include "generator.sv"

`include "driver.sv"

`include "monitor.sv"
`include "scoreboard.sv"

class environment;

//generator and driver instance

generator gen;

driver driv;

monitor mon;

scoreboard scb;

//mailbox handle's

mailbox gen2driv;

mailbox mon2scb;

//virtual interface

virtual intf vif;

//constructor

function new(virtual intf vif);

//get the interface from test

this.vif = vif;

//creating the mailbox (Same handle will be shared across generator and driver)

gen2driv = new();

mon2scb = new();

//creating generator and driver

gen = new(gen2driv);

driv = new(vif,gen2driv);

mon = new(vif,mon2scb);

scb = new(mon2scb);
endfunction

//

task pre_test();

driv.reset();

endtask

task test();

fork

gen.main();

driv.main();

mon.main();

scb.main();

join_any

endtask

task post_test();

wait(gen.ended.triggered);

wait(gen.repeat_count == driv.no_transactions); //Optional

wait(gen.repeat_count == scb.no_transactions);

endtask

//run task

task run;

pre_test();

test();

post_test();

$finish;
endtask

endclass

PROGRAM BLOCK(RANDOM_TEST):
`include "environment.sv"

program test(intf i_intf);

//declaring environment instance

environment env;

initial begin

//creating environment

env = new(i_intf);

//setting the repeat count of generator as 4, means to generate 4 packets

env.gen.repeat_count = 4;

//calling run of env, it interns calls generator and driver main tasks.

env.run();

end

endprogram

PROGRAM BLOCK(DIRECT_TEST):
`include "environment.sv"

program test(intf i_intf);

class my_trans extends transaction;

bit [1:0] count;

function void pre_randomize();

a.rand_mode(0);
b.rand_mode(0);

a = 10;

b = 12;

endfunction

endclass

//declaring environment instance

environment env;

my_trans my_tr;

initial begin

//creating environment

env = new(i_intf);

my_tr = new();

//setting the repeat count of generator as 4, means to generate 4 packets

env.gen.repeat_count = 10;

env.gen.trans = my_tr;

//calling run of env, it interns calls generator and driver main tasks.

env.run();

end

endprogram

INTERFACE:
interface intf(input logic clk,reset);

//declaring the signals

logic valid;

logic [3:0] a;

logic [3:0] b;
logic [6:0] c;

endinterface

TESTBENCH:
//including interfcae and testcase files

`include "interface.sv"

//-------------------------[NOTE]---------------------------------

//Particular testcase can be run by uncommenting, and commenting the rest

`include "random_test.sv"

//`include "directed_test.sv"

//----------------------------------------------------------------

module tbench_top;

//clock and reset signal declaration

bit clk;

bit reset;

//clock generation

always #5 clk = ~clk;

//reset Generation

initial begin

reset = 1;

#5 reset =0;

end

//creatinng instance of interface, inorder to connect DUT and testcase

intf i_intf(clk,reset);

//Testcase instance, interface handle is passed to test as an argument

test t1(i_intf);
//DUT instance, interface signals are connected to the DUT ports

adder DUT (

.clk(i_intf.clk),

.reset(i_intf.reset),

.a(i_intf.a),

.b(i_intf.b),

.valid(i_intf.valid),

.c(i_intf.c)

);

//enabling the wave dump

initial begin

$dumpfile("dump.vcd"); $dumpvars;

end

endmodule

SystemVerilog TestBench Example -- Memory Model

SystemVerilog Verification Environment/TestBench for Memory Model

The steps involved in the verification process are,

 Creation of Verification plan

 Testbench Architecture

 Writing TestBench

Before writing/creating the verification plan need to know about design, so will go through the
design specification.

* In this example Design/DUT is Memory Model.


Memory Model Specification

Signal Defination:

Signal Name Direction wrt to Design Description

Clk input clock signal


Reset input reset signal
addr[1:0] input Address signal on which the
address is specified
wr_en input write enable signal,indicates
the write operation.

rd_en input read enable signal,indicates


the read operation.
wdata[7:0] input wdata signal for write data
rdata[7:0] output rdata signal for read data
Operations:

Write Operation:

address, wr_en and wdata should be driven at the same clock cycle.

Read Operation:

address and rd_en should be driven on the same clock cycle, Design will respond with the data in
the next clock cycle.

Design Features,

 The Memory model is capable of storing 8bits of data per address location.

 Reset values of each address memory location is ‘hFF .

Create Verification Plan

Verification plan is the list of scenarios need to be verified.

let’s list the few scenarios,

1. Write and Read to particular memory location.

o Perform write to any memory location, read from the same memory location, read
data should be same as written data

2. Write and Read to all memory location.

o Perform write and read to all the memory locations (as address is 2bit width the
possible address are 2‘b00, 2’b01, 2’b10 and 2’b11)

3. Default memory value check.

o Check default memory values. (before writing any locations, do read operation we
should get default values as ‘hFF)

4. Reset in Middle of Write/Read Operation.

o Assert reset in between write/read operation and check for default values. (after
writing to few locations assert the reset and perform read operation, we should get
default memory location value ‘hFF)

TestBench Hierarchy and Architecture


SystemVerilog testbench hierarchy to verify "Memory Model"

Testbench block diagram


Writing Verification Environment/TestBench

TestBench Architecture:

Transaction Class:

 Fields required to generate the stimulus are declared in the transaction class.
 Transaction class can also be used as placeholder for the activity monitored by monitor
on DUT signals.
 So, first step is to declare the 'Fields' in the transaction class.
 Below are the steps to write transaction class.
1. Declaring the fields.

class transaction;

//declaring the transaction items


bit [1:0] addr;
bit wr_en;
bit rd_en;
bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

endclass

2. To generate the random stimulus, declare the fields as 'rand'.


class transaction;

//declaring the transaction items


rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

endclass

3. Either write or read operation will be performed at once, so wr_en or rd_en is generated
by 'adding constraint'.

class transaction;

//declaring the transaction items


rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

//constaint, to generate any one among write and read


constraint wr_rd_c { wr_en != rd_en; };

endclass

Generator Class:

Generator class is responsible for,

 Generating the stimulus by randomizing the transaction class


 Sending the randomized class to driver

class generator;

------

endclass

1. Declare the transaction class handle,


class generator;

//declaring transaction class


rand transaction trans;

endclass

2. 'Randomize' the transaction class,


class generator;

//declaring transaction class


rand transaction trans;

//main task, generates(create and randomizes) the packets and puts into
mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
endtask

endclass

3. Mailbox is used to send the randomized transaction to Driver,

This involves,

 Declaring the Mailbox


 Getting the Mailbox handle from env class. ( because, same mailbox will be shared
across generator and driver)
class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction

//main task, generates(create and randomizes) the packets and puts into
mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
endtask

endclass

4. Adding variable to control the number of packets to be created,


class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//repeat count, to specify number of items to generate


int repeat_count;

//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction

//main task, generates(create and randomizes) the repeat_count number of


transaction packets and puts into mailbox
task main();
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
end
endtask

endclass

5. Adding event to indicate the completion of generation process, event will be triggered on the
completion of Generation process.

class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//repeat count, to specify number of items to generate


int repeat_count;
//event
event ended;

//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
this.ended = ended;
endfunction

//main task, generates(create and randomizes) the repeat_count number of


transaction packets and puts into mailbox
task main();

repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
end
-> ended;
endtask
endclass

Interface:
Interface will group the signals, specifies the direction (Modport) and Synchronize the
signals(Clocking Block).

interface mem_intf(input logic clk,reset);


----
endinterface

1. Driver Clocking Block,


//driver clocking block
clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking

2. Monitor Clocking Block,


//monitor clocking block
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking

3. Driver and Monitor modport,


//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);

//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);

4. Complete Interface code,


interface mem_intf(input logic clk,reset);

//declaring the signals


logic [1:0] addr;
logic wr_en;
logic rd_en;
logic [7:0] wdata;
logic [7:0] rdata;

//driver clocking block


clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking

//monitor clocking block


clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking

//driver modport
modport DRIVER (clocking driver_cb,input clk,reset);

//monitor modport
modport MONITOR (clocking monitor_cb,input clk,reset);

endinterface

Driver Class:
Driver class is responsible for,

 receive the stimulus generated from generator and drive to DUT by assigning
transaction class values to interface signals.
class driver;
----
endclass

1. Declare interface and mailbox, Get the interface and mailbox handle through constructor.
//creating virtual interface handle
virtual mem_intf mem_vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction

2. Adding reset task, which initializes the Interface signals to default values.

For simplicity, define is used to access interface signals.

`define DRIV_IF mem_vif.DRIVER.driver_cb

`DRIV_IF will point to mem_vif.DRIVER.driver_cb

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended ---------");
endtask

3. Adding drive task to drive the transaction packet to interface signal.


//drive the transaction items to interface signals
task drive;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
no_transactions++;
end
endtask

4. Adding local variable to track the number of packets driven, and increment the variable in
drive task.
(This will be useful to end the test-case/Simulation. i.e compare the generated pkt's and driven
pkt's, if both are same then end the simulation)

//used to count the number of transactions


int no_transactions;

//drive the transaction items to interface signals


task drive;
------
------
no_transactions++;
endtask

5. Complete driver code.


class driver;

//used to count the number of transactions


int no_transactions;

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended---------");
endtask

//drive the transaction items to interface signals


task drive;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
no_transactions++;
end
endtask

endclass

Environment:
Environment is container class contains Mailbox, Generator and Driver.

Creates the mailbox, generator and driver, shares the mailbox handle across the Generator and
Driver.

class environment;
---
endclass

1. Declare the handles,


//generator and driver instance
generator gen;
driver driv;

//mailbox handle's
mailbox gen2driv;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

2. In Construct Method, Create

 Mailbox
 Generator
 Driver
and pass the interface handle through new() method.

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();
//creating generator and driver
gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
endfunction

3. For better accessibility.


Generator and Driver activity can be divided and controlled in three methods.

 pre_test() - Method to call Initialization. i.e, reset method.


 test() - Method to call Stimulus Generation and Stimulus Driving.
 post_test() - Method to wait the completion of generation and driving.

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
join_any
endtask

task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

4. Add run task to call the above methods,


call $finish after post_test() to end the simulation.
task run;
pre_test();
test();
post_test();
$finish;
endtask

5. Complete environment class code.


`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
class environment;

//generator and driver instance


generator gen;
driver driv;
//mailbox handle's
mailbox gen2driv;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
endfunction

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
join_any
endtask

task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask

endclass
Test:
Test code is written with the program block.

Test is responsible for,

 Creating the environment.


 Configuring the testbench i.e, setting the type and number of transactions to be
generated.
 Initiating the stimulus driving.

program test;
----
endprogram

1. Declare and Create environment,


//declaring environment instance
environment env;

initial begin
//creating environment
env = new(intf);
end

2. Configure the number of transactions to be generated,


//setting the repeat count of generator as 10, means to generate 10 packets
env.gen.repeat_count = 10;

3. Initiating the stimulus driving,


//calling run of env, it interns calls generator and driver main tasks.
env.run();

4. Complete Test Code,


`include "environment.sv"
program test(mem_intf intf);

//declaring environment instance


environment env;

initial begin
//creating environment
env = new(intf);

//setting the repeat count of generator as 10, means to generate 10


packets
env.gen.repeat_count = 10;

//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram

TestBench Top:

 This is the top most file, which connects the DUT and TestBench.
 TestBench top consists of DUT, Test and Interface instances.
 Interface connects the DUT and TestBench.

module tbench_top;
---
endmodule

1.Declare and Generate the clock and reset,


//clock and reset signal declaration
bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;

//reset Generation
initial begin
reset = 1;
#5 reset =0;
End

2. Create Interface instance,


//creatinng instance of interface, inorder to connect DUT and testcase
mem_intf intf(clk,reset);

3. Create Design Instance and Connect Interface signals,


//DUT instance, interface signals are connected to the DUT ports
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);
4. Create test instance and Pass the interface handle,
//Testcase instance, interface handle is passed to test as an argument
test t1(intf);

5. Add logic to generate the dump,


initial begin
$dumpfile("dump.vcd"); $dumpvars;
end

6. Complete testbench top code,


`include "interface.sv"
`include "random_test.sv"

module tbench_top;

//clock and reset signal declaration


bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;

//reset Generation
initial begin
reset = 1;
#5 reset =0;
end

//creatinng instance of interface, inorder to connect DUT and testcase


mem_intf intf(clk,reset);

//Testcase instance, interface handle is passed to test as an argument


test t1(intf);

//DUT instance, interface signals are connected to the DUT ports


memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

//enabling the wave dump


initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule

Monitor :

 Samples the interface signals and convert the signal level activity to transaction level.
 Send the sampled transaction to Scoreboard via Mailbox.
 Below are the steps to write monitor.
1. Writing monitor class.

class monitor;
------

endclass

2. Declare interface and mailbox, Get the interface and mailbox handle through constructor.

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

3. Sampling logic and sending the sampled transaction to scoreboard.

task main;
forever begin
transaction trans;
trans = new();

@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if(`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask

4. Complete monitor code.

`define MON_IF mem_vif.MONITOR.monitor_cb


class monitor;

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual mem_intf mem_vif,mailbox mon2scb);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Samples the interface signal and send the sample packet to scoreboard
task main;
forever begin
transaction trans;
trans = new();

@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if(`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask
endclass

Scoreboard :

Scoreboard receive's the sampled packet from monitor,

 if the transaction type is "read", compares the read data with the local memory data.
 if the transaction type is "write", local memory will be written with the wdata.

class scoreboard;

------

endclass

1. Declaring the mailbox and variable to keep count of transactions, connecting handle through
constructor,
//creating mailbox handle
mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

2. logic to store wdata and compare rdata with stored data,

//stores wdata and compare rdata with stored data


task main;
transaction trans;
forever begin
#50;
mon2scb.get(trans);
if(trans.rd_en) begin
if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual =
%0h",trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h Actual
= %0h",trans.addr,mem[trans.addr],trans.rdata);
end
else if(trans.wr_en)
mem[trans.addr] = trans.wdata;

no_transactions++;
end
endtask

3. Complete scoreboard code.

class scoreboard;

//creating mailbox handle


mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//array to use as local memory


bit [7:0] mem[4];

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF;
endfunction

//stores wdata and compare rdata with stored data


task main;
transaction trans;
forever begin
#50;
mon2scb.get(trans);
if(trans.rd_en) begin
if(mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual
= %0h",trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h
Actual = %0h",trans.addr,mem[trans.addr],trans.rdata);
end
else if(trans.wr_en)
mem[trans.addr] = trans.wdata;

no_transactions++;
end
endtask
endclass

Environment:
Here only updates are mentioned. i.e adding monitor and scoreboard to previous example.

1. Declare the handles,


//generator and driver instance
generator gen;
driver driv;
monitor mon; //---NEW CODE---
scoreboard scb; //---NEW CODE---

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; //---NEW CODE---

//virtual interface
virtual mem_intf mem_vif;

2. In Construct Method, Create

 Mailbox (mon2scb)
 Monitor
 Scoreboard
and pass the interface handle through new() method.

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();
mon2scb = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_vif,mon2scb);
scb = new(mon2scb);
endfunction

3. Calling monitor and scoreboard tasks,

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
mon.main(); //---NEW CODE---
scb.main(); //---NEW CODE---
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
wait(gen.repeat_count == scb.no_transactions); //---NEW CODE---
endtask

4. Complete environment class code.

class environment;

//generator and driver instance


generator gen;
driver driv;
monitor mon;
scoreboard scb;

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

//creating the mailbox (Same handle will be shared across generator and
driver)
gen2driv = new();
mon2scb = new();

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_vif,mon2scb);
scb = new(mon2scb);
endfunction

//
task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
mon.main();
scb.main();
join_any
endtask

task post_test();
wait(gen_ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
wait(gen.repeat_count == scb.no_transactions);
endtask

//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask

endclass

Verification Environment Of FIFO

In this example, we verify a simple synchronous FIFO. Of course in real life we really don't get
to verify a FIFO model, as in companies this are generated using script.

This testbench will slightly different from what we have seen till now.
So the verification components are split into following blocks

 Push Generator
 Pop Generator
 Push Monitor
 Pop Monitor
 Scoreboard
 SystemVerilog testbench top
 SystemVerilog Interface file
 HDL Testbench top

We are going to have some more components that like reset. Push/Pop Driver, Push/Pop Monitor
are going to be part of fifo_driver.sv

Device Under Test

//-----------------------------------------------------
// Design Name : syn_fifo
// File Name : syn_fifo.v
// Function : Synchronous (single clock) FIFO
// //-----------------------------------------------------
module syn_fifo (
clk , // Clock input
rst , // Active high reset
wr_cs , // Write chip select
rd_cs , // Read chipe select
data_in , // Data input
rd_en , // Read enable
wr_en , // Write Enable
data_out , // Data Output
empty , // FIFO empty
full // FIFO full
);

// FIFO constants
parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 8;
parameter RAM_DEPTH = (1 << ADDR_WIDTH);
// Port Declarations
input clk ;
input rst ;
input wr_cs ;
input rd_cs ;
input rd_en ;
input wr_en ;
input [DATA_WIDTH-1:0] data_in ;
output full ;
output empty ;
output [DATA_WIDTH-1:0] data_out ;

//-----------Internal variables-------------------
reg [ADDR_WIDTH-1:0] wr_pointer;
reg [ADDR_WIDTH-1:0] rd_pointer;
reg [ADDR_WIDTH :0] status_cnt;
reg [DATA_WIDTH-1:0] data_out ;
wire [DATA_WIDTH-1:0] data_ram ;

//-----------Variable assignments---------------
assign full = (status_cnt == (RAM_DEPTH-1));
assign empty = (status_cnt == 0);

//-----------Code Start---------------------------
always @ (posedge clk or posedge rst)
begin : WRITE_POINTER
if (rst) begin
wr_pointer <= 0;
end else if (wr_cs && wr_en ) begin
wr_pointer <= wr_pointer + 1;
end
end

always @ (posedge clk or posedge rst)


begin : READ_POINTER
if (rst) begin
rd_pointer <= 0;
end else if (rd_cs && rd_en ) begin
rd_pointer <= rd_pointer + 1;
end
end

always @ (posedge clk or posedge rst)


begin : READ_DATA
if (rst) begin
data_out <= 0;
end else if (rd_cs && rd_en ) begin
data_out <= data_ram;
end
end

always @ (posedge clk or posedge rst)


begin : STATUS_COUNTER
if (rst) begin
status_cnt <= 0;
// Read but no write.
end else if ((rd_cs && rd_en) && !(wr_cs && wr_en)
&& (status_cnt != 0)) begin
status_cnt <= status_cnt - 1;
// Write but no read.
end else if ((wr_cs && wr_en) && !(rd_cs && rd_en)
&& (status_cnt != RAM_DEPTH)) begin
status_cnt <= status_cnt + 1;
end
end

ram_dp_ar_aw #(DATA_WIDTH,ADDR_WIDTH)DP_RAM (
.address_0 (wr_pointer) , // address_0 input
.data_0 (data_in) , // data_0 bi-directional
.cs_0 (wr_cs) , // chip select
.we_0 (wr_en) , // write enable
.oe_0 (1'b0) , // output enable
.address_1 (rd_pointer) , // address_q input
.data_1 (data_ram) , // data_1 bi-directional
.cs_1 (rd_cs) , // chip select
.we_1 (1'b0) , // Read enable
.oe_1 (rd_en) // output enable
);

endmodule

//-----------------------------------------------------
// Design Name : ram_dp_ar_aw
// File Name : ram_dp_ar_aw.v
// Function : Asynchronous read write RAM
////-----------------------------------------------------
module ram_dp_ar_aw (
address_0 , // address_0 Input
data_0 , // data_0 bi-directional
cs_0 , // Chip Select
we_0 , // Write Enable/Read Enable
oe_0 , // Output Enable
address_1 , // address_1 Input
data_1 , // data_1 bi-directional
cs_1 , // Chip Select
we_1 , // Write Enable/Read Enable
oe_1 // Output Enable
);

parameter DATA_WIDTH = 8 ;
parameter ADDR_WIDTH = 8 ;
parameter RAM_DEPTH = 1 << ADDR_WIDTH;

//--------------Input Ports-----------------------
input [ADDR_WIDTH-1:0] address_0 ;
input cs_0 ;
input we_0 ;
input oe_0 ;
input [ADDR_WIDTH-1:0] address_1 ;
input cs_1 ;
input we_1 ;
input oe_1 ;
//--------------Inout Ports-----------------------
inout [DATA_WIDTH-1:0] data_0 ;
inout [DATA_WIDTH-1:0] data_1 ;

//--------------Internal variables----------------
reg [DATA_WIDTH-1:0] data_0_out ;
reg [DATA_WIDTH-1:0] data_1_out ;
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];

//--------------Code Starts Here------------------


// Memory Write Block
// Write Operation : When we_0 = 1, cs_0 = 1
always @ (address_0 or cs_0 or we_0 or data_0
or address_1 or cs_1 or we_1 or data_1)
begin : MEM_WRITE
if ( cs_0 && we_0 ) begin
mem[address_0] <= data_0;
end else if (cs_1 && we_1) begin
mem[address_1] <= data_1;
end
end

// Tri-State Buffer control


// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_0 = (cs_0 && oe_0 && !we_0) ? data_0_out : 8'bz;

// Memory Read Block


// Read Operation : When we_0 = 0, oe_0 = 1, cs_0 = 1
always @ (address_0 or cs_0 or we_1 or oe_0)
begin : MEM_READ_0
if (cs_0 && !we_0 && oe_0) begin
data_0_out <= mem[address_0];
end else begin
data_0_out <= 0;
end
end

//Second Port of RAM


// Tri-State Buffer control
// output : When we_0 = 0, oe_0 = 1, cs_0 = 1
assign data_1 = (cs_1 && oe_1 && !we_1) ? data_1_out : 8'bz;
// Memory Read Block 1
// Read Operation : When we_1 = 0, oe_1 = 1, cs_1 = 1
always @ (address_1 or cs_1 or we_1 or oe_1)
begin : MEM_READ_1
if (cs_1 && !we_1 && oe_1) begin
data_1_out <= mem[address_1];
end else begin
data_1_out <= 0;
end
end

endmodule // End of Module ram_dp_ar_aw

HDL Testbench Top


`include "ram_dp_ar_aw.v"
`include "syn_fifo.v"

module fifo_tb();

parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 3;

reg clk;
wire rst, wr_cs, rd_cs;
wire rd_en, wr_en;
wire [DATA_WIDTH-1:0] data_in ;
wire full, empty;
wire [DATA_WIDTH-1:0] data_out ;

fifo_ports ports (
.clk (clk ),
.rst (rst ),
.wr_cs (wr_cs ),
.rd_cs (rd_cs ),
.rd_en (rd_en ),
.wr_en (wr_en ),
.data_in (data_in ),
.full (full ),
.empty (empty ),
.data_out (data_out)
);

fifo_monitor_ports mports (
.clk (clk ),
.rst (rst ),
.wr_cs (wr_cs ),
.rd_cs (rd_cs ),
.rd_en (rd_en ),
.wr_en (wr_en ),
.data_in (data_in ),
.full (full ),
.empty (empty ),
.data_out (data_out)
);

fifo_top top(ports,mports);

initial begin
$dumpfile("fifo.vcd");
$dumpvars();
clk = 0;
end

always #1 clk = ~clk;

syn_fifo #(DATA_WIDTH,ADDR_WIDTH) fifo(


.clk (clk), // Clock input
.rst (rst), // Active high reset
.wr_cs (wr_cs), // Write chip select
.rd_cs (rd_cs), // Read chipe select
.data_in (data_in), // Data input
.rd_en (rd_en), // Read enable
.wr_en (wr_en), // Write Enable
.data_out (data_out),// Data Output
.empty (empty), // FIFO empty
.full (full) // FIFO full
);

endmodule
SystemVerilog Testbench Top

`include "fifo_ports.sv"

program fifo_top (fifo_ports ports, fifo_monitor_ports mports);


`include "fifo_sb.sv"
`include "fifo_driver.sv"

fifo_driver driver = new(ports, mports);

initial begin
driver.go();
end

endprogram
FIFO Scoreboard

class fifo_sb;
mailbox fifo = new();
integer size;

function new();
begin
size = 0;
end
endfunction

task addItem(bit [7:0] data);


begin
if (size == 7) begin
$write("%dns : ERROR : Over flow detected, current occupancy %d\n",
$time, size);
end else begin
fifo.put(data);
size ++;
end
end
endtask

task compareItem (bit [7:0] data);


begin
bit [7:0] cdata = 0;
if (size == 0) begin
$write("%dns : ERROR : Under flow detected\n", $time);
end else begin
fifo.get (cdata);
if (data != cdata) begin
$write("%dns : ERROR : Data mismatch, Expected %x Got %x\n",
$time, cdata, data );
end
size --;
end
end
endtask
endclass
FIFO Driver

class fifo_driver;
fifo_sb sb;
virtual fifo_ports ports;
virtual fifo_monitor_ports mports;

bit rdDone;
bit wrDone;

integer wr_cmds;
integer rd_cmds;

function new (virtual fifo_ports ports, virtual fifo_monitor_ports mports);


begin
this.ports = ports;
this.mports = mports;
sb = new();
wr_cmds = 5;
rd_cmds = 5;
rdDone = 0;
wrDone = 0;
ports.wr_cs = 0;
ports.rd_cs = 0;
ports.wr_en = 0;
ports.rd_en = 0;
ports.data_in = 0;
end
endfunction

task monitorPush();
begin
bit [7:0] data = 0;
while (1) begin
@ (posedge mports.clk);
if (mports.wr_cs== 1 && mports.wr_en== 1) begin
data = mports.data_in;
sb.addItem(data);
$write("%dns : Write posting to scoreboard data = %x\n",$time, data);
end
end
end
endtask

task monitorPop();
begin
bit [7:0] data = 0;
while (1) begin
@ (posedge mports.clk);
if (mports.rd_cs== 1 && mports.rd_en== 1) begin
data = mports.data_out;
$write("%dns : Read posting to scoreboard data = %x\n",$time, data);
sb.compareItem(data);
end
end
end
endtask

task go();
begin
// Assert reset first
reset();
// Start the monitors
repeat (5) @ (posedge ports.clk);
$write("%dns : Starting Pop and Push monitors\n",$time);
fork
monitorPop();
monitorPush();
join_none
$write("%dns : Starting Pop and Push generators\n",$time);
fork
genPush();
genPop();
join_none

while (!rdDone && !wrDone) begin


@ (posedge ports.clk);
end
repeat (10) @ (posedge ports.clk);
$write("%dns : Terminating simulations\n",$time);
end
endtask

task reset();
begin
repeat (5) @ (posedge ports.clk);
$write("%dns : Asserting reset\n",$time);
ports.rst= 1'b1;
// Init all variables
rdDone = 0;
wrDone = 0;
repeat (5) @ (posedge ports.clk);
ports.rst= 1'b0;
$write("%dns : Done asserting reset\n",$time);
end
endtask

task genPush();
begin
bit [7:0] data = 0;
integer i = 0;
for ( i = 0; i < wr_cmds; i++) begin
data = $random();
@ (posedge ports.clk);
while (ports.full== 1'b1) begin
ports.wr_cs = 1'b0;
ports.wr_en = 1'b0;
ports.data_in= 8'b0;
@ (posedge ports.clk);
end
ports.wr_cs = 1'b1;
ports.wr_en = 1'b1;
ports.data_in= data;
end
@ (posedge ports.clk);
ports.wr_cs = 1'b0;
ports.wr_en = 1'b0;
ports.data_in= 8'b0;
repeat (10) @ (posedge ports.clk);
wrDone = 1;
end
endtask

task genPop();
begin
integer i = 0;
for ( i = 0; i < rd_cmds; i++) begin
@ (posedge ports.clk);
while (ports.empty== 1'b1) begin
ports.rd_cs = 1'b0;
ports.rd_en = 1'b0;
@ (posedge ports.clk);
end
ports.rd_cs = 1'b1;
ports.rd_en = 1'b1;
end
@ (posedge ports.clk);
ports.rd_cs = 1'b0;
ports.rd_en = 1'b0;
repeat (10) @ (posedge ports.clk);
rdDone = 1;
end
endtask
endclass

Ports File

`ifndef FIFO_PORTS_SV
`define FIFO_PORTS_SV

interface fifo_ports (
input wire clk ,
output logic rst ,
input wire full ,
input wire empty ,
output logic wr_cs ,
output logic rd_cs ,
output logic rd_en ,
output logic wr_en ,
output logic [7:0] data_in ,
input wire [7:0] data_out
);
endinterface

interface fifo_monitor_ports (
input wire clk ,
input wire rst ,
input wire full ,
input wire empty ,
input wire wr_cs ,
input wire rd_cs ,
input wire rd_en ,
input wire wr_en ,
input wire [7:0] data_in ,
input wire [7:0] data_out
);
endinterface

`endif

Verification Of UART

In this example, we verify a simple UART

This testbench will slightly different from what we have seen till now.

So the verification components are split into following blocks

 TX generator
 RX generator
 TX monitor
 RX monitor
 Scoreboard
 SystemVerilog testbench top
 SystemVerilog Ports file
 HDL Testbench top

We are going to have some more components that like reset. Logic to control when to terminate.

Device Under Test

//-----------------------------------------------------
// Design Name : uart
// File Name : uart.v
// Function : Simple UART
//
-----------------------------------------------------
module uart (
reset ,
txclk ,
ld_tx_data ,
tx_data ,
tx_enable ,
tx_out ,
tx_empty ,
rxclk ,
uld_rx_data ,
rx_data ,
rx_enable ,
rx_in ,
rx_empty
);
// Port declarations
input reset ;
input txclk ;
input ld_tx_data ;
input [7:0] tx_data ;
input tx_enable ;
output tx_out ;
output tx_empty ;
input rxclk ;
input uld_rx_data ;
output [7:0] rx_data ;
input rx_enable ;
input rx_in ;
output rx_empty ;

// Internal Variables
reg [7:0] tx_reg ;
reg tx_empty ;
reg tx_over_run ;
reg [3:0] tx_cnt ;
reg tx_out ;
reg [7:0] rx_reg ;
reg [7:0] rx_data ;
reg [3:0] rx_sample_cnt ;
reg [3:0] rx_cnt ;
reg rx_frame_err ;
reg rx_over_run ;
reg rx_empty ;
reg rx_d1 ;
reg rx_d2 ;
reg rx_busy ;

// UART RX Logic
always @ (posedge rxclk or posedge reset)
if (reset) begin
rx_reg <= 0;
rx_data <= 0;
rx_sample_cnt <= 0;
rx_cnt <= 0;
rx_frame_err <= 0;
rx_over_run <= 0;
rx_empty <= 1;
rx_d1 <= 1;
rx_d2 <= 1;
rx_busy <= 0;
end else begin
// Synchronize the asynch signal
rx_d1 <= rx_in;
rx_d2 <= rx_d1;
// Uload the rx data
if (uld_rx_data) begin
rx_data <= rx_reg;
rx_empty <= 1;
end
// Receive data only when rx is enabled
if (rx_enable) begin
// Check if just received start of frame
if (!rx_busy && !rx_d2) begin
rx_busy <= 1;
rx_sample_cnt <= 1;
rx_cnt <= 0;
end
// Start of frame detected, Proceed with rest of data
if (rx_busy) begin
rx_sample_cnt <= rx_sample_cnt + 1;
// Logic to sample at middle of data
if (rx_sample_cnt == 7) begin
if ((rx_d2 == 1) && (rx_cnt == 0)) begin
rx_busy <= 0;
end else begin
rx_cnt <= rx_cnt + 1;
// Start storing the rx data
if (rx_cnt > 0 && rx_cnt < 9) begin
rx_reg[rx_cnt - 1] <= rx_d2;
end
if (rx_cnt == 9) begin
rx_busy <= 0;
// Check if End of frame received correctly
if (rx_d2 == 0) begin
rx_frame_err <= 1;
end else begin
rx_empty <= 0;
rx_frame_err <= 0;
// Check if last rx data was not unloaded,
rx_over_run <= (rx_empty) ? 0 : 1;
end
end
end
end
end
end
if (!rx_enable) begin
rx_busy <= 0;
end
end

// UART TX Logic
always @ (posedge txclk or posedge reset)
if (reset) begin
tx_reg <= 0;
tx_empty <= 1;
tx_over_run <= 0;
tx_out <= 1;
tx_cnt <= 0;
end else begin
if (ld_tx_data) begin
if (!tx_empty) begin
tx_over_run <= 0;
end else begin
tx_reg <= tx_data;
tx_empty <= 0;
end
end
if (tx_enable && !tx_empty) begin
tx_cnt <= tx_cnt + 1;
if (tx_cnt == 0) begin
tx_out <= 0;
end
if (tx_cnt > 0 && tx_cnt < 9) begin
tx_out <= tx_reg[tx_cnt -1];
end
if (tx_cnt == 9) begin
tx_out <= 1;
tx_cnt <= 0;
tx_empty <= 1;
end
end
if (!tx_enable) begin
tx_cnt <= 0;
end
end

endmodule
HDL Testbench Top

`include "uart.v"
module top();

wire reset ;
wire ld_tx_data ;
wire [7:0] tx_data ;
wire tx_enable ;
wire tx_out ;
wire tx_empty ;
wire uld_rx_data ;
wire [7:0] rx_data ;
wire rx_enable ;
wire rx_in ;
wire rx_empty ;
wire loopback ;
wire rx_tb_in ;
reg txclk ;
reg rxclk ;

uart_ports ports (
.reset (reset ),
.txclk (txclk ),
.ld_tx_data (ld_tx_data ),
.tx_data (tx_data ),
.tx_enable (tx_enable ),
.tx_out (tx_out ),
.tx_empty (tx_empty ),
.rxclk (rxclk ),
.uld_rx_data (uld_rx_data ),
.rx_data (rx_data ),
.rx_enable (rx_enable ),
.rx_in (rx_in ),
.rx_empty (rx_empty ),
.loopback (loopback )
);

uart_top tbtop(ports);

initial begin
$dumpfile("uart.vcd");
$dumpvars();
txclk = 0;
rxclk = 0;
end
// Loopback control logic
assign rx_in = (loopback) ? tx_out : rx_tb_in;
// RX and TX Clock generation
always #1 rxclk = ~rxclk;
always #16 txclk = ~txclk;

// DUT Connected here


uart U (
.reset (reset),
.txclk (txclk),
.ld_tx_data (ld_tx_data),
.tx_data (tx_data),
.tx_enable (tx_enable),
.tx_out (tx_out),
.tx_empty (tx_empty),
.rxclk (rxclk),
.uld_rx_data (uld_rx_data),
.rx_data (rx_data),
.rx_enable (rx_enable),
.rx_in (rx_in),
.rx_empty (rx_empty)
);

endmodule
SV Testbench Top

`include "uart_ports.sv"

program uart_top (uart_ports ports);

`include "uart_sb.sv"
`include "uart_txgen.sv"

uart_txgen txgen = new(ports);

initial begin
fork
txgen.goTxgen();
join_none

while (!txgen.isDone()) begin


@ (posedge ports.txclk);
end
repeat (200) @ (posedge ports.txclk);
$write("%dns : Termintating the simulation\n",$time);
end
endprogram

UART Scoreboard

class uart_sb;
mailbox tx = new();
mailbox rx = new();

task txAdd(bit [7:0] data);


begin
tx.put(data);
$write("%dns : txAdd : Added data %x\n",$time, data);
end
endtask

task rxAdd(bit [7:0] data);


begin
rx.put(data);
$write("%dns : rxAdd : Added data %x\n",$time, data);
end
endtask
task txCompare(bit [7:0] data);
begin
bit [7:0] org_data;
tx.get(org_data);
if (data != org_data) begin
$write("%dns : txCompare : Error : Expected data %x, Got %x\n",
$time, org_data, data);
end else begin
$write("%dns : txCompare : Match : Expected data %x, Got %x\n",
$time, org_data, data);
end
end
endtask

task rxCompare(bit [7:0] data);


begin
bit [7:0] org_data;
rx.get(org_data);
if (data != org_data) begin
$write("%dns : rxCompare : Error : Expected data %x, Got %x\n",
$time, org_data, data);
end else begin
$write("%dns : rxCompare : Match : Expected data %x, Got %x\n",
$time, org_data, data);
end
end
endtask
endclass

UART Transcation Generator

class uart_txgen;
uart_sb sb;
virtual uart_ports ports;
bit tx_done;
bit rx_done;

// Connects the transmitter output to recevier input


bit loopback;
// Number of frames to send to transmitter
integer no_tx_cmds;
// Number of frames to send to receiver
integer no_rx_cmds;
// Delay the reading of data from receiver
bit rx_over_flow;
// Send frame to transmitter before it has sent out last frame
bit tx_over_flow;
// Insert framming error (stop bit) in frame sent to receiver
bit rx_frame_err;

function new (virtual uart_ports ports);


begin
this.ports = ports;
sb = new();
tx_done = 0;
rx_done = 0;
no_tx_cmds = 5;
no_rx_cmds = 5;
rx_over_flow = 0;
rx_frame_err = 0;
ports.rx_tb_in = 1;
ports.uld_rx_data = 0;
end
endfunction

// Main method, which starts rest of methods


task goTxgen();
begin
tx_done = 0;
rx_done = 0;
assertReset();
fork
txDriver();
rxDriver();
txMonitor();
rxMonitor();
join_none
end
endtask
// This method asserts method
task assertReset();
begin
@ (posedge ports.rxclk);
ports.reset = 1;
$write("%dns : Asserting reset to Uart\n",$time);
repeat (5) @ (posedge ports.rxclk);
ports.reset = 0;
end
endtask

task txDriver();
begin
integer i = 0;
integer tx_timeout = 0;
bit [7:0] tx_data = 0;
ports.tx_enable = 1;
for (i = 0; i < no_tx_cmds; i ++) begin
tx_data = $random();
sb.txAdd(tx_data);
if (loopback == 1) begin
sb.rxAdd(tx_data);
end
// Check if uart is ready to accept data for transmission
while (ports.tx_empty == 0) begin
@ (posedge ports.txclk);
tx_timeout ++ ;
if (tx_timeout > 10) begin
$write("%dns : txDriver : Warning : tx_empty is 0 for more then 10
clocks\n",
$time);
end
end
tx_timeout = 0;
// Drive the data in UART for transmitting
@ (posedge ports.txclk);
ports.ld_tx_data = 1;
ports.tx_data = tx_data;
$write("%dns : txDriver : Transmitting data %x\n",$time, tx_data);
@ (posedge ports.txclk);
ports.ld_tx_data = 0;
ports.tx_data = 0;
while (ports.tx_empty == 1) begin
@ (posedge ports.txclk);
tx_timeout ++ ;
if (tx_timeout > 10) begin
$write("%dns : txDriver : Warning : tx_empty is 1 for more then 10
clocks\n",
$time);
end
end
tx_timeout = 0;
end
tx_done = 1;
end
endtask

task rxDriver();
begin
bit [7:0] rx_data = 0;
integer i,j = 0;
ports.rx_enable = 1;
if (loopback == 1) begin
ports.loopback = 1;
end else begin
ports.loopback = 0;
for (i = 0; i < no_rx_cmds; i++) begin
rx_data = $random();
sb.rxAdd(rx_data);
$write("%dns : rxDriver : Transmitting data %x\n",$time, rx_data);
@ (posedge ports.txclk);
ports.rx_in = 0;
for (j = 0; j < 8; j ++) begin
@ (posedge ports.txclk);
ports.rx_in = rx_data[j];
end
@ (posedge ports.txclk);
ports.rx_in = 1;
@ (posedge ports.txclk);
end
end
rx_done = 1;
end
endtask

task txMonitor();
begin
bit [7:0] tx_data = 0;
integer i = 0;
while (1) begin
@ (posedge ports.txclk);