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

Intro To SystemVerilog

Uploaded by

Mo'men Sameh
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
303 views

Intro To SystemVerilog

Uploaded by

Mo'men Sameh
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 202

Restricted document for designated employees of Nvidia Corporation.

Provided for Nvidia on 17-May-2019.

Introduction to
SystemVerilog
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Introduction to
SystemVerilog
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Introduction to
SystemVerilog

Copyright © 2003 - 2014 by Doulos. All Rights Reserved


All intellectual property rights, including copyright, patents, design rights and know-how in or
relating to the course or course materials provided or made available in connection with the
course remain the sole property of Doulos Ltd or their respective owners and no copies may
be made of course materials unless expressly agreed in writing by Doulos Ltd.

All trademarks acknowledged.

Doulos takes great care in developing and maintaining materials to ensure they are an
effective and accurate medium for communicating design know-how. However, the
information provided on a Doulos training course may be out of date or include omissions,
inaccuracies or other errors. Except where expressly provided otherwise in agreement
between you and Doulos, all information provided directly or indirectly through a Doulos
training course is provided “as is” without warranty of any kind.

Doulos hereby disclaims all warranties with respect to this information, whether express or
implied, including the implied warranties of merchantability, satisfactory quality and fitness
for a particular purpose. In no event shall Doulos be liable for any direct, indirect, incidental
special or consequential damages, or damages for loss of profits, revenue, data or use,
incurred by you or any third party, whether in contract, tort or otherwise, arising for your
access to, use of, or reliance upon information obtained from or through a Doulos training
course. Doulos reserves the right to make changes, updates or corrections to the
information contained in its training courses at any time without notice.

Doulos Limited Doulos


Church Hatch, 22 Market Place, 2055 Gateway Place, Suite 220,
Ringwood, Hampshire, BH24 1AW, UK San Jose, CA 95110, USA

Tel: +44 (0) 1425 471223 Tel: 1-888-GO DOULOS


Email: [email protected] Email: [email protected]

Introduction to SystemVerilog 1.0


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Contents
1 Introduction ..................................................................................................3
2 Programming Language Features ................................................................
13
3 Basic Data Types .........................................................................................
29
4 Interfaces .....................................................................................................
43
5 RTL Processes .............................................................................................
61
6 RTL Types ....................................................................................................
75
7 Clocking Blocks ............................................................................................
93
8 Arrays and Queues ......................................................................................
113
9 Bus-Functional Modeling ..............................................................................
127
10 Randomization ........................................................................................
135
11 Coverage ......................................................................................................
147
12 Other Language Features ............................................................................
153
13 The Direct Programming Interface ...............................................................
169
14 Index ............................................................................................................
189

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 1


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

2 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
1
Provided for Nvidia on 17-May-2019.

Introduction
Introduction to SystemVerilog 1.0

Introduction

Aim

• To provide some introductory remarks on


SystemVerilog and look at the most basic
differences compared to Verilog

Topics covered

• What is SystemVerilog?
• Language evolution
• Language features
• Modules, ports, and parameters
• Standard verification methodologies
• References

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 3


1 Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction

What is SystemVerilog?

• The world’s first HDVL,


or Hardware Design and Verification Language

• IEEE 1800-2009 SystemVerilog is the merger of


IEEE Std 1364-2005 Verilog, and
IEEE Std 1800-2005 SystemVerilog

• SystemVerilog RTL, aka concise RTL

• SystemVerilog Assertions, aka SVA

• SystemVerilog Test Bench, or class-based verification

When it was first introduced, SystemVerilog was announced as the world’s first HDVL, or hardware
design and verification language. SystemVerilog is meant to encompass the features of both an HDL, i.e.
Verilog, and a language for functional verification.

The current version of the SystemVerilog standard resulted from the merger of the IEEE 1364-2005
Verilog standard and the IEEE 1800-2005 SystemVerilog standard. Officially, Verilog no longer exists as a
standalone language standard.

SystemVerilog is usually viewed as having three main components, namely SystemVerilog RTL,
SystemVerilog Assertions, and SystemVerilog Test Bench. Each of these three components go by various
names.

4 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
1
Provided for Nvidia on 17-May-2019.

Introduction
Introduction to SystemVerilog 1.0

Language Evolution
IEEE 1800
Classes SVA DPI SystemVerilog
(from OpenVera) (unified with PSL) (from SuperLog)

SystemVerilog 3.0
Interfaces
(from SuperLog)

VHDL C

Verilog 2005
Verilog 1995

SystemVerilog has evolved from the Verilog hardware language. At each stage in the process, new
features have been added; Verilog-1995, Verilog-2001 and Verilog-2005 remain as subsets of
SystemVerilog.

The original Verilog language shares some features with VHDL and C.

Verilog-2001 and Verilog-2005 added a few new features to the language, most of which come from
VHDL and C.

SystemVerilog 3.0 again borrows from VHDL and C – by now most of the features of these languages are
part of SystemVerilog – and adds interfaces and assertions.

SystemVerilog 3.1/a/1800 add features for writing testbenches, a new syntax for writing assertions and
new applications programming interfaces (APIs).

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 5


1 Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction

SystemVerilog versus Verilog


module blk #(parameter n = 1, m = n) (
input a, b, Both 4-state scalars
input [n-1:0] c, d, Both 4-state vectors
output [m-1:0] p,
output q
);
assign ...
always ...
endmodule

module top;
logic aa, bb, qq; 4-state variables
logic [3:0] cc, dd, pp;
blk #(.n(4), .m(4)) inst (.a(aa), .b(bb), .c(cc),
.d(dd), .p(pp), .q(qq));
endmodule

SystemVerilog uses ANSI-style port, parameter, and argument lists from Verilog 2001 in which both
inputs and outputs appear on the same line when declaring modules, tasks, and functions. (Although the
legacy Verilog 1995 port syntax still exists for backward compatibility, there are situations in
SystemVerilog where ANSI-style port lists must be used.)

In Verilog and SystemVerilog, when you insert a range at the front of a comma-separated list of names,
you have to be aware that you are in effect introducing a vector data type which is used for all the names
in the list. So in the example above, inputs c and d are both n-bit, 4-state vectors.

SystemVerilog introduces a new keyword logic that has two different meanings, depending on the context
in which it is used. Either it is equivalent to the old keyword reg, meaning that it is defining a 4-state
variable as opposed to a wire, or it is used to specific that the data type of a variable or a wire is 4-state,
as opposed to something else. Unlike Verilog, where the same 4-state data type is used throughout most
of the language, SystemVerilog fully supports named, user-defined data types.

Another difference between Verilog and SystemVerilog is that SystemVerilog allows an output port to be
connected to a variable: in Verilog, an output port can only be connected to a net/wire.

6 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
1
Provided for Nvidia on 17-May-2019.

Introduction
Introduction to SystemVerilog 1.0

Reg, Logic, and Bit

reg 4-state variable


logic 4-state type (variable or wire)
bit 2-state variable

reg [7:0] r;
Each bit is 0,1,X, or Z
logic [7:0] l;

bit [7:0] b; Each bit is 0 or 1

wire [7:0] w; Wires are always 4-state

In most situations the new logic keyword is synonymous with reg, but it is also possible to use the logic
keyword when declaring wires. However used, logic means that each bit of the variable/wire is 4-state (0,
1, X, or Z). Another new keyword, bit, is used exclusively to define 2-state variables that can have the
values 0 and 1. Either of these new types can be used to define vectors (as shown above).

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 7


1 Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction

Variables, Wires, and Ports


module modu (
input wire a,
• A variable can be

input var b, • assigned using procedural assignments


input logic c, • or exactly one continuous assignment
output wire p, • or connected to exactly one output port
output var q,
output logic r
); No need for wire or var in practice
assign p = b;
assign q = c;
assign r = a; module top;
endmodule var logic a, p, pp, q;
wire logic b, c, r;
modu inst1 (.a, .b, .c, .p, .q, .r);
modu inst2 (.*);
modu inst3 (.p(pp), .*);
endmodule
Shorthand port connections

The keyword var can be used to declare a variable as opposed to a wire. There are almost no situations
where the var keyword is actually necessary, so it is usually omitted.

The keyword logic used in a port declaration implies that the port is a variable, as opposed to a wire. So
in the example above, ports a and p are wires, whereas b, c, q, and r are variables. It very rarely makes
and difference whether an input port is a variable or a wire.

For many experienced Verilog users, one of the most surprising feature of SystemVerilog is that it allows
variables to be used in several contexts where Verilog required the use of a wire. In particular, a
SystemVerilog variable can be assigned by exactly one continuous assignment or can be connected to
exactly one output port. A variable can still be assigned from any number of procedures, of course. In the
example above, we see continuous assignments to variables and wires, and also see output ports
connected to variables and wires. In practice, this means that it is very unusual to need to use wires in
SystemVerilog: variables are adequate most of the time.

Another of SystemVerilog's innovations is shorthand port connections. In the common situation where a
port is connected to a variable (or a wire) of the same name, the port connection can be shortened from
.name(name) to .name, as shown above. The so-called wildcard port connection .* means connect every
port to a variable (or a wire ) of the same name. It is possible to combine a wildcard port connect with
explicit connections so that certain ports are connected explicitly by-name and others are connected using
the wildcard connection.

8 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
1
Provided for Nvidia on 17-May-2019.

Introduction
Introduction to SystemVerilog 1.0

SystemVerilog Language Features


C-style data types & control - enum, struct, typedef, ++, break, return
Synthesis-friendly "concise" RTL notation
Packages
Interfaces

SystemVerilog Assertions

Clocking blocks (synchronization between DUT and test bench)


Object-oriented programming - classes
Constrained random stimulus generation
Functional coverage
Dynamic processes, dynamic arrays, queues, mailboxes, semaphores

Direct Programming Interface (DPI) - calling C from SystemVerilog


Extensions to VPI

SystemVerilog offers three distinct capabilities, sometimes referred to as SystemVerilog RTL,


SystemVerilog Assertions, and SystemVerilog Test Bench. SystemVerilog RTL, of course, is for hardware
design, and you can think of it as a series of improvements to Verilog, which include programming
language features from C, interfaces, and packages as well as the RTL constructs. SystemVerilog
Assertions are for writing checkers, and SystemVerilog Test Bench is for creating constrained random
verification environments, which is perhaps the most significant application of SystemVerilog today.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 9


1 Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction

Caveats
• C-like control constructs and data types
• Concise RTL
A better Verilog
• VHDL-like package and import
• Assertions

• Non-portable constructs Ill-defined

• Classes
• Constraints and coverage based on classes Class-based
• Built-in types - strings, queues, maps verification

• Virtual interfaces

Used by standard verification methodologies

SystemVerilog is a very large and complex language, and as we have already seen could be regarded as
being several different languages rolled into one. As a result, simulator vendors have struggled over the
years to implement the entire language in a consistent and high quality way, and it is very important to be
aware of some of the pitfalls as you start to adopt SystemVerilog. Otherwise you will waste a lot of time
trying to debug your "unusual" coding style or have difficulties porting code between simulators.

There are some areas of SystemVerilog that are pretty solid, that is, well-defined and consistently
implemented. These include the features that are close to the original Verilog language or close to VHDL
or C, and include the new synthesis-aware RTL features and assertions (SVA). The class-based features
are also well-defined and well-implemented, mainly due to their prevalence in the standard verification
methodologies (VMM, OVM, and UVM).

10 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
1
Provided for Nvidia on 17-May-2019.

Introduction
Introduction to SystemVerilog 1.0

The UVM Family Tree


Cadence Mentor Synopsys

e Vera
eRM RVM

SV/e SV/SC SV
URM AVM VMM

SV
OVM

Accellera
OVM 2.1.1

SV
UVM

The diagram above shows the "family tree" of functional verification methodologies related to OVM and
UVM. The headings Cadence/Mentor/Synopsys indicates the vendor from which each of the lineages
originated. The superscripts show the principle language of each methodology. The meaning of each
abbreviation is as follows:

• AVM – Advanced Verification Methodology

• eRM – e Reuse Methodology

• OVM – Open Verification Methodology

• RVM – Reference Verification Methodology

• URM – Universal Reuse Methodology

• UVM – Universal Verification Methodology

• VMM – Verification Methodology Manual

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 11


1 Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction

Books and Resources

• Google and Amazon


• Search for systemverilog

• http://ieeexpore.ieee.org
• Search for systemverilog 1800-2012

• http://www.accellera.org

• http://www.doulos.com/knowhow/sysverilog

End

A number of SystemVerilog resources are variable, including manuals, books and web sites.

The SystemVerilog LRM is available to download, free of charge, from the IEEE.

For information on a particular tool’s SystemVerilog support, refer to the tool’s documentation, which may
include User Guides, Release Notes and Application Notes.

A number of books about SystemVerilog have been published. These cover the full range of the
application of SystemVerilog. You can find details of these on the Internet.

Details of the Doulos Golden Reference Guides mentioned are as follows:

SystemVerilog Golden Reference Guide; 410 pages, Doulos Ltd., ISBN 0-9547345-3-X

SystemVerilog Assertions Golden Reference Guide; 102 pages, Doulos Ltd., ISBN 0-9547345-4-8

12 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Programming Language Features

Aim

• To learn SystemVerilog’s improved


programming language features, many
inspired by C

Topics covered

• Static and automatic variables


• Increment and assignment operators
• Labelling blocks
• Time units
• Do While and Assert
• Task and function syntax
• Strings and $sformat

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 13


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

C-Like Language Features


logic [7:0] a, b, f;

always_comb Combinational process (always_ff = clocked process)


begin
Local variable declarations
static int count = 8;
for (int i = 0; i < 8; i++) Variable initialization
begin
count--; int = 32 bits, no Xs or Zs

if (a[i]) continue;
++ --
if (b[i]) break;
end break, continue
f = count;
end

SystemVerilog adds several features directly inspired by the C language.

SystemVerilog permits local declarations inside blocks (begin-end or fork-join) without any need to
name those blocks. It also permits local declarations within the first line of a for loop, as shown above.

SystemVerilog supports proper variable initialization. That is, the initial value given to the variable will be
the value of the variable the first time it is read: unlike Verilog, SystemVerilog does not rely on implicit
initial statements to initialize variables.

SystemVerilog adds several so-called 2-state data types such as int, called 2-state because each
individual bit is either 0 or 1 (no Xs or Zs). Type int is 32 bits and signed.

SystemVerilog adds ++, == and all the assignment operators from C. Assignment operators modify the
value of the variable on the left-hand-side.

SystemVerilog adds the keywords break and continue from C. break jumps out of the enclosing loop,
continue jumps to the next iteration of the enclosing loop.

14 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Static vs Automatic Variables
always_comb
begin
static int count = 8; Initialized once at compile time
...

always_comb
begin
automatic int count = 8; Initialized each time around
...

always_comb
begin
int count; static, but no initialization
count = 8;

Variables that are declared locally and initialized inline may be declared as static or automatic. static
variables are only initialized once and retain their values on exit from the block. automatic variables are
re-initialized each time the block is entered.

It is an error to omit the keyword static in contexts in which the variable could be either static or
automatic; some tools would issue a warning and others an error if the static keyword were omitted
before a local static variable initialization. Hence it is better to either include the static/automatic keyword
or use a separate variable assignment rather than an inline initialization.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 15


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Static vs Automatic Tasks

task static ST; // default


int i; // static
automatic int j;
...
endtask

task automatic AT;


int i; // automatic
static int j;
...
endtask

By default, any local variables declared within a task or function are static, and hence retain their value
between task calls unless they are explicitly declared as automatic. On the other hand, if a task or
function is declared to be automatic, then any local variables are automatic unless explicitly declared as
static.

16 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
++, --, and Assignment Operators
• Increment and decrement operators

i++; i = i + 1;

j = i++; j = i; i++; Post-increment

j = ++i; i++; j = i; Pre-increment


j += i; j = j + i;

• Assignment operators

= += -= *= /= %= &= |= ^= <<= >>= <<<= >>>=

if ( a = b ) Error

if ( (a = b) ) Can use an assignment in an expression

SystemVerilog provides a collection of interesting new operators, broadly bringing its set of operators into
line with the C language. In particular, “assignment operators” such as += and the C increment and
decrement operators have been added to the language.

Blocking assignment operators may be used in expressions, provided they are enclosed in parenthesis.
This is to prevent the common mistake of writing

if (a=b)

Instead of

if (a==b)

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 17


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Labeling
• Verilog named block • Block need not be named

begin: name begin


int temp; int temp;
... ...
end end

• Name may be repeated at end

begin: name module mod;


... endmodule: mod
Must match
end: name

task T;
endtask: T

In Verilog begin-end and fork-join blocks could be labelled. Labelling was required if variables were
declared in a block.

In SystemVerilog the label may be repeated at the end, as shown. This is useful for documenting the
code. The label at the end must be the same as the one at the beginning.

Modules, tasks and functions may also have their names repeated at the end.

In SystemVerilog, the block name may appear before the keyword begin or fork. But you cannot have a
name both before and after the begin or fork. In fact, any procedural statement may be labelled in
SystemVerilog. This is especially useful for loops, because they can then be disabled.

Despite enhancing named blocks in this way, one reason for using them is removed: in SystemVerilog
variables may be declared in unnamed blocks!

18 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Time Units
• Verilog `timescale directive gives compilation order dependencies
• Replace with timeunit and timeprecision

module M ( ... );
timeunit 1ns;
Must appear before any declarations
timeprecision 1ps;

Time literal
always #10 ...

Means 10ns
initial #10ns ...
endmodule : M Time literal

In Verilog, the time unit and precision could be specified using the `timescale directive. As is the case with
all the compiler directives, this may create dependencies in the order in which files are compiled.

SystemVerilog allows you to include a timeunits declaration, which consists of a timeunit, a timeprecision,
or both as the first statement(s) in a module (before any declarations, except ports in an ANSI-style
module). The timeunits declaration is applicable only to the module in which it occurs. It is also applicable
to nested modules.

Time literal
SystemVerilog also introduces time literals, which are values of the form number unit. Time literals may
be used wherever delay values can be used in Verilog, for example in procedural assignments.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 19


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Do While Loop
initial
begin
static int count = 1;
do
$display("count = ", count++);
while (count < 10);
end

initial
do
begin
static int count = 1;
#1ns $display("count = ", count);
count += 1;
end
while ($time < 10ns);

As we saw earlier, loop constructs and subprograms are enhanced by addition of the break, continue and
return keywords. There is also a new do-while loop, matching the same construct in C. Note that if you
want more than one statement inside a do-while, the statements need to be enclosed within begin...end
(or fork...join).

20 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Immediate Assertions

Condition to test. We expect


this to be true ...

assert ( output == expected );

... if it is true

(nothing)

... If not
Tool-dependent format
# ** Error: Assertion error
# Time 15ns Scope: Top.Design File: design.sv …

SystemVerilog adds an assert statement that can be called from within any procedure, similar to the
assert in VHDL or C.

If the assertion expression is true, then no action is taken: simulation proceeds normally.

However, if the expression is false then the simulator writes out an error message. The exact format of
the message is tool-dependent, but will include the simulation time when the error occurred, the
hierarchical scope that contains the assertion, and the name of the source file and the number of the line
number containing the assertion.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 21


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Immediate Assertions
Optional label

a_OutWrong : assert ( output == expected )


ok_count++;
else $error("Output is wrong");

# ** Error: Output is wrong


The default
# Time 15ns Scope: Design.a_OutWrong File: …

$info Includes time, scope, file & line


$warning
$error
$fatal Use only in assertions

The assert can be followed by a so-called action block, that is, a procedural statement that is executed if
the assertion holds each time it is tested, and optionally an else part that is executed only if the assertion
fails. The label in front of the assert is optional, but can be helpful in determining the cause of the
assertion violation.

There are four system tasks, $info, $warning, $error, and $fatal, that print out a messages with an
associated severity level. These four system tasks can only be called in the action block of an assert
statement.

22 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Enhanced Tasks and Functions

task T (input logic [7:0] a, b,


ANSI-style argument list
output logic [3:0] c, d);
...
begin-end not needed!
return;
...
endtask : T

function int F (bit a, int b); Can return any type


if (a)
return b; return statement
return 0;
endfunction : F Optional label

Tasks and functions can be declared using ANSI-style argument lists using syntax similar to the ANSI-
style parameter and port lists of modules.

Tasks and function can contain multiple sequential statements without any need to enclose them within a
begin-end block.

Tasks and functions can return control by executing a return statement. In the case of a function, the
return statement can be used to return a value from the function, just like C.

The type returned from a function may be any SystemVerilog type, including a user-defined type
(described later).

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 23


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Task and Function Arguments


• Input arguments can have default values

task T (a = 0, input logic

int b = 0, input int


output c, output logic
int d);
output int
c = ~a;
d = b + 1;
endtask

• Can pass arguments by name and omit arguments that have default values

initial
T(.b(1), .c(C), .d(D));

Task and function ports have a default direction of input and a default type of logic. Once a direction is
given, subsequent ports default to the same direction.

Input arguments may have default values.

Named argument mapping can be used when calling tasks and functions. The abbreviated syntax used
when connecting ports is not allowed here.

24 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Void Functions
function int add(int i, j);
return i + j;
endfunction

function void print(int i = 0); • Tasks can execute timing


$display("i = ", i); controls (consume time),
functions cannot
endfunction

begin
add(2, 2); Illegal!
print; Void function called as a statement
void'( add(2, 2) ); Type cast to discard return value
end

A void function is a function that does not return a value. A void function is very similar to a task, the
difference being that a void function shares the same restriction as all other functions, namely, a function
is not allowed to consume any time. Calls to void functions are procedural statements, just like calls to
tasks.

The void keyword can be used instead of a type name in certain other contexts too, for example in a type
cast. A void cast in effect throws away the value returned from a function, allowing that function to be
called as a procedural statement as if it were a void function.

The syntax for a type cast in SystemVerilog is a type name, then the single apostrophe, then an
expression in parenthesis.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 25


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

Argument and Return Types


Any built-in or user-defined type

function string choose(int select, string text1, text2);


case (select)
0: return text1;
1: return text2;
default return "";
endcase
endfunction

string text = choose(n, "Hello", "World");

Functions have also been enhanced in SystemVerilog. Functions may have input, output, inout and ref
arguments (Verilog functions may only have inputs.) As with tasks, function arguments have a default
direction and type of input logic and inherit the direction of previous arguments in the list. Arrays may be
passed as arguments.

The return statement may be used to return from the function before reaching the end.

26 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0 2

Features
Programming Language
Type string

int n;

string a; Empty string

string b = "Hello"; Initialization

a = b; Assignment

a = {b, " World"}; Concatenation

if (b == "Hello") Comparison

$display(b); %s not needed

n = b.len; String length

n = b[0]; Select the first character

b = a.substr(6, 10); Select a substring (like a part select)

In Verilog, you can store string values in arrays. Eight bits are required for each character. Verilog arrays
have fixed sizes, so when you create an array in which string values will be stored, you need to know in
advance how big the strings will be. You use the %s format specifier to display string values.
SystemVerilog introduces a new string data type. A string variable resizes dynamically according to the
length of the string that is assigned to it and you can write string values without using %s.

The string type has many useful operators and methods, including comparison (==, !=, <, <=, >, >=),
concatenation and replication.

Strings can be indexed (like an array), which returns a single byte. Strings are always numbered from 0,
with str[0] being the first character of the string, str.

Methods are functions or tasks that are called by appending a full stop and the method name to a string
variable. Some methods have arguments; these are supplied in parentheses after the method name, like
a task or function call. Parentheses are not needed if there are no arguments.

The slide shows some examples of string methods. There are many more; they are, of course, all
documented in the SystemVerilog LRM.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 27


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
2
Features
Programming Language

$sformat and $sformatf

• $sformat is like $display


• $sformat prints to its first argument, a string
• $sformatf is a function that returns a string

int i;
logic [7:0] v;
string txt;
$sformat(txt, "i = %0d v = %b", i, v);

function string convert2string;


return $sformatf("i = %0d v = %b", i, v);
endfunction
Just enough characters
End

$sformat is a member of the $display family, that is, it is concerned with formatting text strings. Whereas
$display prints its output to standard output, $sformat puts its output into its first argument, which must be
a string. There is also a function-form, $sformatf, that returns the formatted string as the value of the
function. This latter from is particularly useful.

28 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Basic Data Types


3

Basic Data Types


Aim

• Introduce SystemVerilog’s new data types

Topics covered

• 4-state and 2-state types


• Enumerations
• Structs and unions
• Packed and unpacked types
• Multidimensional arrays
• Packages and import

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 29


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 4-state and 2-state Types


Basic Data Types

• 4-state types
Signed Unsigned Width
logic signed logic 1 bit
logic signed [n:m] logic [n:m] N bits

• 2-state types (variables only, not wires)

Signed Unsigned Width


bit signed bit 1 bit
bit signed [n:m] bit [n:m] N bits
byte byte unsigned 8 bits
shortint shortint unsigned 16 bits
int int unsigned 32 bits
longint longint unsigned 64 bits

As well as logic, the new name for reg, SystemVerilog introduces several new so-called 2-state data
types, which can be used to declare scalars or vectors. Each bit of 2-state type is either 0 or 1 – it cannot
be X or Z. The initial value of a 2-state variable is 0.

Many of these types were “borrowed” from C. This makes it easier to interface to existing C functions –
using the DPI, for example – or to translate algorithms written in C into SystemVerilog. The number of bits
used to represent byte, shortint, int and longint data is well defined in SystemVerilog (8, 16, 32 and 64 bits
respectively).

Note the default signing rules for the new 2-valued vector types.

30 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Initial Values
3

Basic Data Types


• Every variable and wire has a well-defined initial value

2-state 0
4-state 1'bX
real 0.0
string ""
Class handle null

Every variable and wire as an initial value defined by the language. This initial value is 1'bx (unknown) for
a 4-state type, is 0 for a 2-state type, and is the nearest analogy to 0 for other types as shown by the table
above.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 31


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 Examples
Basic Data Types

reg [7:0] r;
logic [7:0] v;
Equivalent

logic signed [31:0] s;


u = v; X, Z → 0
integer i;
Equivalent u = -1; 65535

v = u; 8'hFF
logic [31:0] t;
integer unsigned j; u = v; 255
Equivalent

bit [15:0] b;
shortint unsigned u;
Equivalent

SystemVerilog does not introduce any new 4-state variable types. The new name logic is a synonym for
reg – the two can be used interchangeably (with some minor exceptions). logic is a better name than reg
for this variable type, because a hardware register (flip-flop) is not necessarily inferred by it. As we shall
see later, the rules for assigning variables have been relaxed, so that for may designs logic can replace
both reg and wire.

SystemVerilog is not strongly typed where integers are concerned. You can freely assign values between
the different 2- and 4-state types without the need for type conversions or explicit casts. The usual Verilog
truncation and extension rules for assigning integers with different widths apply.

You could use a linting tool to impose strong typing by requiring explicit casting.

If a 4-state value that contains Xs or Zs is assigned to a 2-state variable, the Xs and Zs become 0s.

32 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Caveats with Signed Types


3

Basic Data Types


Unsigned Signed
logic [n:m] logic signed [n:m]
wire [n:m] wire signed [n:m]
part_select [n:m]
integer
'd1 1
4'b1001 4'sb1001
$unsigned() $signed()

• Mixed signed / unsigned operands give UNSIGNED arithmetic


• Use $signed(...) to force signed interpretation of part selects

Some aspects of Verilog-2001 signed arithmetic are a little surprising at first glance, and provide
opportunities for hard-to-trace errors.

As we have already mentioned, arithmetic and copying is always performed using unsigned arithmetic if
one or more of the operands is unsigned. Therefore, it's essential to be aware of how the signed or
unsigned property of an operand is determined.

Part selects
A part select of a vector is always unsigned, even if the original vector was signed.

Signed literals
Based literals such as 'd1, 4'b1001 etc. are treated as unsigned values, whereas implicit decimal literals
such as 1 are signed, as are explicitly signed literals such as 8'sb1001

Conversions
The built-in pseudo-functions $signed() and $unsigned() allow you to convert unsigned expressions or
operands into signed values with the same bit-pattern, and vice versa. For example, the expression
3'b111 is positive, but the expression $signed(3'b111) is negative.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 33


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 Enumerations
Basic Data Types

enum { idle, start, stop } state;


Variable

if (state == idle) Comparison


state = start; Assignment

typedef enum {idle, start, stop } state_t;


User-defined type
state_t state;
Variable

Enumerations allow you to define a data type whose values have names. Such data types are
appropriate and useful for representing state values, opcodes and other such non-numeric or symbolic
data. Of course, each different name must have an underlying representation as a bit-pattern or integer.
The default is that they are type int and have values the values 0, 1, 2 etc. As we’ll see, SystemVerilog
allows you to control this aspect of the representation easily.

The named values of an enumeration type (known as enumeration constants) act like constants. You can
copy them to and from variables of the enumeration type, compare them with one another and so on.

typedef
typedef may be used to declare a user-defined type. Instead of declaring a variable of an enum type, it is
usually better to declare a user-defined enum type using typedef, and then declare a variable of the user-
defined type.

34 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Type-Checking of Enumerations
3

Basic Data Types


0 1 2 Values of type int

typedef enum { idle, start, stop } state_t;


state_t state;

if (state == 1) OK – type state_t implicitly cast to int

state = 2; Error – RHS must be idle, start, or stop

state = state_t'(2); OK – explicit type cast from int to state_t

Enumerations are strongly typed. You can’t copy a numeric value into a variable of enumeration type,
unless you use a type-cast as shown in the last box. According to the IEEE LRM, type-checking should
also be performed when passing values as arguments to a task or function, or when using relational
operators. However, when you use an enumeration in an expression, the value you are working with is
the literal’s integer equivalent; so, for example, it’s OK to compare an enumeration variable with an
integer; and it’s OK to use an enumeration value in an integer expression. (In the example i is an int or
similar.)

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 35


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 struct
Basic Data Types

• Aggregate of dissimilar data items, just like C

struct {
bit b;
int i;
logic [7:0] v;
} s;
Variable

s.b = 1;
s.i = -8;
s.v = 8'hff;

s = '{1, -8, 8'hff};

Assignment pattern

SystemVerilog offers struct and union user-defined data types, modeled on the same idea in C. VHDL
users may be familiar with struct in the form of VHDL record data types.

A struct is declared as shown. Note that unlike the C language, SystemVerilog does not allow the optional
structure tag before the {.

Struct members are selected using the .name syntax, as shown. Structure literals and expressions may
be formed using braces preceded by an apostrophe ('{}) – this is called an assignment pattern.

36 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

typedef struct
3

Basic Data Types


• struct is best used with typedef

typedef struct {
int x, y;
} point_t;

typedef struct {
point_t ctr;
int radius;
} circle_t; Type

Variable
circle_t c; Assignment pattern using keys
c = '{ ctr:'{x:2, y:3}, radius:4};
$display(c.ctr.x ,, c.ctr.y ,, c.radius);

It is often useful to declare a new type for a struct using the typedef keyword and then use that new type
to declare several variables.

The example above shows structs defined with typedefs and nested two-deep. Note that a value for the
outermost struct can be written using a nested assignment pattern. Furthermore, within an assignment
pattern the fields of the struct can be identified using the field names as keys.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 37


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 Packed Struct
Basic Data Types

typedef struct packed { Bits contiguous in memory


logic sign;
Can be 2-state or 4-state
logic [7:0] exponent;
logic [22:0] mantissa;
} Float32;

31 ... ... 2 1 0

Float32 a, b, c;
a.sign = 0;
a.exponent = 8'h80;
a.mantissa = 23'h400000;

b = '{sign:0, exponent:8'h80, mantissa:23'h400000};

c = 0;
c[30:0] = a[30:0]; Packed type equivalent to vector[n:0]

A packed data type has all of its constituent fields or elements packed together, without any gaps, to form
a single contiguous vector of bits. SystemVerilog supports packed structs and packed arrays. Packed
data types can be based on 2-state or 4-state elements: if any of the values in the struct are 4-state then
all the values are stored as 4-state (even if the native data type of the field would restrict its values to
being 0 and 1).

A significant feature of all packed data types is that it is possible to access the value as if it were a single
vector with its index running down to 0. Hence packed_var[0] would always be the least significant bit of
the variable packed_var.

38 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Packed and Unpacked Arrays


3

Basic Data Types


logic [7:0] vector;

logic [7:0] memory [0:255];

Packed dimension(s) Unpacked dimension(s)


[3:0][7:0]
[1][0]
[1][1]
[1][2]
[1][3]
.
logic [3:0][7:0] array [1:2][0:15]; .
3 4 1 2 [1][15]
[2][0]
[2][1]
[2][2]
Most Least
significant significant [2][3]
1 2 3 4 .
.
array[2][15][3][7] = 1'b1; [2][15]

In the original Verilog language, so-called memories were defined in an unusual way, with two separate
array subscripts. The range before the variable name represents the word size of the memory, whereas
the range after the variable name represents the address space of the memory. The need for backward
compatibility with the legacy syntax causes some peculiarities in the syntax used to define multi-
dimensional arrays in SystemVerilog.

A SystemVerilog array can have any number of packed dimensions, written before the variable name, and
any number of unpacked dimensions, written after the variable name. When it comes to writing a
subscript to access elements or slices of the array, the first unpacked dimension becomes the most
significant index and the last packed dimension becomes the least significant index, leading to a very
peculiar ordering of the subscripts relative to the definition of the dimensions. It is possible to leave
subscripts off the end of the list, subject to some restrictions, as we will see next.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 39


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 Indexing Multidimensional Arrays


Basic Data Types

logic [3:0][7:0] array [1:2][0:15];


array[2][15][3][7] = 1'b1;
array[2][15][3:2] = 16'hff; Packed array is just a vector
array[2][15] = 32'hffffffff;
or any expression!
array[2] = 0;
array = 0;

logic [31:0] array2 [2][16]; [2][16] = [0:1][0:15]


array2 = array; Unpacked dimensions match,
packed dimensions differ
logic [31:0] array3 [32];
array3 = array2; Unpacked dimensions don't match

Packed dimensions are guaranteed to be laid out contiguously in memory and thus can be copied to other
packed variables or sliced, but are restricted to 2-state and 4-state types. In contrast, unpacked
dimensions can only be copied to variables with identical unpacked dimensions.

When copying bits to and from packed dimensions, because they are packed, the number of array
dimensions don't need to match, and even the total number of bits copied doesn’t need to match. With
unpacked dimensions, the number of dimensions and the number of elements in each dimension must
match exactly, although subscript bounds and direction do not need to match, only the number of
elements.

The example above also shows an alternative syntax that can only be used for unpacked dimensions,
with int array[8] having the same meaning as int array[0:7], for example.

Equivalent types
A further rule relating to unpacked array copying is that the source and destination array's element types
must be equivalent. The equivalence of two types has a formal definition in the SystemVerilog LRM, but it
can be summarised as: For two packed data types (packed structs, packed arrays, simple vectors) to be
equivalent, they must have the same number of bits, have the same signedness (signed or unsigned),
and the same two-state/four-state attribute. With a very few exceptions, for unpacked types to be
equivalent, they need to be the same type.

40 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Packages
3
Contains parameters,

Basic Data Types


package pkg; types, tasks, functions,
localparam width = 16; variables
typedef logic [width-1:0] BusType;
function logic Parity (BusType b);
...
endfunction User-defined type
int global_variable = 0;
endpackage

module MyModule;
import pkg::*; Wildcard import
BusType bus;
assign par = Parity(bus); Only used names are imported
initial
pkg::global_variable ++; Alternative to import
...

A package is a container for shared or global declarations, but may not contain any processes, i.e. assign,
initial or always statements. Packages are declared outside modules.

The declarations in a package can be used by using a name of the form


package_name::declaration_name, which is called a resolved name. Alternatively, they may be imported:

import package_name::declaration_name;

makes declaration_name directly visible, whereas a “wildcard import” statement:

import package_name::*;

makes all the names in the package candidates for import. They are not imported unless they are actually
used.

Note that wildcard port connection using .* does not cause wildcarded names to be imported, but the
shorthand port connection .name does.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 41


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

3 Packages and Ports


Basic Data Types

package pkg;
typedef logic [7:0] BusType;
...
endpackage

Not ideal - adds names into file scope


import pkg::*;
module MyModule (input BusType bus);
...

module MyModule (input pkg::BusType bus);


...
Recommended

module MyModule import pkg::*; (input BusType bus);


... SV 2009
End

Using packages to hold type declarations is a good idea, but it presents a small problem when trying to
use types from a package to specify the data type of a module's port.

In the preferred Verilog-2001 or "ANSI" port list style there is a problem: how can we declare the port’s
data type when we have not yet seen the definition?

One possible approach is to import it from a package outside the module. This works well in simple
situations, but has the significant drawback that it puts the type definition into the global or file scope. For
various reasons this is undesirable and we do not recommend it for serious use, even though it is very
convenient when working with only a small number of design files.

It is much preferable to use the package name explicitly to qualify type names in the module's port list.
Using the scope resolution operator :: allows us to specify from which package a definition should be
taken. Although this is a little more verbose than the file-scope import, it is much clearer and Doulos
strongly recommends this technique.

42 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Interfaces
4
Aim

Interfaces
• Introduce the use of SystemVerilog
interfaces for design and modelling

Topics covered

• Interfaces
• Ports and parameters on interfaces
• Modports
• Generic interface ports

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 43


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Modelling a Bus
4 • As an example, consider the AMBA Advanced Peripheral Bus (APB)
Interfaces

Many designs make use of buses. These may be proprietary or one of a number of industry-standard
buses. As an example, we are going to look at modelling the AMBA Advanced Peripheral Bus (APB), a
simple synchronous bus interface.

The write transfer starts with the address, write data, write signal and select signal all changing after the
rising edge of the clock. The first clock cycle of the transfer is called the SETUP cycle. After the following
clock edge the enable signal PENABLE is asserted, and this indicates that the ENABLE cycle is taking
place. The address, data and control signals all remain valid throughout the ENABLE cycle. The transfer
completes at the end of this cycle.

For a read transfer, the timing of the address, write, select and strobe signals are all the same as for the
write transfer. In the case of a read, the slave must provide the data during the ENABLE cycle. The data
is sampled on the rising edge of clock at the end of the ENABLE cycle.

Full details of APB can be found in the AMBA Specification document, available from ARM Ltd.
(www.arm.com).

44 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

APB with Master and Single Slave


4

Interfaces
PCLK
PSEL
PENABLE
APB PWRITE APB
Master PADDR Slave
PWDATA
PRDATA

Exactly one master One or more slaves

An APB system has exactly one bus master, and one or more slaves. We are going to consider first the
simple case of one master and one slave.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 45


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Modelling APB using Ports


4
Interfaces

module Master (input logic PCLK, PSEL, PENABLE, PWRITE,


input logic [15:0] PADDR,
input logic [31:0] PWDATA,
output logic [31:0] PRDATA,
...
);
There may be other ports
...
endmodule

This shows how the APB system might be modelled using an ANSI-style port list from Verilog-2001
onward.

Each signal belonging to the bus is modelled as a separate port. All of the functionality of the bus has to
modeled within one or more modules.

In the next slide we are going to replace the list of ports with a single interface port.

46 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Simple Interface = Bundle of wires/vars


4
interface APB;

Interfaces
logic PCLK, PSEL, PENABLE, PWRITE;
logic [15:0] PADDR;
logic [31:0] PWDATA;
logic [31:0] PRDATA;
endinterface

module Master (APB iport, ...);


...
endmodule Interface port Must use ANSI-style port list

At their simplest, interfaces are just packages of interconnect. They are more appropriate for this purpose
than structs, because it’s not possible to include nets as part of a struct.

Modules can have ports of an interface type, and these ports can be associated with any suitable
interface instance in just the same way that module ports of net type can be associated with any suitable
net. Note that interface ports don’t have a direction like input or output, they simply have a type, which is
the name of an interface. Interface connections are by default bi-directional, although we shall see that it
is possible to provide direction information using modports.

Interface ports are only allowed with the “ANSI” style of port declarations, where the ports are declared in
the port-list at the top of the module.

It is also necessary to instantiate the interface itself, so that the modules have something to connect to!

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 47


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Package versus Interface


4 package pkg;
typedef logic [15:0] t_APB_a; Package must be
compiled before being
typedef logic [31:0] t_APB_d;
Interfaces

referenced
endpackage

interface APB;
Interface has same
logic PCLK, PSEL, PENABLE, PWRITE; scope rules as module
pkg::t_APB_a PADDR;
pkg::t_APB_d PWDATA; Interface can contain
pkg::t_APB_d PRDATA; the same items as
endinterface modules (almost)

module Master (APB iport, ...);


import pkg::*; Interfaces and modules
can be compiled in any
... order
endmodule

Although there are similarities on the surface, interfaces and packages are very different kinds of
language construct. A package must be compiled before being referenced from anywhere else in the
code, and would typically define types, tasks, functions, and localparams. The compilation order is critical.
On the other hand, an interface is like a module in the sense that modules and interfaces can be compiled
in any order (the compiler will accept a module instantiation before compiling the module definition), and
you can use hierarchical references across module and interfaces instances.

Interfaces and modules can contain almost the same kinds of declarations and statements. About the only
thing that an interface cannot contain that a module can is a module instantiation (though that may
change in the future).

48 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Instantiating an Interface

APB_Master APB_Slave
4
(module) instance (module) instance

Interfaces
APB APB
Master APB (interface) Slave
(module) (module)

APB (Interface)
instance

Interfaces are instanced, just like modules and form part of the hierarchy of the design. Having been
instanced, an interface can be connected to module ports that have the correct interface type.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 49


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Instantiating an Interface
4 • Interfaces must be instantiated, just like modules
• An interface is like a module that can be connected to a port
Interfaces

interface APB; module Design (...);


...
endinterface APB iface_inst ();

module Master ( Master master (


APB iport, ...); .iport(iface_inst), ...);
...
endmodule Slave slave (
.iport(iface_inst), ...);
module Slave (
APB iport, ...); ...
...
endmodule endmodule

Interfaces are declared at the same level as modules and the syntax for an interface instance is identical
to that of the module instance. In fact, interfaces have a lot in common with modules and are perhaps
best understood as fancy modules that have the additional feature of being usable as the types of
interface ports. The interface’s instance name (iface_inst in the example) can be connected to the
interface port of one or more module instances: if an interface port is given a named type (it need not be),
it must be the same name as the interface itself (APB in this example).

50 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Accessing Interface Members


interface APB;
4
logic PCLK, PSEL, PENABLE, PWRITE,
t_APB_a PADDR,
Name of interface

Interfaces
t_APB_d PWDATA,
t_APB_d PRDATA
endinterface module Master (APB iport, ...);
...
always begin : ClockGenerator
iport.PCLK = 0;
#Period iport.PCLK = 1;
interface_port.member
#Period;
end : ClockGenerator
...
endmodule

Where an interface is connected to an interface port, members of the interface can be accessed from
within the module using hierarchical references through the interface port. For example, the APB clock
signal PCLK is accessible from the module Master using the name iport.PCLK, where iport is the name of
interface port on the Master module. Almost any named declared in the interface can be accessed in this
way.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 51


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Ports and Parameters on Interfaces


4 interface Bus
#(parameter ADDR_W = 16, DATA_W = 16) Interface itself has parameters
Interfaces

(input Clk, Reset); Interface itself has ports

logic [ADDR_W-1:0] Address;


logic [DATA_W-1:0] Data;
logic WE;
endinterface

module TestBus;
logic Clk = 0;

Bus #(.ADDR_W(8), .DATA_W(32)) bus_inst (.Clk, .Reset);


...
Just like instancing a module
endmodule

We have seen that interfaces are very similar to modules. A further similarity is that interfaces, like
modules, can have parameters and ports. The syntax for these is exactly the same as the syntax for
declaring module ports and parameters.

Parameters may be used to specify the widths of the signals in a bus, for example.

Typically, interface ports would be used for system-wide signals like clocks and resets, that are not part of
a specific bus. (The example we have been considering – the APB bus – does include a clock, PCLK.)

An interface’s ports are considered to be members of the interface, and may be accessed in the same
way as the other members (including through modports, as discussed later). The ports must be connected
when the interface is instanced.

52 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Ports on Interfaces
4

Interfaces
Clock

Input

X
Module Interface Module

Interface ports are typically used for signals such as clocks and reset that originate outside of the modules
being interconnected by the interface. Rather than feeding these signals into a module port and from
there out through an interface port to an interface, the signal can be connected directly to the interface.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 53


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Synthesis of Interfaces
4 master_inst slave_inst

module master( pins_inst module slave(


Interfaces

pins pif pins pif


interface pins (input clk);
); );
logic RE, WE;
logic [7:0] addr;
types_pkg::data_t wdata;
always ... always @(...pif.clk)
types_pkg::data_t rdata;
pif.RE <= 1; ... = pif.RE;
endinterface
...
endmodule endmodule

module top (...);


logic clk;
pins pins_inst ( .clk );
master master_inst( .pif(pins_inst) );
slave slave_inst ( .pif(pins_inst) );
endmodule

In cases where interfaces are used as a way to create a bundle of wires (or variables), interfaces can be
synthesized. This slide illustrates an interface being used in a rather straightforward way to encapsulate a
standard bus interconnect scheme. Clearly, if the connected modules are themselves synthesisable, it
would make sense for the connecting interface to be synthesisable too.

54 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Synthesis Results

master_inst Outputs clk Inputs slave_inst


4
module master( module slave(

Interfaces
pins pif pins pif
RE
); );
WE

addr
always ... always @(...pif.clk)
pif.RE <= 1; wdata ... = pif.RE;
... rdata
endmodule endmodule

Input Output
module top (...);
logic clk;
pins pins_inst ( .clk );
master master_inst( .pif(pins_inst) );
slave slave_inst ( .pif(pins_inst) );
endmodule

The interface is indeed synthesisable. Current synthesis tools that support interfaces do so by
"exploding" the interconnect captured in the interface, declaring individual signals in the enclosing (top-
level) module. Naturally these signals must be renamed; typically the interface and modport names are
concatenated with underscores to form a prefix.

It is useful to try a very simple example like this on your synthesis tool, and get the tool to write out a
Verilog-95 netlist after it has performed SystemVerilog analysis (compilation) and elaboration, but before it
has begun its optimisation. In this way you can see exactly how the tool interpreted your SystemVerilog
code.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 55


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Modports
4 interface pins (input clk);
logic RE, WE;
logic [7:0] addr;
module master( module slave(
Interfaces

types_pkg::data_t wdata;
pins pif pins pif
types_pkg::data_t rdata;
); );
modport master_mp (
always ... always ...
input clk, rdata,
pif.RE <= 1; ... = pif.RE;
output RE, WE, addr, wdata);
pif.WE <= 0; ... = pif.WE;
modport slave_mp (
... ...
input clk, RE, WE, addr, wdata,
output rdata);
endinterface

module top (...);


logic clk;
pins pins_inst ( .clk );
master master_inst( .pif(pins_inst.master_mp) );
slave slave_inst ( .pif(pins_inst.slave_mp) );
endmodule

An interface can define any number of modports. When a module accesses an interface through a
modport, the modport effectively acts like a filter and restricts the access the module has to the interface.
The module has to honour the input/output directions defined by the modport.

It is important to realise that modport directions are relative to the module. Although they are defined
within the interface, the directions specified in a modport are those that would otherwise appear in the port
list of the corresponding module. In other words, the modport ’s directions are seen from the client
module’s point of view, not from the interface’s point of view. This can be quite confusing at first glance,
although it’s really very sensible because the modport almost becomes part of the module that uses it.

Syntax quirk: having defined a modport, it is entirely up to you whether and where you choose to use it.
The idea is that if you use a modport, the compiler will check that you've obeyed the rules when accessing
the interface. The modport can be ignored entirely, or used only to declare the interface port, or used only
when connecting the interface port to the interface, or used in both places.

This is still synthesizable.

56 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

APB Modports
• A modport is like a filter that restricts access to the interface 4
• You can always strip out modports – they add nothing but checking

Interfaces
interface APB;
logic PCLK, PSEL, PENABLE, PWRITE;
t_APB_a PADDR;
t_APB_d PWDATA;
t_APB_d PRDATA;

modport master_mp ( output PCLK, PSEL, PENABLE,


PWRITE, PADDR, PWDATA, input PRDATA);

modport slave_mp ( input PCLK, PSEL, PENABLE,


PWRITE, PADDR, PWDATA, output PRDATA);
endinterface

A simple interface like the one we have been using is bi-directional – all the members of the interface are
bi-directional. Also, all the members are accessible to every module that connects to the interface. So it is
possible, for example, for a slave module to drive the APB PCLK signal. This is undesirable – we want
PCLK to be an output of the APB bus master, and an input to the APB slaves.

By adding modports we can specify various different modes of connection to an interface. In this
example, we give the APB interface two modports, representing a master and a slave connection to the
bus respectively. We can then connect the modport, not the interface, to a module instance’s port.

Modports give us a much better compromise between the flexibility of interfaces and the need to control
port direction.

Note that each modport can contain a mixture of input, output and inout connections. Therefore, we do
not specify the direction of a module port if it is going to be connected to a modport. The directions of all
connections in the modport are determined by the modport’s definition; the “client” module has no choice
in the matter.

A modport need not contain all the signals in the interface. This provides the ability to restrict access by
some clients to some members of the modport, because a module connected to a modport can only
access the interface members that are in the modport. Our APB example does not use this feature.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 57


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Connecting the APB Modports


4 interface APB; module Design (...);
...
modport master_mp (...); APB iface ();
Interfaces

modport slave_mp (...);


endinterface Master master (
.iport(iface.master_mp) );
module Master (
APB.master_mp iport); Slave slave (
... .iport(iface.slave_mp) );
endmodule
...

module Slave (
endmodule
APB.slave_mp iport);
...
Can have neither,
endmodule
either, or both

If an interface has modports, you can connect to the modports of the interface instance. There are two
ways of doing this:

Either declare the module’s port type as InterfaceName.ModportName, for example, APB.Master,

Or connect InterfaceInstance.ModportName to a port of type Interface.

You could do both, but one of these is sufficient. If you do this, the modport names would have to match.

You need only declare a modport once within an interface, and then as many modules as you wish can
make use of it. Every time a module instance uses a modport in its connection list, a new modport “stub”
is added to the chosen interface instance.

In the APB example, PRDATA would need to be declared as a wire (or one of the other net types)
because a variable can have only one structural driver.

58 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Generic Interface Ports


interface APB; 4
...
modport master_mp ( output PCLK, PSEL, PENABLE,

Interfaces
PWRITE, PADDR, PWDATA, input PRDATA);
endinterface
Connect to any interface that has a master_mp

module Master ( module Design (...);


interface.master_mp iport1, APB iface1 ();
interface iport2); ANO iface2 ();
...
begin Master master (
iport1.PSEL = 1; .iport1(iface1),
data = iport1.PRDATA; .iport2(iface2) );
data = iport2.x; ...
endmodule endmodule

Connect to any interface that has a variable x End

When creating an interface port, instead of explicitly naming the type of the interface it is possible to
substitute the keyword interface to create a generic interface port. A generic interface port can be
connected to an instance of any kind of interface in the port connection list. When a module attempts to
access the contents of an interface through a generic interface port, the validity of the names being
accessed will be checked at that point.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 59


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

4
Interfaces

60 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

RTL Processes

Aim

• To explain the RTL features of


5
SystemVerilog (assumes you already know
how to write RTL Verilog or VHDL)

RTL Processes
Topics covered

• Register Transfer Level


• always_comb, always_ff, always_latch
• priority and unique
• Wild equality

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 61


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

SystemVerilog and RTL Synthesis

• This section does not teach the principles of RTL synthesis


5
• For hardware designers
RTL Processes

• Assume you already know RTL synthesis

• RTL synthesis issues are almost identical to Verilog and VHDL


(language differences, not synthesis differences)

• For verification engineers

• Become familiar with SystemVerilog features used at RTL

62 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Register Transfer Level

Register transfer

RTL Processes
Combinational
Logic

Registers

Just in case you are new to RTL synthesis, let's quickly recap the essentials. Designing hardware with
SystemVerilog is not about knowing the syntax, but requires that you understanding what RTL synthesis
is all about. An RTL description partitions the digital circuit into registers and the combinational transfer
function between those registers. This structure could be instantiated, though it is more usual to infer the
registers and logic from procedural code. In any particular block there may be some purely combinational
signal paths from input to output, or paths might pass through one register, or two registers, or any
number of registers. In any case, understanding the structure of the hardware block you are designing in
terms of registers and combinational logic is absolutely key to using RTL synthesis.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 63


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Combinational Logic

ModuleName Instance(...);

5 Module instantiation
RTL Processes

assign Output = ...;


Continuous assignment
Combinational
Logic

always_comb Complete sensitivity

begin
... Complete assignment
Output = ...;
end No feedback

There are three ways to describe combinational logic in SystemVerilog (just like Verilog or VHDL),
namely: module instantiation, continuous assignments, and combinational always blocks. Note the three
golden rules that we use to teach proper RTL coding style apply here: complete sensitivity, complete
assignment, and no feedback.

• In modern Verilog, writing always @* is usually sufficient to ensure complete sensitivity in a


combinational always block. SystemVerilog adds always_comb which has the benefit of including
variables referenced indirectly through function calls in the sensitivity and also gets the tools to do
some extra checks.

• Complete assignment means that every single variable that's assigned within the always must be
assigned in every possible execution path, otherwise you would be inferring transparent latches.
SystemVerilog adds new keywords (unique, priority) and semantics to help with this.

• No feedback means no feedback. This is important. Many synthesis pitfalls are associated with
incomplete sensitivity or incomplete assignment.

64 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Clocked Processes

always_ff @(posedge Clock or posedge Reset)


if (Reset) 5
Output <= value_on_asynchronous_reset;
else if (Enable)

RTL Processes
Output <= function_of_inputs_and_state;

Nonblocking assignments

Reset

Registers are inferred from clocked processes, which have there own coding rules:

• The sensitivity list (also known as the event control in SystemVerilog) must contain the clock, asynchronous
control signals and nothing else.

• If there are any asynchronous control signals, there must be an if statement to test for their active values and
execute the corresponding asynchronous actions, with the final else branch executed only if all the control
signals are inactive.

• Any assignments to variables that are used outside the process must be nonblocking assignments (to avoid
simulation races), and any assignments to variables that represent flip-flops should be nonblocking
assignments (for good style). Pretty much every coding standard ever written says "Use nonblocking
assignments for flip-flops".

Incomplete assignments are still an issue (e.g. the case where Reset and Enable are both 0 in this
example) but now the values will be stored in a flip-flop between clock cycles anyway, so the incomplete
assignment results in innocent feedback through the flip-flop (or maybe an enable signal on the flip-flop)
rather than an unwanted transparent latch.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 65


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

always_comb, always_latch, always_ff


Inferred sensitivity list
always_comb

5
Unlike always @*, sensitive to embedded functions
...
Variables shall not be assigned by more than one process
RTL Processes

always_latch Same rules as always_comb, different intent


if (enable)
...

always_ff @(posedge clk or posedge reset)


if (reset)
Exactly one event control, including a clock
...
else
...
always_ff @(posedge clk iff (enable == 1)) ...

SystemVerilog gives you three special forms of always block dedicated to synthesis. always_comb is for
combinational logic. It is like always @* except that always_comb is also sensitive to any variables read
indirectly by functions called from the always block, and so guarantees complete sensitivity. Synthesis
tools might be expected to check that the logic really is combinational, in other words to check for
complete assignment as well as complete sensitivity. always_latch has exactly the same rules as always
comb, but the intent is different. always_ff is for clocked always blocks. always_ff must have exactly one
event control, which must appear immediately after the always keyword. Synthesis tools might be
expected to check that the coding rules for synthesizing clocked logic had been followed. All three share a
common rule that you can only assign a variable in one process (so you don't get the outputs of regular
logic gates wired together).

The iff variant is provided for the concise expression of clock enables by gating the event expression with
a boolean condition. However, you need to be aware that not all synthesis tools support the iff keyword in
this context.

66 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Synthesis-Friendly If / Case

module M1 (input [1:0] a, b, c, d, output logic [1:0] f);

always_comb 5
priority if (a == b)
a

RTL Processes
f = 1;
b
else if (a == c) f
c
f = 2;
d
else if (a == d)

f = 3;

endmodule

Conditions evaluated in sequence for synthesis (as well as simulation)

Run-time warning if no conditions match and no final else

SystemVerilog has two new keywords priority and unique to direct the synthesis of if and case
statements. priority and unique can be used in combinational or clocked always blocks, but we'll
illustrate using always_comb. priority and unique help avoid pitfalls of incomplete assignment and when
directing synthesis to make assumptions about the exclusivity of conditions.

priority indicates explicitly to anyone reading the code that the conditions in the if statement are
evaluated in sequence from top to bottom. Of course, the if statement has this behavior anyway! The
point of priority is to make this explicit. The one change that priority does makes to the semantics of the if
statement is that at least one condition should match - priority implies that the input conditions are
completely decoded. You get a run-time warning if none of the conditions are true and there is no final
else part to the if statement.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 67


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

priority case

module M2 (input [1:0] a, b, c, d, output logic [1:0] f);


5 always_comb

priority case (a)


a
RTL Processes

b: f = 1;
b
c: f = 2; f
c
d: f = 3;
d
endcase

endmodule

Conditions evaluated in sequence for synthesis (as well as simulation)

Run-time warning if no conditions match and no default

The priority keyword can also appear in front of a case statement, with exactly the same rules as a
priority if. The expression at the top is compared with each of the statement labels one-by-one until a
match is found, and the labels are allowed to be variables. Again, the priority keyword doesn't change
the meaning of the case statement except to produce a run-time warning if the expression is unmatched,
but it does create a clear expectation that we are synthesizing priority logic. Just to be clear, 'a' should
equal b, c, d, or possibly more than one of these. If it doesn't you get a warning from simulation. You
expect to get priority decoding logic synthesized.

68 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

unique if

module M3 (input [3:0] state, output logic [1:0] f);

always_comb
5
unique if (state[0])

RTL Processes
f = 0;
else if (state[1])
f = 1;
else if (state[2])
f = 2;
else if (state[3])
f = 3;
endmodule

Run-time warning if no conditions match or more than one condition matches

The alternative to priority is unique. unique enforces complete assignment (like priority) but also
requires that the branches of the if statement are mutually exclusive. In other words, it means that is this
example the synthesis tool can assume that only one bit of the state vector will be hot (else a warning
during simulation). In other words, it means the conditions of a unique if can always be decoded in
parallel, so the synthesis tool can minimize the logic because it doesn't have to generate priority logic to
arbitrate between conflicting conditions.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 69


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

unique case

module M4 (input [3:0] state, output logic [1:0] f);


5 always_comb

unique case (1'b1)


RTL Processes

state[0]: f = 0;

state[1]: f = 1;

state[2]: f = 2;

state[3]: f = 3;

endcase

endmodule

Run-time warning if no conditions match or more than one condition matches

And finally, unique case. Again, the keyword unique allows the synthesis tool to assume that exactly one
bit of the 4-bit state vector is hot, and so to minimize the logic. If there are no matches or more than one
match you will get a warning from simulation.

70 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Wild Equality Operators

module M5 (input logic [3:0] a,


output logic [3:0] f);
always_comb
5
unique if (a ==? 4'b1xxx)

RTL Processes
f = 3;
else if (a ==? 4'b01xx)
f = 2;
else if (a ==? 4'b001x)
f = 1;
else if (a == 4'b0001)
f = 0;
endmodule

==? !=? X and Z on right-hand-side mean don't care

Otherwise, mismatch if any two bits differ

Systemverilog includes new wild equality operators. Wild equality allows the kind of pattern matching you
can do with a Verilog casex to be done using an if statement instead: x and z are wild cards. We can now
use if statements and case statements interchangeably in many contexts. The wild equality operator is
much cleaner than the casex, because it is only an x on the right-hand-side that counts as a wild card; an
x on the left-hand-side represents itself. You can see in this example that always_comb and unique are
used to state explicitly that we are describing combinational logic using an if statement in which one and
only one condition matches each time around.

Both x and z count as don't care values, but only on the right hand side of the operator: if x or z should
appear in the left hand operand, they count as themselves. After accounting for don't cares on the right
hand side, if two vectors differ in any bit position then ==? returns false (and !=? returns true). As is the
case with the logical equality operators (== and !=) it is still possible for the wild equality operators to
return an unknown result; this may happen if the left-hand operand includes x or z values.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 71


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

case inside

module M6 (input logic [3:0] a,

5 output logic [3:0] f);

always_comb
RTL Processes

unique case (a) inside

4'b1???: f = 3; Can have a list of ranges


[4'b0100:4'b0111]: f = 2;

4'b001?: f = 1;

4'b0001: f = 0;

endcase

endmodule Same asymmetric wildcard matching as ==?

SV-2005, but not yet supported by all tools

The case inside statement is, in effect, a replacement for Verilog's casex and casez. Unlike the
traditional casex and casez, case inside uses the same asymmetric wildcard matching as the ==?
operator. That is, it is only x's and z's in the case statement branches that count as wildcards, not x's and
z's in the expression at the top of the case statement.

Note that although case inside has been part of the SystemVerilog standard since 2005, it is still not
supported by all synthesis tools.

72 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

inside Operator

module M7 (input logic [3:0] a,

output logic f, g); 5


always_comb

RTL Processes
begin

{f, g} = 0;

if (a inside { 4'b0??1 } )

f = 1;

if (a inside { [0:3], [12:15] } )

g = 1;

end
Same asymmetric wildcard matching
endmodule

End

inside can also be used as a standalone operator, independent of any case inside statement. It uses the
same asymmetric wildcard matching as case inside and ==?. Note that when inside is used as a
standalone operator, the curly braces {} are a necessary part of the syntax.

We will also see the inside operator used to define randomization constraints.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 73


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

5
RTL Processes

74 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

RTL Types

Aim

• To explain the RTL features of


SystemVerilog (assumes you already know
how to write RTL Verilog or VHDL)
6
Topics covered

RTL Types
• Synthesizable data types
• Enums
• Packed structs, unions, and arrays
• Packages, ports and parameters
• Synthesis of interfaces

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 75


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Synthesizable Data Types


• 4-state types
• logic, integer
• enum
• packed array/struct/union
6
• 2-state types

RTL Types

bit, byte, shortint, int, longint


• enum
• packed array/struct/union

• signed, unsigned
• typedef to any of the above
• Array of any of the above

At the time of writing, many synthesis tools have at least some support for SystemVerilog, and some have
excellent support. For details for a particular tool, please refer to that tool’s documentation. The slide
shows which data types are likely to be supported for synthesis.

76 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Enums for Finite State Machines


Values 0 – 5 of type int
typedef enum
{IDLE, REQ, WAITING, READ0, READ1, WRITE0} state_t;

state_t state;

always_ff @(posedge reset or posedge clk)


begin
if (reset)
6
begin
state <= IDLE;

RTL Types
WE <= 0; Non-blocking assignments
RE <= 0;
end
else
begin
case (state)
IDLE: begin
WE <= 0;
if (enable) state <= REQ;
end
REQ: begin
Assigning state some other value would be an error

We can use enums to define the state variable for an FSM. In Verilog you would have to define a set of
parameters, one for each state. In SystemVerilog you can use an enum type to give a name and value to
each state.

Each enum is associated with a base type, which must be one of SystemVerilog’s integral types. The
default base type is int and the enumeration values are numbered counting up from 0. So in the example
shown here, state_t is equivalent to a 32-bit 2-state integer with the enumeration values IDLE through
WRITE0 having the numerical values from 0 through to 5, respectively.

Using an enumeration type in this way helps to catch errors, because assigning the variable state any
value other than the six valid enum values would be caught as an error by the compiler.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 77


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Base Types and Values

Base type

typedef enum logic [5:0]

6 {IDLE = 6'b100000, REQ = 6'b010000, WAITING = 6'b001000,

READ0 = 6'b000100, READ1 = 6'b000010, WRITE0 = 6'b000001}


RTL Types

state_t;

state_t state; Default value = default for base type = 6'bx

You can explicitly define the bit pattern used to represent each state within the enum. This is part of the
SystemVerilog language rather than being some kind of comment or synthesis directive. Also, you can
specify the 'base type' of the enum – logic [5:0] in this case. Since the base type is logic, the default
value of the state variable will be 6'bx, which is neat when describing hardware. (Compare this to VHDL
where the variable always gets initialized to one of the values of the enumeration type.)

78 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Rules for Overriding the Enum Values


Base type = int Initial value = 0

0 1 2 7 8 9 6 5
enum { aa, bb, cc, dd = 7, ee, ff, gg = 6, hh = 5 } variable;

Start from 0 Count from 7 Can be out-of-order


6

RTL Types
Initial value = 0
3 4 5
enum { jj = 3, kk, mm } ano_variable;

4-state x and z allowed Too wide Duplicate

enum logic { pp = 1'b0, qq = 1'bx, rr = 2'b11, ss = 1'b0}


logic_variable = 1'b1;

Each enumeration value is unambiguously associated with a numeric value. By default the enumeration
values count up from 0 but they can be set explicitly to any value, in any order, providing there are no
duplicates and the values are within the range of the base type.

The default initial value of a variable of an enum type is the default value of the underlying type, not the
first value of the enum type. This means that a variable of an enum type may have an initial value that is
not a legal value for the type. You should be careful to initialise such variables explicitly.

There is a particular potential problem if you are using enums for synthesis. The problem is that synthesis
tools may not follow the simulation semantics. It is best to play safe and not make any assumptions in
your code about what values the synthesis tool may use.

The values must be appropriate for the type. If a value includes a bit width, it must be exactly correct for
the type. If a type is not specified explicitly, it may be specified implicitly by using a sized value – again all
the sized constants in a enumeration must have the same size (unsized value can be used too).

If an enumeration has a 4-state type (i.e. reg, logic or integer), the enumeration constants may include Xs
and Zs. If so, the following constant’s value must be specified explicitly.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 79


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Packed Struct (Review)


signed/unsigned?

struct packed {
4-state packed struct
logic [3:0] x;
int y; x y

6 bit
} vec;
flag;
4 32 1
RTL Types

vec.x = 4'b1111; vec = 7;

vec.y = -1; vec = vec + 1;

vec.flag = 1; vec[36:35] = vec[1:0];

vec = '{4'b1111, -1, 1};


Assignment pattern

As a review of material presented in an earlier section, here is an example of using a packed struct.
Remember that a packed struct can be treated as if it were a C struct, i.e. individual fields can be
accessed using ".". Or, you can write a value for a complete struct using an assignment pattern (like an
aggregate in VHDL). At the same time, because the struct is packed, the whole thing can be treated as if
it were a single one-dimensional vector. Hence the assignment vec = 7 is valid in this example, as are the
part-selects on variable vec.

80 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Packed Union

typedef struct packed { oneAddr 0 1 opcode reg0


bit [ 1:0] flag; 15 14 13 4 3 0
bit [13:4] opcode;
bit [ 3:0] reg0;
} oneAddr_t; twoAddr 1 opcode reg1 reg0
15 14 87 4 3 0
6
typedef struct packed {
bit flag; instruction_t i;

RTL Types
bit [14:8] opcode;
bit [ 7:4] reg1; i.oneAddr.flag = 2'b01;
bit [ 3:0] reg0; i.oneAddr.opcode = 10'h3ff;
} twoAddr_t; i.oneAddr.reg0 = 4'h1;

i.twoAddr.flag = 1'b1;
i.twoAddr.opcode = 7'h7f;
typedef union packed { i.twoAddr.reg1 = 4'h2;
oneAddr_t oneAddr; i.twoAddr.reg0 = 4'h1;
twoAddr_t twoAddr;
} instruction_t; i[7:0] = 0;

SystemVerilog takes this further with packed unions. You may remember unions from C, which 30 years
ago could be used to save memory by re-using it for multiple purposes. SystemVerilog packed unions are
useful in hardware for interpreting the contents of a vector in different ways. In this example we have two
packed structs defining two different instruction formats for the same 16 bit register, and a packed union
that combines these two structs into one. Then when you access the variable i you can pick which
interpretation you want by writing i.oneAddr or i.twoAddr then picking a field from the corresponding
struct. Because the datatypes are packed, you can always fall back to treating the variable like a regular
SystemVerilog vector, i[7:0], ignoring the fields.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 81


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Multidimensional Arrays

• Packed multidimensional arrays help avoid index arithmetic errors

logic [1023:0] vec;


...
6 vec[((row * 8) + col) * 16 +: 16] = 16'hffff;
vec[((row * 8) + col) * 16 + bitn] = 1'b0;
RTL Types

Versus
logic [7:0][7:0][15:0] arr;
...
arr[row][col] = 16'hffff;
arr[row][col][bitn] = 1'b0;

Packed types are in general preferable to unpacked types for synthesis, because packed types are
always synthesizable in a very straightforward way. It is not just packed structs and packed unions that
can be useful. Packed multidimensional arrays can be useful, particularly to avoid making errors in the
index arithmetic when describing structures.

Compare the upper and lower examples on this slide. Declaring rows and columns as separate array
dimensions is far more intuitive than having one long vector.

82 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Odds and Ends

logic [7:0] vec;


int i;

vec = 8'(i); Bit width cast


6
vec = '1; 8'b11111111
Unsized bit values

RTL Types
vec = 'X; 8'bxxxxxxxx

Streaming operators
i = { >> { 32'hdeadbeef }}; No-op

i = { << { 32'hdeadbeef }}; Reverse the bits


i = { << 4 { 32'hdeadbeef }}; 32'hfeebdaed
i = { shortint { 32'hdeadbeef }}; 32'hbeefdead

A bit width cast changes the number of bits in a vector, truncating or extending the vector. The vector will
be truncated or extended anyway in most contexts, but using the bit width cast makes your intent more
explicit and may avoid warnings from synthesis or linting tools.

An unsized value is a single bit value that gets sized according to the context in which it is used, with
every bit being set to the given bit. The value can be '0 '1 'x 'X 'z or 'Z.

The streaming operators may be used to reverse the order of a stream of bits, with the further useful
feature that you can specify the chunk size that is used during the reversal. By default, << simply reverses
at the bit level. These operators may also be used to create packed values from unpacked values.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 83


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Nets, Ports and Data Types


• Nets/wires can have any 4-state type

typedef enum logic [1:0] {X, Y, Z} T;


wire T ctrl;

6
wire struct {logic [7:0] x, y; } coord;
RTL Types

• Variable ports can have any data type

output enum {true, false} flag;

• Beware tool support issues!

Nets/wires in SystemVerilog can have any 4-state type, including enums and user-defined types, but a net
cannot have a 2-state type (e.g. bit, int).

Ports that happen to be variables can have any data type, because they are variables. Ports that happen
to be nets must have 4-state types.

Although the code examples above are legal according to the SystemVerilog standard, you should
beware tool support issues. Not all tools support SystemVerilog-style types for ports and nets, and the
advantages of using them are not that great anyway.

84 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Types and Packages


package types_pkg;
typedef logic [7:0] data_t; Packed dimension
endpackage 7:0

module mem #(parameter asize = 8) (


input
input
input
logic clk, RE, WE,
logic [asize-1:0] addr,
types_pkg::data_t wdata, 2asize
6
output types_pkg::data_t rdata
);

RTL Types
types_pkg::data_t memory [(2 ** asize)-1:0];

always_ff @(posedge clk) Unpacked dimension


begin
if (WE)
memory[addr] = wdata;
if (RE)
rdata <= memory[addr];
end
endmodule: mem

Here is an example showing a type being imported from a package into a module that describes a
memory. The type of the data busses is declared in the package. The memory module is parameterized
with the width asize of the address bus. The memory variable uses the user-defined data_t from the
package, prefixed using the scope resolution operator ::. The memory array has one packed dimension
an one unpacked dimension.

Given this code, synthesis tools may generate a lot of flip-flops and multiplexing, or may actually infer a
RAM. As it happens, both the Altera and Xilinx synthesis tools infer a RAM from this particular code.

Note the use of always_ff for the synchronous procedure.

Note the use of the SystemVerilog exponentiation operator **

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 85


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Type Parameters
This module works on any data type
module ShiftReg
#(parameter depth = 2, parameter type T = logic)
(input clk, input T data, output T Q);

6 T SR[1:depth];
Ports and variables of parameterised type

always_ff @(posedge clk) begin


SR[1] <= data;
RTL Types

for (int i=1; i<depth; i++) SR[i+1] <= SR[i];


end
assign Q = SR[depth];
endmodule

Parameterised module instance


ShiftReg #(.depth(4), .T(int))
IntShifter (.clk, .data(dIn), .Q(dOut));

SystemVerilog significantly extends the Verilog parameter system by allowing modules to have type
parameters, as shown on the slide. This facility allows us to do many of the things that C++ does using
type templates.

In the example, the internal delay line has depth elements each of type T. The code in the module does
not manipulate the objects of type T in any way, but merely copies them from place to place; it would
therefore work correctly regardless of the type used for T. Our module can thus be parameterised for the
type of data it processes, making it easier to re-use.

A default type must be supplied, in the same way that ordinary parameters must have a default value.

The named parameter association in the module instance makes it easy to see how the parameters are
set.

86 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Synthesis of Interfaces (Review)

master_inst slave_inst

module master( pins_inst module slave(


pins pif pins pif
interface pins (input clk);
); );
logic RE, WE;

always ...
pif.RE <= 1;
logic [7:0] addr;
types_pkg::data_t wdata;
types_pkg::data_t rdata;
always @(...pif.clk)
... = pif.RE;
6
endinterface
...

RTL Types
endmodule endmodule

module top (...);


logic clk;
pins pins_inst ( .clk );
master master_inst( .pif(pins_inst) );
slave slave_inst ( .pif(pins_inst) );
endmodule

In cases where interfaces are used as a way to create a bundle of wires (or variables), interfaces can be
synthesized. This slide illustrates an interface being used in a rather straightforward way to encapsulate a
standard bus interconnect scheme. Clearly, if the connected modules are themselves synthesisable, it
would make sense for the connecting interface to be synthesisable too.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 87


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Synthesis Results (Review)

master_inst Outputs clk Inputs slave_inst

module master( module slave(


pins pif pins pif
RE
); );
WE

6 always ...
pif.RE <= 1;
addr
wdata
always @(...pif.clk)
... = pif.RE;
... rdata
RTL Types

endmodule endmodule

Input Output
module top (...);
logic clk;
pins pins_inst ( .clk );
master master_inst( .pif(pins_inst) );
slave slave_inst ( .pif(pins_inst) );
endmodule

The interface is indeed synthesisable. Current synthesis tools that support interfaces do so by
"exploding" the interconnect captured in the interface, declaring individual signals in the enclosing (top-
level) module. Naturally these signals must be renamed; typically the interface and modport names are
concatenated with underscores to form a prefix.

It is useful to try a very simple example like this on your synthesis tool, and get the tool to write out a
standard Verilog netlist after it has performed SystemVerilog analysis (compilation) and elaboration, but
before it has begun its optimisation. In this way you can see exactly how the tool interpreted your
SystemVerilog code.

88 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Multiple Drivers on a Bus

APB.Master modport APB Interface


instance

PCLK,
APB APB
PSEL etc.
Master
(module) PRDATA
Slave
(module) 6

RTL Types
Tristate bus? Multiplexer? Wire-OR?

• Multiple drivers on net: OK for


synthesis, bad for hardware APB
• Multiple deposits to variable:
APB.Slave
Slave
good modelling, not (module)
modports
synthesisable

There is a significant problem associated with the use of interfaces to model a typical modern on-chip
bus. Such buses usually have a "bus fabric" or other switching scheme to determine which of several
possible sources drives a given signal. Even in the very simple bus, such as APB, the PRDATA signal
can be driven by any one of the slaves. And yet when multiple module instances are connected to an
interface instance through an interface port, each of those module instances will access precisely the
same variables in the interface. In the diagram above, what you want is for each slave module instance to
access different bits of the bus structures in the interface, for example, different bits of the PSEL vector.

There is a straightforward approach to modelling this, which involves coding the bus fabric as a regular
module and using an interface only for the point-to-point connections between bus fabric and attached
masters and slaves, which has the big advantage that it synthesises correctly today in all tools that handle
SystemVerilog interfaces. However, this solution is clumsy if you need to parameterise the number of
masters or slaves. On the next few slides we present a more flexible solution based on the use of
modport expressions.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 89


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

How to Differentiate Connections?


interface pins (input clk);
...
module master( types_pkg::data_t [3:0] wdatav; module slave(
pins.mas pif types_pkg::data_t [3:0] rdatav; pins.sla pif
); );
modport mas (

6 module master(
pins.mas pif
input clk, rdata,
output RE, WE, addr, wdatav); module slave(
pins.sla pif
); modport sla ( );
input clk, RE,WE, addr, rdatav,
RTL Types

output rdata);
endinterface

module top (...);


logic clk;
pins pins_inst ( .clk );
master master_inst1 ( .pif(pins_inst.mas) );
master master_inst2 ( .pif(pins_inst.mas) );
slave slave_inst1 ( .pif(pins_inst.sla) );
slave slave_inst2 ( .pif(pins_inst.sla) );
endmodule

The issue is that we typically want different instances of a given component (e.g. master or slave) to
access different connections. In this example, wdatav and rdatav have become arrays-of-vectors,
because each master has its own wdata, and each slave has its own rdata. Each master needs to write
to a different element of the wdatav array. But as things stand each master is connected to the interface
in the same way, so there is no nice way to have each master instance access different elements of the
arrays.

90 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Modport Expressions

module master( interface pins (input clk);


pins pif ...
module slave(
); types_pkg::data_t [3:0] wdatav;
pins pif
always ... types_pkg::data_t [3:0] rdatav;
);
wdata <= ...
modport mas0 ( ... Same
module master(
pins pif
output .wdata(wdatav[0]) );

modport mas1 ( ...


module slave(
pins pif
6
); );
output .wdata(wdatav[1]) );
always ...

RTL Types
...
wdata <= ...
endinterface

Port identifier Modport expression


module top (...);
logic clk;
pins pins_inst ( .clk );
master master_inst1 ( .pif(pins_inst.mas0) );
master master_inst2 ( .pif(pins_inst.mas1) ); Connect different
slave slave_inst1 ( .pif(pins_inst.sla0) ); modports
slave slave_inst2 ( .pif(pins_inst.sla1) );
endmodule

There is solution, and that is to use 'modport expressions'. A modport expression is effectively an alias for
modport. The master module uses the wdata modport expression. Each master accesses a different
element of the wdatav array by accessing the interface through a different modport. The modports are
selected when the interface port is connected (don't want to fix the choice of modport when declaring the
interface port).

That's fine, but it does mean declaring a whole list of differently-named modports, which could become a
nuisance, especially if the number of connections were parameterized. There is an even better way,
shown on the next slide.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 91


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Modport Expressions with Generate


interface pins (input clk);
module master(
pins pif ... module slave(
); pins pif
always ... for (genvar i = 0; i < 4; i++) );
wdata <= ... begin: blk

6 module master(
pins pif
modport mas ( ...

end
output .wdata(wdatav[i]) ); module slave(
pins pif
); );
always ... ...
RTL Types

wdata <= ... endinterface

Not supported by all tools


...
pins pins_inst ( .clk );

for (genvar i = 0; i < 4; i++)


begin: gen
master master_inst ( .pif(pins_inst.blk[i].mas) );
end
... End

You can use a generate statement to create the modports. (The keyword generate is optional in
SystemVerilog and in Verilog-2005 and is not used here.) There is now only a single modport declared
inside a generate, where the genvar selects which element of wdatav is used in the modport
expression. We end up with a bunch of modports each within a different named block that we can access
by hierarchical name from outside. You can see that at the bottom where we connect the interface port
(pif) by naming a particular modport.

92 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Clocking Blocks

Aim

• Develop a consistent and reliable timing model


for connecting a SystemVerilog testbench to
the design-under-test

Topics covered

• Clocking Blocks
7
• Input and output skew

Clocking Blocks
• Clocking drives and synchronization
• #1step sampling
• Signal aliasing
• Clocking blocks versus programs

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 93


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Basic Testbench

DUT
Stimulus Stim Resp Checking
generation input output

clk
Clock events Clock events

7 Basic Testbench

initial begin always begin


Clocking Blocks

@(posedge clk) @(posedge clk)


#2 InA <= ... #7 assert ( OutC == ExpC );
#3 InB <= ... #1 assert ( OutD == ExpD );
... ...
Inputs/outputs driven/sampled at various times

A standard Verilog testbench is usually a module that contains an instance of the design under test (DUT)
and procedures (initial or always) to provide test stimulus and check the design’s response. The
testbench has to provide test stimulus and sample the response at appropriate times with respect to the
system clock. This is relatively straightforward to code, but is clumsy, because the timing strategy must be
applied explicitly for each drive and sample. Consequently, control of timing is scattered throughout the
testbench and is hard to modify.

94 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Clocking Blocks
• Adds gate-level delays to inputs and samples output
• Isolates verification environment from SystemVerilog DUT/RTL scheduling

Test harness (module)


DUT
Stim Resp
Stimulus input output Checking
generation and
Coverage
7
clk
Clock events Clock events

Clocking Blocks
Clocking blocks

SystemVerilog verification environment

Hardware verification languages (HVLs) such as e and Vera have no knowledge of simulation time, but
can receive events from the simulation and these events can be used to trigger a burst of activity in the
HVL environment. To implement this, HVL vendors provide some mechanism to link Verilog signals to
variables in the HVL world. These bridge blocks – ports in the e language, interfaces in Vera – are a
convenient place to encapsulate the detailed timing of driving and sampling.

clocking
Although SystemVerilog is a unified language, we nevertheless need a similar way to separate the
verification code from the design under test. SystemVerilog achieves this using a clocking block, a
specialised SystemVerilog construct that provides centralised control of the timing of a group of signals all
related to the same clock.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 95


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Test Harness

module Design ...

module Harness;
Design DUT (...);
// Clock generator ...
// Clocking blocks

7 module Testbench;
// Doesn’t instance DUT
endmodule

// or Harness
Clocking Blocks

endmodule
Top-level modules
Harness.name...
$root.Harness.name...

The test harness is a module, which contains an instance of the design, the clocking block(s) and a clock
generator. The verification environment is also a module, although it could be a program (programs are
discussed later.) Both the test harness and the verification environment module could be at the top-level
($root), communicating using hierarchical names.

96 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Top-Level Module

module Design ...

module Harness;
Design DUT (...);
// Clock generator ...?
// Clocking blocks
module Testbench;
// Doesn’t instance DUT
endmodule 7
// or Harness

Clocking Blocks
module Top;
endmodule
Testbench TB_inst ();
Top.Harness_inst.name...
Harness Harness_inst ();
$root.Top.Harness_inst.name...
// Clock generator ...?
endmodule

Top-Level Module
Alternatively, the test environment and the test harness could themselves be instanced in a single top-
level testbench module. If this were the case, it would also be possible to include the clock generator in
this top-level module.

Whether or not you have this top-level module is a matter of personal preference. It doesn’t affect the
overall structure of the test environment.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 97


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Clocking Block Syntax

DUT logic From_DUT, To_DUT;

To_DUT From_DUT
clocking my_cb @(posedge clk);
3ns t t 2ns input #2ns From_DUT;
output #3ns To_DUT;

7 Q

D Q
D
endclocking
clockvar name =
my_cb.variable/net name
Clocking Blocks

clockvars

• Clocking inputs are DUT outputs


• Input clockvars can only be read, output clockvars can only be written

SystemVerilog’s clocking construct addresses the issue of driving and sampling a design’s inputs and
outputs in a testbench.

Test inputs are driven with a specified output skew delay after the clock. Test outputs are sampled with a
specified input skew before the clock. (Note that the input/output directions are relative to the testbench,
not the design.)

Here is a declaration of a clocking block. This diagram shows when driving and sampling occurs.

Unlike in modules, where you can “break the rules” and assign values to inputs and read the values of
outputs, clocking blocks are stricter – clocking inputs may be sampled, but must not be driven; clocking
outputs may be driven but must not be sampled. If you want to both drive and sample a signal, you would
declare it inout and/or provide input and output skew values.

98 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Input and Output Skew


clocking my_cb @(posedge clk);
input #2ns From_DUT;
output #3ns To_DUT;
endclocking
Input Output
skew skew

clk

2ns 3ns
7
From_DUT

Clocking Blocks
my_cb.From_DUT
clockvars
my_cb.To_DUT

To_DUT

Clocking inputs (design outputs) are sampled before the clock – the input skew value (2ns in the example)
specifies how far before. Sampling occurs in the Postponed region – at the very end of the time step,
when $strobe and $monitor are executed.

Clocking outputs (design inputs) are driven after the clock – the output skew value (3ns in the example)
specifies how long after. Clocking drives are scheduled in the NBA region of the appropriate future
timeslot.

Both the variables connected to the DUT and the clockvars will be available in the simulator, so you can
add them to a waveform display, for example.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 99


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Creating a Clocking Block


module test_harness; test_harness
logic clk, To_DUT, From_DUT; DUT
DUT m (clk, To_DUT, From_DUT); To_DUT From_DUT

clk
t t

clocking my_cb @(posedge clk); Q D


input #2 From_DUT; D Q

output #3 To_DUT;

7 endclocking

initial begin: Clock_Generator Stimulus Output


Clocking Blocks

... generator checking


endmodule
Where to put these?

• Clock generated in test harness, NEVER on the testbench side of a


clocking block

Typically, a SystemVerilog testbench will include an instance of the device under test (DUT), a clocking
block, a stimulus generator and a response checker. The clocking block acts as an interface between the
DUT and the stimulus/response.

In the example shown, a clocking block called my_cb is declared. This is clocked on the rising edge of
the clock. The input (sampling) skew is #2 and the output (driving) skew is #3. The clocking declaration
also acts as an instance of the clocking block.

clockvar
By prefixing the names To_DUT and From_DUT with the name of the clocking block (my_cb), we have
access to the clockvars defined by the clocking block. When a testbench writes to an output clockvar, or
reads from an input clockvar, the timing defined by the clocking block will be used.

Your testbench can also “go round the back” of the clocking block and access the signals (such as
To_DUT and From_DUT) directly. However, this is strongly discouraged – the clocking block is there to
provide managed access to those signals, with timing and direction appropriately controlled, and it is in
most cases quite inappropriate to subvert that mechanism. Clocking blocks can usefully be added to
interfaces, as we will see shortly, and modports can be used to help enforce access through the clocking
block.

This example shows the clocking block inside a module. Clocking blocks may also be used inside
interfaces.

100 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Testbench and Clocking Block


module test_harness; test_harness
logic From_DUT, To_DUT;
DUT
...
clocking my_cb @(posedge clk); To_DUT From_DUT

input #2 From_DUT; clk


t t
output #3 To_DUT;
endclocking Q D
D Q
endmodule

module Tester;
initial begin synchronize clocking drive 7
@(test_harness.my_cb)
test_harness.my_cb.To_DUT <= 0;

Clocking Blocks
...

initial begin synchronize sense


@(test_harness.my_cb)
if (test_harness.my_cb.From_DUT != 0) $display ("BAD");
endmodule : Tester
clockvar

This is how a testbench using a clocking block could be structured. Later we will see some important
guidelines about reliable, race-free sampling and driving of signals through a clocking block. At this
stage, though, we merely note that the testbench takes care to reference DUT signals only by means of
the clocking block; it accesses clockvars, and never touches the signals directly.

The test module Tester contains initial blocks to drive the stimulus and check the response. The top-level
test harness module test_harness instances the design, the clocking block and the clock generator. It
would also be possible to have it instance the tester module, but in this example we have chosen to keep
the two blocks separate and to simulate them together as parallel hierarchies that communicate through
cross-module reference.

Clocking drive (also called Synchronous Drive)


Any assignment to an output clockvar must use the nonblocking assignment operator <=. However, this
is not truly a nonblocking assignment; it’s a special operation, unique to clocking blocks, known as a
clocking drive.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 101


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Cycle Delays and Clocking


• To wait for N clock cycles

task automatic cycles(int unsigned N);

repeat (N) @(test_harness.my_cb); Synchronize


endtask

7 cycles(1); Always sync before driving or sampling clockvar

test_harness.my_cb.To_DUT <= 1; Clocking drive


Clocking Blocks

• NEVER synchronize on the clock event directly outside of clocking

@(test_harness.my_cb) ...

@(posedge test_harness.clk) ...

In the testbench, clocking block signals are driven or sampled in specific cycles, with the exact timing
specified in the clocking block. To wait for the next cycle of the clocking block’s clock event, you can
apply cycle delays by using the clocking block's name in an @ timing control, just as though it were an
event. To wait for multiple clock cycles, just write a loop.

A clocking drive should always be synchronized to the clocking block. Asynchronous clocking drives
simply do not work!

It is very important that your testbench code should never wait for the original clock event. Doing so could
give rise to obscure and hard-to-debug race conditions between your testbench and clocking block input
sampling behaviour. Whenever you want your testbench to wait for the next clock event, use
@(clocking_block_name) as a procedural delay. This fits naturally with the idea that a testbench should
do all its synchronous driving and monitoring through a clocking block rather than by direct manipulation
of signals.

102 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Input and Output Skew Syntax Summary


Unless specified,
default input skew is #1step,
clocking cb @(posedge clk);
default output skew is #0
default input #1step output #2ns;

input A; input #1step, same sampling as assertions


output B; driven 2ns after posedge clk
input #1ns C, D; sampled 1ns before posedge clk
output negedge E; driven on negedge clk 7
input #0 F; sampled after nonblocking assignments update

Clocking Blocks
output #0 G; driven in Re-NBA region of current time step

input #1ns output #1ns H; TWO clockvars: one input, one output

inout I; shorthand, timings must be default

endclocking
Doulos strongly discourages use of input #0

The default clocking input skew value is #1step. An input skew of this value means that the input is
sampled in the Postponed region of the immediately preceding time step. The effect of this is exactly as if
the sampling were to take place in the Preponed region of the current time slot. Which has the useful
effect that signals sampled through a clocking block using #1step will yield exactly the same values as are
seen in assertions. The default output skew is #0. This means the clocking output is driven in current time
step – in the re-NBA region.

default : Defaults are overridden with a default statement. Regardless whether the default has its
standard value or a user-specified default, the skew for inputs and outputs can be specified in the input or
output declaration.

input #0 : An input skew of #0 has a special meaning – the input will be sampled in the Observed region
of the current time step. We discourage the use of this feature because its sampling is affected by any
non-zero delays.

posedge : You can also use posedge or negedge for the skew value, in which case the sampling or
driving timeslot is determined by the corresponding edge of the clock signal, rather than by a fixed time
delay. This provides a convenient way to drive DUT inputs on the inactive clock edge.

inout : Inouts have both an input and an output skew. An inout declaration is used to declare a clocking
inout with default skew. To override the default, use the input skew output skew syntax. Note, however,
that inout does not truly create a bi-directional clocking signal; it creates two independent structures in the
clocking block, that are hooked to the same external signal. Two separate clockvars are created,
although they have the same name! In practice this causes no difficulties because it is impossible to read
an output clockvar or to write an input clockvar, so it’s always obvious which of the two clockvars you are
accessing.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 103


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Simplified Scheduling Model


From previous time slot • Time slot is divided into regions

Preponed
sample values for assertions

Active =
#N ... #N ...
Events Events
created by created in
7 earlier slots
<= #N NBA
<=

<= #N
future slots
Clocking Blocks

evaluate assertions Observed


if triggered $strobe, Creation
$monitor of events

Postponed
Execution
To next time slot

To understand the operation of clocking blocks it is helpful to know how a SystemVerilog simulator
manages its activity at any given moment. This behaviour is standardized across all simulators. When all
current events have been processed, the simulator advances time to the next moment at which there is
activity scheduled. The activity performed by a simulator at a given moment is known as a time slot.

This diagram shows a simplified version of the scheduling model. The key point is that nonblocking
assignments place events in the NBA region; these events are only activated once all the events
generated by blocking assignments have been processed.

The diagram also shows where assertions fit in. Values used by assertions are sampled in the preponed
region of the time slot, before any activity takes place. Any triggered assertions are evaluated in the
observed region, after the event activity has been processed. The postponed region is used by the
system tasks $strobe and $monitor, just before time advances to the next slot.

104 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Scheduler Regions
From previous time slot Preponed Assertion and #1step sampling

Active

#0
Modules Inactive <=
$strobe,
$monitor
NBA

Assertions Observed 7
Re-active

Clocking Blocks
#0
Programs Re-inactive <=
SVA actions
Re-NBA Assign clockvars

Postponed To next time slot

Preponed – Variables used in assertions are sampled, and #1step sampling occurs for clocking block
inputs.

Active – Ordinary code runs in this region, including processes and continuous assignments.

Inactive – Contains events that are scheduled to occur after a delay of #0. When the active region
becomes empty, any events in the inactive region are promoted to the active region.

NBA – Contains non-blocking assignments. When the active and inactive regions are both empty, any
non-blocking assignments are promoted to the active regions for completion.

Observed – Properties and assertions are executed in this region (using variable values sampled in the
preponed region).

Reactive – Procedural code contained within programs (as opposed to modules) and the action blocks
associated with concurrent assertions are executed in this region.

Re-inactive – is to the reactive region what the inactive region is to the active region.

Re-NBA – is to the reactive region what the NBA region is to the active region. Clocking block outputs
with #0 delay are assigned in this region.

Postponed - $monitor and $strobe run in this region just before the simulator advances to the next time
step.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 105


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Stimulus and Response


module Testbench; module TH;
initial logic [7:0] D, Q; bit clk;
forever @(TH.cb) begin DUT m (clk, D, Q);
clocking cb @(posedge clk);
if (TH.cb.Q !== expected)
input #2 Q; output #3 D;
$display("BAD");
endclocking
TH.cb.D <= stimulus; always #5 clk = ~clk;
... use Q to calculate D? endmodule : TH

7 TB drives... D1 D2 D3 D4
clk
Clocking Blocks

#3 #3 #3 #3 • 2-clock delay

D D1 D2 D3 • Q1 can affect D3,


DUT acts... but not D2
Q Q1 Q2 Q3 • Testbench cannot
#2 #2 #2 #2 react to DUT?
TB reads... ?? ?? Q1 Q2

This timing diagram shows how a clocking block and a testbench can work together to generate stimulus
and check response of a DUT. Thanks to the clocking block we can now reliably drive and sample DUT
signals at the appropriate times. However, we have introduced a 2-cycle latency between the testbench
applying a stimulus and seeing the corresponding response. This makes it a little difficult to create truly
reactive testbenches that observe DUT state and create new stimulus based on that state; in our clocking-
block environment, the newly calculated stimulus will be applied two clocks later, rather than on the next
clock as we would probably desire.

Note that, in our example, reading Q1 and driving D3 happen at the same time. It is entirely possible for
the testbench code to read Q1 first, and use this value to influence the value that’s chosen for D3.
However, it is not possible to use Q2 to influence D3. In our diagram, we cannot see the value Q2 until
the fourth clock event, when we are about to drive D4. This could present a problem in the case of a
design with asynchronous feedback.

The really important idea here is that all testbench activity happens at the clock events. The testbench is
cycle-based.

106 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Signal Aliasing
• Clocking signals may be associated with expressions using local or
hierarchical names

clocking cb @(posedge clk);


output control = {Enable, UpDn, Load};
input #1ns Q = Top.U1.Counter.Q[7:0];
endclocking
7
initial

Clocking Blocks
begin
@(TH.cb) TH.cb.control <= 3'b100;
repeat (12) @(TH.cb);
assert (TH.cb.Q === 8'b00001010);
...

All the examples so far have declared existing names as clocking inputs and outputs. It is also possible to
declare a clocking input or output to be an alias for an expression. The expression may involve
hierarchical names and must be legal in a port connection to a port that has the same direction as the
clocking signal.

In the example, the output control is associated with the expression {Enable, UpDn, Load}. The
assignment TH.cb.control <= 3’b100 sets Enable to 1, and UpDn and Load to 0.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 107


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Multiple Clocking Blocks


• Can have > 1 clocking block per variable
• For a variable, the last clocking drive wins

clocking rise @(posedge clk); clocking fall @(negedge clk);


output data; output data;
endclocking endclocking
7
initial begin no drive here, so no update
Clocking Blocks

...
@rise rise.data <= 4'b1010;
@fall fall.data <= 4'b1100; 1010 1100

end

Many designs have more than one clock. A separate clocking block can be declared for each clock.

The same variable might appear as an output in two or more clocking blocks. An important application of
this is for double data rate memory (DDR SDRAM) interfaces, as shown here. The feature would also be
useful if a general-purpose bus was connected to different clock domains in a system.

If the same variable is driven from two different clocking blocks, there is no contention – the behaviour is
“last one wins” as when clocking blocks aren’t being used. Effectively, the clocking blocks take turns to
make assignments to their target signal.

Note that each clocking block updates its target variable only on those cycles where there has been a
clocking drive. If there is no clocking drive to a clockvar at a given cycle, the corresponding variable is not
updated.

108 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Driving a Net
• Clocking drives nets or variables (hides the difference from the testbench)

W L
implicit continuous assign

implicit internal variable

wire W;
logic L; 7
clocking cb @(posedge clk); t t
output #1 W;

Clocking Blocks
output #1 L; Q Q
endclocking TH.cb.L <= 1; D D
TH.cb.W <= 1;
clk
Testbench writes both using clocking drive
cb.W cb.L

Clocking blocks have the very useful feature that their outputs can be either nets or variables. So far, our
discussion has concentrated on the use of variables as outputs from a clocking. This slide illustrates the
behaviour when a clocking output is a net: the clocking block creates a hidden internal variable, and
copies that variable on to the target wire by zero-delay continuous assignment.

The testbench, accessing signals only through the corresponding clockvars, does not need to be aware of
the difference between wires and variables in the DUT or test harness module.

This behaviour means that it is generally not useful to have multiple clocking blocks driving a given wire;
the values from the various clocking blocks would be resolved, just as if they had been driven through
continuous assign statements.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 109


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Clocking Blocks in Interfaces

interface Bus (input clk);


logic [31:0] addr, data;
logic rd, wr;

clocking cb @(posedge clk);


output addr, rdwr = {rd, wr};
inout data;

7 endclocking

modport Test_mp (clocking cb);


endinterface
Clocking Blocks

module Test (Bus.Test_mp theBus);


initial begin
@(theBus.cb)
theBus.cb.addr <= ...
modport enforces the use of clocking
theBus.cb.data <= ...
...

Clocking blocks may be declared in interfaces and in modports. This encapsulates the testbench timing
for the interface in the interface itself, instead of in the testbench. In other words, it forms part of the
reusable verification IP for the protocol modelled by the interface. This is the recommended style.

By using a modport with a clocking block, we oblige the testbench to drive and sample the signals
synchronously.

An interface of this type usually needs to be written specially to capture the connectivity between
testbench and design. It is unlikely that an interface forming part of the design itself could be used in this
manner. Clocking blocks are not synthesisable.

110 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Clocking Blocks versus Programs

• Clocking blocks
• Isolate the test bench from Verilog scheduler issues in the DUT (#1step)
• Encapsulate pin-level timing in the clocking block

• Program blocks add further "reactive" scheduler regions


• Isolate the test bench from Verilog scheduler issues in the DUT
7
• Three choices

Clocking Blocks
Use neither clocking blocks nor programs, just modules
• Use clocking blocks and modules but not programs (recommended)
• Use modules and programs with clocking blocks

End

In Verilog, modules are used to model both the design and the testbench. In SystemVerilog, you can of
course continue to use modules in this way. But SystemVerilog includes program blocks, which are
intended to provide a home for testbench code. By “testbench code” we mean test stimulus and response
checking. Test environments may also contain models of components that interface with the design; these
should continue to be modelled using modules.

Superficially, programs look very much like modules. They may have ports, and are instantiated just as
modules are. They may be declared outside any modules and interfaces. They may also be declared
(i.e. nested) inside modules and interfaces, although at the time of writing there is limited tool support for
nested module or program declarations.

Programs have a number of restrictions compared with modules:


Programs are leaves of the hierarchy – you can’t instance programs inside other programs.
Programs may include initial statements, but not always statements (use initial forever instead).
Variables in programs may only be written using blocking assignments (or continuous assignments).
Non-program variables must be written using non-blocking assignments.

In this course we completely avoid the use of program blocks. Some vendors and writers regard this as
the most appropriate guidance; others strongly recommend the use of programs. If you wish to learn
more about program blocks, you can find further information in the Appendix sections of this course.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 111


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

7
Clocking Blocks

112 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Arrays and Queues

Aim
• To learn about the various data types that
SystemVerilog introduces for verification

Topics covered

• Dynamic Arrays


Queues
Associative Arrays
8

Arrays and Queues

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 113


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Array-like Containers

Array Size fixed at compile-time. Can be multi-dimensional.

Size fixed at run-time whenever new called,


Dynamic Array
elements indexed 0, 1, 2, ...

Queue

8
Associative Array
Arrays and Queues

In this section we are considering three new kinds of class-based container, as shown in the table above.

Despite its name, a dynamic array has a fixed size, but that size is determined each time the dynamic
array is created or re-created by calling new at run-time. In contrast to the dynamic array, the size of a
regular array is fixed at compile-time.

114 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Dynamic Arrays
string names[]; Dynamic array declaration

initial
begin
Create 10 empty strings, names[0] to [9]
names = new[10];
names[0] = "Fred";
... Replace with 20 new strings
names = new[20];
Replace with 3 new strings
...
names = {"Mary", "Mungo", "Midge"};
8
assert( names.size == 3 );

Arrays and Queues


names.delete;
end Dynamic array methods

A dynamic array is one whose size may be set or changed during simulation. Resizing is achieved
automatically by assigning new contents. An array of empty elements can be created using the new[]
operator (known as the "constuctor"). This can be used where the variable is declared:

string names[] = new[5];

There are two dynamic array methods, .size and .delete. .size returns the number of elements currently
allocated. .delete deletes the entire array.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 115


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Copying a Dynamic Array

Initialize
string names[] = new[10];
string more_names[] = {"Peter", "Paul", "Mary" };

initial
begin
... Larger array; keep existing
contents
names = new[20](names);

8 more_names = new[5]({"Mary", "Mungo", "Midge"});


names = new[20](more_names)
end
Arrays and Queues

Must have [size]

When resizing an existing array, new on its own would eradicate the contents. To prevent this, new may
take one argument, which is an expression used to initialize the array. In fact it could be the name of a
different dynamic array – the values are copied.

116 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Array-like Containers

Array Size fixed at compile-time. Can be multi-dimensional.

Size fixed at run-time whenever new called,


Dynamic Array
elements indexed 0, 1, 2, ...

Can insert/delete elements at front/back/middle at run-time,


Queue
but always contiguous and indexed 0, 1, 2, ...

8
Associative Array

Arrays and Queues


In contrast to the dynamic array, the size of a queue really is dynamic. A queue can stretch and shrink at
run-time, and you can even insert or delete elements from the middle of the queue. Regardless of
elements being inserted or deleted, the first element in the queue is always numbered 0, and consecutive
elements are always numbered sequentially: the elements get renumbered each time an elements is
inserted or deleted.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 117


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Queues
• Queue = variable-sized array
• A one-dimensional unpacked array that grows and shrinks automatically

• Declarations:

int addresses[$]; Queue of int

string names[$]; Queue of string


logic [7:0] FIFO[$:1023];
Maximum index – size is 1024
8 • Queues may be initialized

Assignment pattern syntax –


int numbers[$] = {1, 2, 3};
'{1, 2, 3} not required
Arrays and Queues

string names[$] = {"Tom", "Dick", "Harry"};

A queue is an array that has one variable-sized unpacked dimension. It may in addition have one or more
packed dimensions. The difference between a queue and a dynamic array is that a queue is optimized for
adding or removing elements to the existing contents at its front or back.

Queues are declared by specifying the range as [$] or [$:N]. The former is an unbounded queue and the
latter has a maximum index of N, or in other words a maximum size of N+1 elements.

Queues may be initialized when they are declared. If not, they will have no elements.

118 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Working with Queues

int i, numbers[$] = {1, 2, 3};

i = numbers[0]; First element – 1

i = numbers[$]; Last element – 3


numbers = {numbers, 4}; Append
numbers = {0, numbers}; Prepend
numbers = numbers[1:$]; Delete first
numbers = numbers[0:$-1]; Delete last
numbers = {}; Empty queue 8
i = numbers[0]; 0
i = numbers[1'bx];

Arrays and Queues


0

Default int value

Queues may be used just like standard unpacked arrays. The only differences are (i) that you can
reference the last element using the index $ and (ii) that when assigning a queue, you don’t need the
same number of elements in the source and target.

If you use an unknown index – one containing Xs or Zs – or if you use an out-of-range index, the result is
the default value of the queue’s element type. For example, this would be 0 for an element type of int or
1’bx for an element type of logic.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 119


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Queue Methods
Can pass queues as
int numbers[$];
task/function arguments

task printqueue (string name, int q[$]);


$display("Queue %s[$] has %0d members:",
name, q.size); Number of elements
for (int i = 0; i < q.size; i++)
$write(" %0d", q[i]);
$write("\n");

8 endtask
printqueue ("numbers", numbers );

• Also
Arrays and Queues

.delete .insert .pop_front .push_front .pop_back .push_back

As with dynamic arrays, a number of queue methods are defined, one of which, .size, is illustrated here.

These are the queue methods:

function int size() – returns the number of elements in the queue

function void insert(index, item) – inserts item at the specified index position.

function void delete (index) – deletes the specified index

function type pop_front() – removes item from the start of the queue

function type pop_back() – removes item from the end of the queue

function void push_front(item) – inserts item at the start of the queue

function void push_back(item) – inserts item at the end of the queue

120 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Nesting, Assignment Patterns, and %p


typedef struct {
string txt;
byte unsigned b;
int array[]; Dynamic array
} T;
T q[$]; Queue
initial
Nested concatenation
begin
q.push_back( '{"abc", 98, {1, 2}} );
q.push_back( '{"def", 99, {3, 4, 5} );
8
$display("%p", q);

Arrays and Queues


'{'{txt:"abc", b:98, array:'{1, 2}}, '{txt:"def", b:99,
array:'{3, 4, 5}}}
Keys

Just as an example, here we show a dynamic array nested inside a struct nested inside a queue. We
have two calls to push_back to add two elements to the initially empty queue. Each push_back call
takes an assignment pattern nested two-deep to express the value of the struct with its nested dynamic
array.

The call to $display illustrates the %p formatter, which can be used to format the values of structs, arrays,
and queues. The output from %p shows three levels of nesting: array-within-struct-within-queue, with
element tags being used to identify the named fields within the struct.

Note that values for dynamic arrays and queues can be written using concatenation {...} or assignment
patterns '{...}. In the example above, although the nested array values were written using concatenation,
they are printed out in the form of assignment patterns.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 121


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Array-like Containers

Array Size fixed at compile-time. Can be multi-dimensional.

Size fixed at run-time whenever new called,


Dynamic Array
elements indexed 0, 1, 2, ...

Can insert/delete elements at front/back/middle at run-time,


Queue
but always contiguous and indexed 0, 1, 2, ...

8 Can insert/delete elements anywhere at run-time,


Associative Array elements are non-contiguous (aka sparse array),
index type can be integral or string or class type
Arrays and Queues

We can now complete our table.

The associative array allows even more flexibility than the queue in the sense that the elements do not
need to be numbered contiguously: there can be gaps in the element numbering, although because of
this, the element ordering is less well-defined than a queue or an array. The index type of an associative
array is not restricted to an integer: the index can be a string or can be an object handle (part of the class-
based language).

122 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Associative Arrays
• Aka. sparse array or non-contiguous array or map

logic [15:0] sparse_memory [int];


string surnames [string];
int array [byte unsigned];
bit flags [*];
Wild-card index = any integral value

array[1] = 1;
array[10] = 2;
array[100] = 3;
foreach (array[i])
Useful!
8
$display("i = %0d, array[i] = %0d", i, array[i]);

Arrays and Queues


i = 1, array[i] = 1
i = 10, array[i] = 2
i = 100, array[i] = 3

All the arrays we have been looking at have contiguous integral indices. An associative array is one that
does not have a contiguous index – instead it is like a lookup table. The array element corresponding to
the index is not created until a value is assigned to it. Additionally, the index type need not be integral – it
could be string, for example.

An associative array with a wild-card index (i.e. [*]) can have values of any integral type. Two index values
having different widths, even if they have the same numerical value, are considered distinct.

In the example, the assignment to surnames uses an associative array literal, which is a form of
assignment pattern.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 123


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Associative Array Methods

string surnames [string];

surnames = '{ "Joe": "Smith", "Klaus": "Schultz",


"Pierre": "Leblanc" }; Assignment pattern
assert( surnames.size == 3 );

if ( surnames.exists("Joe") )
8 $display( surnames["Joe"] ); Smith

surnames.delete("Klaus");
Arrays and Queues

assert( surnames.size == 2 );
$display( surnames["Jawahar"] ); Warning! ""

A number of methods can be used with associative arrays. These enable you to find out about the array
(how many elements?), its members (has this value been stored?) and enable you to iterate through the
array.

The order in which the elements are stored is in accordance with the index’s data type. For example, if the
index is int, the elements are stored in (index) numerical order; if the index is string, they are stored in
lexical order. For other types, the LRM does not clarify the ordering.

If you attempt to read the value of a non-existent element, the simulator should give you a warning, and
the actual value returned will be the default value of the element type.

124 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Foreach
• foreach is used to iterate through an array’s elements

int numbers[10];
foreach ( numbers[i] )
$display( "numbers[%0d] = %d", i, numbers[i];

• Multi-dimensioned arrays too. (Packed dimensions change most rapidly)

bit [7:0] twodim[10][10];


foreach ( twodim[i, j, k] )
twodim[i][j][k] = 1'b1; 8

Arrays and Queues


Or with one or more subscripts omitted...

foreach ( twodim[ , j] ) twodim[3][j] = j + 1;


End

The foreach loop is used to iterate over the elements of an array.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 125


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

8
Arrays and Queues

126 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Bus-Functional Modeling

Aim

• To learn how to use bus-functional models


to improve the testbench structure

Topics covered

• Bus-Functional Modeling
• Separate Test from Test Harness
• Tasks/functions in interfaces

Bus-Functional Modeling
A BFM allows the testbench developer to model protocol and timing for a particular bus interface, using
method calls from the testbench to drive DUT signals. The aim is to separate what the test is to do from
the detail of how and when to do it. This separation means that changes to the protocol (or swapping in
an entirely new interface) will require no changes to the tests, so long as the method calls are the same.

In this section we use introduce the ‘separation of concerns’ approach as our first step towards a scalable
and reusable methodology for developing testbenches.

The ideas developed in this topic for structuring the test and the test environment will be a good
foundation for later in the course when we are concerned with class based verification.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 127


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Bus Functional Modeling

Bus-Functional Model (BFM) Design-under-Test (DUT)

write( address, data);


Protocol
read( address, data); &
Timing

9 Commands / function calls Pin wiggles


Bus-Functional Modeling

The Bus Functional Model models the bus timing and protocol in a set of methods (tasks and functions)
which drive or respond to bus transactions. The DUT-facing side of the BFM models the timing between
signals accurately, both within a clock cycle by adding set up timing if necessary, as well as modelling
multi-cycle transactions. Control on the test bench side of the BFM is by calls to BFM tasks and functions,
collectively referred to as methods. The processess of going from the more abstract method calls to
physical, timed pin wiggles is an important feature of both BFM and test bench design.

We shall see later in the course how we can then place timing and protocol based checks in the BFM,
allowing other checking within the test environment to concentrate on higher level checking.

128 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Simple Module-Based BFM


Pin-level connections to DUT

module serBFM (output logic serial = 1);

time T_per_bit;

function void set_bit_time (time t);


T_per_bit = t;
Called from endfunction
testbench

task send (input [7:0] v);


serial = 0;
repeat (10) #T_per_bit
{v, serial} = {1'b1, v};
endtask 9
endmodule

Bus-Functional Modeling
This simple example models the bus behaviour with 2 methods which are called from the testbench – a
procedural interface. This BFM merely controls the signal, serial, which connects directly to the DUT.
One method, set_bit_time, controls bit timing, while the task, send, serialises the character to be sent
and models the (multi-cycle) interface protocol.

Even this BFM has two quite separate interfaces:

• A DUT facing port interface, providing accurate signal timing and transitions connecting to the DUT

• A testbench facing procedural interface in the form of a set of methods, so that the testbench can make calls
without concern for the implementation detail or timing.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 129


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Testbench using BFM


Testbench
bfm dut
test1 serBFM Design
module testbench; serial

logic serial;

serBFM bfm ( .serial );


Design dut ( .serial );

initial
begin: test1
bfm.set_bit_time(52083);
#1000;
bfm.send("H");
9 bfm.send("i");
end
Hierarchical references
endmodule
Bus-Functional Modeling

We can now create an instance of the BFM and connect it to the DUT in the testbench module. At this
stage we will assume that the DUT outputs are appropriately connected, we are only concerned here with
driving the serial wire in to the DUT.

The stimulus generation, itself, is defined in an initial block and makes use of the calls to the BFM
methods. To a test writer (who might not be the same person as the testbench developer), using this API
to control the DUT is more intuitive than having to be concerned with the details of internal testbench
behavior.

However, this approach requires us to instantiate the DUT and testbench blocks (other BFMs, clock
generators etc) in the module, so that when we want to create the remaining 199 tests, we have to copy
too much code. And, of course, the DUT port list might change, so the next step is to separate these
potential changes from the test code.

130 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Separate Test from Test Harness


• Hide BFM structure behind a module test_harness;
set of proxy tasks ...
serBFM bfm ( .serial );
Design dut ( .serial );

function void set_bit_time


module testbench; (time t);
bfm.set_bit_time(t);
test_harness th(); endtask
task send (input [7:0] v);
initial
bfm.send(v);
begin: test1
endtask
th.set_bit_time(52083);
...
#1000 TestHarness
th.send("H"); bfm dut
th.send("i");
end
proxy tasks
serBFM
serial
Design 9
endmodule

Bus-Functional Modeling
Continuing the theme of separation of concerns, we can create a test harness to contain an instance of
the DUT and our BFM, along with other testbench blocks. This now means that the testbench consists of
an instance of the test harness along with the test code itself in an initial block, as before. Now the other
199 tests instantiate the same harness and any changes to the testbench or DUT interface is localised
there and doesn’t have to be chased through any number of tests.

The test code in the testbench module now calls the test harness methods which themselves call BFM
methods – the test harness methods are referred to as proxy methods. Note that this test will work happily
with any test harness that exposes the same methods (set_bit_time, send). Similarly the test harness is
written in such a way that it is independent of the test that uses it. This independence of test and
testbench is a critically important feature of the test bench design methodology which is being introduced
in this session. In a real verification project, the planning phase requires a great deal of thought and
documentation to achieve this independence.

The test harness tasks in the example control a single BFM, but in a more complex (real) example, the
proxy tasks might each control one or more BFMs whose behaviour is hidden from the test itself. Thus we
have introduced the possibility of a layered architecture in to the test harness where each layer interacts
with modules in the layer above (responses, perhaps) and below (driving and control).

A problem with the way it is set out in the slide is that the complex test harness will contain a number of
tasks. SystemVerilog provides the interface construct which helps solve this.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 131


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Task/Function in Interface
interface serial_if;
logic serial; BFM tasks/functions
in an interface
...
function void set_bit_time (time t);
...
task send (input [7:0] v);
... tasks/functions imported into module
modport mp (import set_bit_time, send);
endinterface

module stimulus (serial_if.mp iport);

initial or serial_if or interface.mp


begin

9 iport.set_bit_time(52083);
#1000
iport.send("H");
Bus-Functional Modeling

iport.send("i");

Interfaces, like modules, can contain methos which can be called from outside the interface using the dot
notation for cross module references: iport.send(…). The modport construct provides a way of specifying
signal direction and of providing access to both signals and methods. The modport specifies directionality
from the point of view of the block it is connected to, hence the name which is an abbreviation of module
port. So in the slide, the modport, mp, imports the methods set_bit_time and send and makes them
available to blocks that it connects to. Where an interface is being referenced through a modport, any
task or function that is to be called from a module needs to be listed in the modport declaration.

The interface, unlike modules, can be passed as part of a module’s port list in a very flexible way:

• The type of interface can be passed, serial_if – module code the accesses the required modport
methods or signal by cross module reference, specifying the modport and signal

• The modport can be specified, serial_if.mp, as in the example, in which case a call such as
iport.send(…) is controlled by the modport’s import list.

• A generic interface (with or without a modport) can be used for greater flexibility (other interface types
can be connected).

Note that when importing (or exporting) a method through a modport, it is usual just to provide just the
method name. It is possible to specify the complete prototype in the import/export statement, but at the
time of writing, this feature is not supported by all SystemVerilog simulator.

132 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

APB Interface With Tasks


interface APB; APB read and write
logic PCLK, PSEL, PENABLE, PWRITE;
t_APB_a PADDR;
functionality is encapsulated in
t_APB_d PWDATA, PRDATA; tasks in the interface

task Write (input t_APB_a adr, t_APB_d data);


...
endtask

task Read (input t_APB_a adr, output t_APB_d data);


...
endtask

modport Master ( output PCLK, PSEL, PENABLE,


PWRITE, PADDR, PWDATA, input PRDATA);

modport Slave ( input PCLK, PSEL, PENABLE,


PWRITE, PADDR, PWDATA, output PRDATA);

modport TB_Master (output PCLK, import Read, Write );


9
endinterface

Bus-Functional Modeling
SystemVerilog interfaces may contain tasks, functions, initial and always procedures, continuous
assignments, assertions – in fact, anything that a module may contain except module instances.

Interfaces can be used to model bus functionality, they provide reusable bus-functional and transaction-
level access to the bus and they can also be a convenient place to do the bus protocol checking through
assertions.

The APB example interface here has methods for performing bus reads and writes. Notice they are
named without reference to the protocol, so that by moving these tasks in to the interface, the testbench
can call an interface read or write without concern for the underlying protocol, so long as the arguments
are correct, with the result that the interface code and the testbench code are ‘de-coupled’ from each
other and therefore each is more re-usable (compare now the situation we had in the earlier slides).

In the example, which shows an interface modelling an APB bus, a modport called TB_Master has been
added. This modport will be connected to the tester module instance that forms part of the testbench. The
TB_Master modport does not contain any interface signals (except PCLK), because the interface signals
themselves will be controlled by the tasks.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 133


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

An APB Test

module apb_test (interface.TB_Master bus);

initial Generic interface, specific modport


begin
for (int i = 0; i < N; i++)
bus.Write( .adr(i), .data(i) );

for (int i = 0; i < N; i++)


begin
apb_types::t_APB_d value;
bus.Read( .adr(i), .data(value) );
assert( value == i );
9 end
end

endmodule
Bus-Functional Modeling

End

A generic interface with specified modport is passed in the portlist to the test module. As explained earlier,
this allows a modport named TB_Master from any type of interface to be connected and so long as the
modport imports a read and write task with matching prototype, this test code could be reused with
different interfaces.

In this session we have begun to develop some very important themes to creating test environments:

• The Bus Functional Model models the protocol and timing on the bus.

• We have seen how to structure test environment so that the test code can be separated from the test
harness containing a DUT instance, BFMs and other testbench blocks.

• We have seen how separating concerns can lead to clearer and more reusable code by placing tasks
and methods in the BFM

• We have seen how we be able to create a layered structure in the test environment where control is
passed down to the BFMs and responses might be passed back up.

Interfaces (with modports) can be used to model the connectivity between modules in a flexible way

134 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Randomization

Aim

• To understand how to use SystemVerilog’s


randomization features for module-based
verification

Topics covered

• Testbench Automation
• Random numbers in SystemVerilog
• Randomize with inline constraints
• Random stability

10

Randomization
Random generation of stimuli has the potential of creating unexpected sequences of stimuli and thereby
uncover bugs more effectively than directed testing alone. Directed testing anticipates where a bug might
lie and therefore is less likely to hit unexpected cases or test enough scenarios. On the other hand
applying purely random stimulus is inefficient because most of the generated sequences would be
unrealistic or impossible in a real life application.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 135


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Testbench Automation

• Randomization:

• Constrain the randomization to focus on realistic stimuli.

• Generate unanticipated scenarios to uncover unexpected bugs

• Automated checking (assertions, scoreboards, and reference model)

• Functional Coverage

• Record which features have been covered to guide further testing

• Estimate how much more testing is needed

Productivity is provably testing the required design features

10 quickly and repeatably.


Randomization

Being able to constrain the randomization of stimuli in a repeatable way (for retesting) so as to apply life
like yet unexpected combinations, allows the test team to verify the required design features more
efficiently.

With directed testing one can be sure what a particular input stimulus is testing for and there fore check
the expected DUT response. However, with random stimulus generation, the actual DUT input needs to
be sampled independent of the generation scheme and the expected DUT behaviour must be inferred
and checked using a reference model. By separating the concerns of generating stimuli from those of
sampling and checking, the test bench components can be reused in different configurations.

136 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Random Numbers in SystemVerilog


initial 303379748
-1064739199
repeat(4)
-2071669239
$display( $random ); -1309649309
Pseudorandom algorithm
Verilog defined by language
standard

initial ???????????
??????????
repeat(4)
?????????
$display( $urandom ); ???????????
Each tool uses a different
RNG algorithm
initial ?
?
repeat(4) ?
$display( $urandom_range(0,7) ); ?

Random values in the range 0 to 7


10

Randomization
In SystemVerilog it is still possible to use the IEEE Verilog $random function and the LRM specifies the
algorithm for how it generates pseudo-random numbers.

SystemVerilog adds new system functions and syntax to extend random generation and the standard
allows each tool vendor to implement their own random number generator (RNG) and constraint solver.

Each process and object has its own RNG, each of which is initialised with a seed from the parent thread
at the start of simulation. This means that the pseudo-random sequences generated in threads are
isolated from each other and each RNG will cycle through the same sequence of values given the same
initial seed – a property referred to as random stability.

$urandom and $urandom_range uses the thread’s RNG to generate a sequence of values.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 137


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

std::randomize

byte unsigned a, b;
logic [7:0] c;

initial
repeat(4)
begin
bit ok; Randomize "scope variables"
std::randomize(a, b, c);
$display(a, b, c);

ok = std::randomize(a, b, c) with { a < b; b < c; };


end
In-line constraints
Constraint solver might fail
10
Randomization

The std package defines a more powerful randomization function, randomize(), to generate random
values for its arguments, which must be scope variables, in other words, variables from the current scope
as opposed to variables called with a cross module reference.

std::randomize() can be called with a constraint block by using the with {…} syntax. Constraints are
placed inside the braces, each constraint is completed with a semi-colon, including the last constraint.
The example in the slide randomizes the variables a, b and c with the constraints a must be less than b
and b must be less than c.

The function returns a bit,1 if the randomization was successful and 0 otherwise. It is a good idea to test
the result of the randomization call in case any applied constraints caused the randomization to fail.

Notice that you do not need to prefix randomize with std::, although you may if you wish to clarify the
intent or to explicitly use the randomize() function from the std package.

138 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Seeding and Random Stability


• Every process and object has its own RNG, seeded hierarchically from its parent

initial
begin: P1 RNG P1 seeded from cmd line
if ( !std::randomize(a, b, c) ) ...
end
initial No effect
begin: P2 RNG P2 seeded from cmd line
repeat(N)
if ( !std::randomize(p, q, r) ) ...
fork
begin: P3 RNG P3 seeded from P2 Effect
repeat(N3)
if ( !std::randomize(p, q, r) ) ...
end
begin: P4 RNG P4 seeded from P2 No effect
repeat(N4)
if ( !std::randomize(x, y, z) ) ...
end
...
10

Randomization
SystemVerilog adds a deterministic random number generator (RNG) to every thread and object. Each
child RNG will be initialised with the next seed provided from the parent thread RNG. The RNG will
generate the same sequence of values given the same initial seed.

In the slide, process P1 uses its own RNG and so will not affect the sequence of random values
generated in process P2 (P1 and P2 have seeds provided by the parent process).

However, P2 is the parent process for P3 and P4 and they are created in a fork…join block after the first
repeat loop has been executed, but since there is no prediction of the order in which they are seeded,
they are initialised with the 2 next values of P2’s RNG – one to P3 and the other to P4. So now, the
sequence of values generated in P3 and P4 will depend on N in the previous repeat loop. However,
having both been initialised, the sequence in P4 will not be affected by how many times std::randomize()
is called in P3 – and vice versa.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 139


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Saving & Restoring Seeds


initial
begin
string randstate;
Built-in class
std::process p;
p = process::self();
p.srandom(9); Seed RNG for this process

randstate = p.get_randstate();

repeat(4) $display( $urandom );

p.set_randstate(randstate); String is tool-specific

repeat(4) $display( $urandom );


10 end
Randomization

srandom seeds the current process (or object) from an integer – by using the std package process type
one can get a reference to the current process from within a module.

get/set_randstate allow you to save and subsequently restore the random state of the process, but
because the string is tool-specific, these two calls only make sense when used as a pair.

In the example above, you get the identical sequence of 4 random numbers repeated twice.

Note that the string, randstate, returned from get_randstate() is implementation specific and the purpose
is to store the randomization state in a way that can be restored, as shown.

140 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Random Sequence of Valid Actions


• Do not apply totally random inputs to DUT!

Unrealistic stimulus,
repeat (100) not very useful
{ Adrs, Data, nWr, nRd } = $urandom;

enum {cyc_Rd, cyc_Wr} cycle_kind;


repeat (100) begin
if ( !std::randomize(TestAdrs, cycle_kind) ) ...
if (cycle_kind == cyc_Rd) begin
ReadCycle(TestAdrs, ResultData); Random sequence of
well-defined actions
end else begin
if ( !std::randomize(TestData) ) ...
WriteCycle(TestAdrs, TestData);
end
end
10

Randomization
Rather than generating completely random values, it is preferable to randomize a variable whose purpose
is to control how other variables are generated. These variables are referred to as control knobs and in
the slide, the cycle_kind control knob is used to select either a read or write cycle.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 141


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Randcase

always @(posedge clk)


begin
Start <= 0; Reset <= 0; Stop <= 0;
randcase
1 : Reset <= 1; Reset for 1% of the time
Weights
5 : Start <= 1; Start for 5% of the time
5 : Stop <= 1; Stop for 5% of the time
89 : ;
endcase
end

Weights need not add up to 100


10
Randomization

As an alternative to the technique shown on the previous slide, SystemVerilog provides a weighted case
statement – randcase. The syntax is like that of an ordinary case statement, except that there is no case
expression after the keyword randcase, and the randcase item expressions are weights, which indicate
the relative likelihood of the corresponding randcase item being executed.

In the example, the randcase is executed on every clock. The total value of all the randcase item weights
is 100, so each weight represents the probability, expressed as a percentage, of the corresponding
statement being executed. So Reset is asserted 1% of the time, Start 5% of the time and Stop 5% of the
time. 89% of the time, none of these signals is asserted.

This is only an example: the weights need not total 100.

142 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Randcase

always @(posedge clk)


begin
Start_or_stop = Start || Stop;
Start <= 0; Reset <= 0; Stop <= 0;
randcase
1 : Reset <= 1;
5 * !Start_or_stop : Start <= 1;
Case items can
5 * !Start_or_stop : Stop <= 1;
be expressions
89 * !Start_or_stop : ;
99 * Start_or_stop : ;
endcase
end
10

Randomization
You may remember that in Verilog, case item expressions need not be constant. The same is true of
randcase item expressions.

The example has been rewritten to prevent Start or Stop being asserted for more than one consecutive
clock. This is achieved by using the variable Start_or_stop to adjust the weights in every clock cycle
following one where Start or Stop was asserted.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 143


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Randsequence
• Generates a random sequence of actions based on a grammar

initial 1st production

randsequence (start)
Productions

Nonterminal start: part1 part2 part3; Code block

Terminal part1: { $display("part1"); };


part2: { $display("part2"); };
part3: repeat (100) repeated; Random choice
repeated: either | or_this | abort;
either: { $display("either"); };
part1 or_this: { $display("or_this"); };
part2
or_this abort: { $display("abort"); break; };
or_this endsequence
either Terminate the randsequence
10 abort
Randomization

There is often a requirement to generate a sequence of data values according to some sort of rules of
“grammar”. SystemVerilog includes the randsequence statement to do this.

A randsequence statement looks rather like a case statement. It generates a grammar-driven sequence of
random productions. Randsequence contains one or more production items, each of which consists of a
name followed by a colon and then a list of one or more alternative rules, separated by |. These are
chosen at random

This simple example illustrates how randsequence works. First the production in parentheses after the
keyword randsequence is selected – start in this example. The production, start, consists of 3 further
productions which will be generated one after the other in the order given. The productions, part1 and
part2, are terminal productions as they contain SystemVerilog code which is executed. The non-terminal
production, part3, includes a control – repeat (100) – which causes the production, repeated, to be called
repeatedly.

The production, repeated, selects one of its 3 productions. If either or or_this is selected, the code block is
executed and the next loop of part3 is executed. If abort was selected, the code block is executed, but the
break forces execution to continue from after the endsequence statement.

This example shows how one can generate interesting random sequences of behaviour from just a few
rules.

144 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

More Randsequence Features


initial
Weights for randomization
randsequence (start)
start: body := 8 | empty := 2 | abort := 1;
body: prefix start suffix;

Recursive production
Also case and rand join
Arguments to productions

choice: if (n < 4) action("abc", n-1)


else action("def", n+1);

action(string txt, int k): { $display(txt, k); };


...
endsequence
End
10

Randomization
This slide illustrates further features of randsequence.

Production start selects one of 3 options according to a weighting so option body is selected 8 times out
of 11, empty 2 out of 11 and abort is selected 1 out of 11 times.

Production choice includes a conditional test and calls production action with an argument list

Note that productions can contain loops as shown by body.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 145


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

10
Randomization

146 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Coverage

Aim

• To understand how to record functional


coverage in SystemVerilog

Topics covered

• Testbench Automation
• Covergroups
• Coverpoints
• Cross coverage
• Coverage bins

11

Coverage

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 147


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Test bench Automation

• Randomization:

• Constrain the randomization to focus on realistic stimuli.

• Generate unanticipated scenarios to uncover unexpected bugs

• Automated checking (assertions, scoreboards, and reference model)

• Functional Coverage

• Record which features have been covered to guide further testing

• Estimate how much more testing is needed

Productivity is provably testing the required design features

quickly and repeatably.

11 Collecting functional coverage requires a way of automatically recording what stimuli have been applied to
the design by the test environment. In the Randomization section, we saw how scenarios can be
Coverage

generated in a random, but controlled, fashion. In this section, we look at how we can record what stimuli
have been applied.

It is important to plan early in the verification task to identify the design features to be tested and from
that, workout what scenarios need to be generated. Test bench components monitor the DUT signals –
both the stimulus and response – and so have the information needed to record what has been tested.

With random generation of stimuli, there is no explicit relationship with the resulting DUT response. It is
necessary, therefore, to develop monitoring components to sample signals in the design and from this
infer what stimuli and responses actually occurred rather than were intended to occur. This collection of
functional coverage enables the verification team to identify which tests were performed, and what DUT
behaviour was exercised.

Checking
It is an important principle that the DUT behaviour is monitored independently and concurrent with
stimulus generation. The DUT behaviour is used for both functional coverage collection, so is also used
as an input to a reference model so that the behaviour can also be concurrently checked.

148 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Functional Coverage
• Create covergroups starting from a test plan

module InstructionMonitor (
input bit clk, decode, 14
input logic [2:0] opcode, 9 10
input logic [1:0] mode ); 7 6
5
3
covergroup cg 0
000 001 010 011 100 101 110 111
@(posedge clk iff decode);
coverpoint opcode; Coverage hole
coverpoint mode;
endgroup 18 16
13
7
cg cg_inst = new;
0 1 2 3
...
...
Does not count X or Z
endmodule: InstructionMonitor

Functional coverage is distinct from property coverage which is a part of the SystemVerilog Assertions
11
(SVA) language, although both types of coverage are used and important for evaluating the state of the

Coverage
verification task and most simulators have an integrated coverage database facility that allows you to
assess both forms of coverage together.

At its simplest, we might be interested in counting the number of times a variable takes has each of its
possible values at a point in time. In SystemVerilog, coverage is based on the covergroup construct and
in the example we have two variables, opcode and mode, which define coverpoints. The covergroup is
sampled on each occurrence of the expression @(…), referred to as the sampling event. So in this case,
on each rising clock edge of clk when decode is true, mode and opcode are sampled and the count for
the sampled value is incremented. These counted values are stored in bins; by default there is one bin for
each distinct value. The coverage information can be obtained by calling a coverage method or system
function, or by reading the report that the simulator creates. Any value that is never seen during a
simulation, will have a bin count of zero and is referred to as a coverage hole.

SystemVerilog's coverage system measures only 2-state values, not X or Z values. It is important to
avoid presenting any X/Z values to the coverage mechanism. Consider using assertions with the
$isunknown system function to confirm that this never occurs in practice.

When defined in a module, the covergroup needs to be instantiated like any other variable, but needs to
be created using new.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 149


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Covergroup Syntax
• Covergroup can be in a module, interface, program or class
Classes are not
module InstructionMonitor ( discussed here
input bit clk, decode,
input logic [2:0] opcode,
covergroup name
input logic [1:0] mode );

covergroup cg Sampling event


@(posedge clk iff decode);
coverpoint opcode; Sample only when enabled
coverpoint mode; Variables or expressions
endgroup

cg cg_inst = new; Create a covergroup instance

... Don't
... covergroup typically in a BFM or checker
forget!
endmodule: InstructionMonitor

11 Coverage collection incurs a performance and memory cost, so it is important to sample only when
necessary rather than on every occurrence of some frequent event, like a clock edge. One way of
Coverage

selecting the clock edge is to provide a boolean qualifying expression after the (optional) iff keyword. The
purpose of the expression is to provide a short clear control of the sampling event – if there is a complex
expression to be evaluated, this is best done outside the covergroup block and using the result with in the
iff expression.

It is easy to forget to instantiate the covergroup – most cases where no coverage is collected is due to the
covergroup not being instantiated (it’s worth checking any iff qualifying expression too)

150 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Coverage Bins
logic [2:0] opcode;
shortint unsigned data;

covergroup cg2 @(posedge clk);


option.per_instance = 1; Don't merge coverage data across instances
option.at_least = 5;
Need at least 5 hits on each and every bin
coverpoint opcode {
bins null_op = { 3'b000 };
bins jump_op = { [3'b001:3'b010] };
bins alu_op = { [3'b011:3'b100], [3'b110:3'b111] };
}
coverpoint data iff (opcode > 3'b010) { Conditional sampling
bins array[4] = { [0:$] };
}
An array of 4 bins
endgroup

By default, a separate bin is created for each possible value of a variable, but it may be that not all values
11
are interesting, possible or, in fact, legal. SystemVerilog provides a bins syntax to control how many bins

Coverage
are to be used and what set or range of values will increment the count for a bin.

Sets
A set of possible values, written using a set-like syntax in curly brackets (braces), can be allocated to a
single named bin. Alternatively, the bin's name is followed by square brackets; this creates an individual
bin for each value in the set. Finally the bin's name can be followed by square brackets containing a
number; this represents a number of bins, over which the set of values is to be distributed. The
distribution of values into bins aims to be as uniform as possible, but the distribution is sometimes a little
surprising unless you take care to make the number of bins an exact divisor of the number of values in the
set.

Value-ranges
Ranges in the set syntax can be specified using [low:high] syntax. Either the low or high limit can be $ to
denote the extreme limit of the value's possible range.

Instance Coverage

By default, coverage is collected across all instances of a covergroup (so called, type coverage). This
might not be what is wanted, so there are a number of options available, one of which, per_instance,
controls whether each separate instance is covered separately or collectively. When per_instance = 1,
each instance coverage is collected separately in addition to the type coverage. By default it is 0.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 151


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Cross Coverage
10
Coverpoint labels
11 2 2
0 0 0 1 1

covergroup cg ...
cp1: coverpoint opcode;
cp2: coverpoint mode; 10 3
0 0 1 0 1 1 1
cross cp1, cp2;

mode
endgroup

01 3 3 2 3 2
0 0 0

7
• A bin for each combination 00 3
1 1
4
2
0 0
• A lot of data - use with care
000 001 010 011 100 101 110 111
opcode
End

11 Cross coverage creates coverage for combinations of coverage points. In this way we can count the
number of times opcode was 0 when mode was 0 and so on – here there are 32 bins created.
Coverage

Although we had just the one hole for opcode when considered on its own (100 is never hit) and mode
does have 100% coverage, the combination reveals 11 holes or about 66% coverage. When defining
cross coverage, it is even more important to understand which bins are illegal and uninteresting and
which ranges of values can be assigned to more interesting bins.

It is very easy (through a lack of thorough planning) to end up with a vast number of cross coverage
points, resulting in pessimistic coverage numbers.

152 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Other Language Features

Aim

• To learn some other useful but less


important SystemVerilog language features,
mostly related to arrays

Topics covered

• $root and $unit


• Enumeration methods
• Multidimensional arrays
• Assignment patterns
• Array querying functions
• Bit-stream casting

12

Other Language Features

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 153


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

$root and $unit

int n = 1; File scope (not recommended)

module foo;
int n = 2; Module scope

initial
begin: foo Block name foo hides module name foo
static int n = 3; Block scope
assert( n == 3 );
assert( foo.n == 3 );
assert( $root.foo.n == 2 );
$root.top-level-module
assert( $root.foo.foo.n == 3 );
assert( $unit::n == 1 ); $unit::name-at-file-scope

$root

12 In all versions of SystemVerilog, global objects may be referenced using a hierarchical name that begins
with $root. You may also be able to reference global objects with their simple name, provided the name is
not otherwise visible using SystemVerilog’s name search rules.
Other Language Features

$unit

In SystemVerilog, you can place declarations outside of any modules. For example you can declare tasks,
functions and variables outside modules.

Typically, a design in SystemVerilog will be written across several files. Any declarations outside the
modules are called compilation unit declarations. The SystemVerilog standard states that source files can
be grouped into compilation units. The way this is done is tool-dependent. According to the SystemVerilog
LRM, a compilation unit could be one of the following:

• A single file

• All the files in the design

• The files that are compiled with a single compile command

The declarations in a compilation unit in effect form a package that has the name $unit, and that is
implicitly wildcard-imported into the compilation unit. The declarations cannot be made visible outside the
compilation unit in which they appear.

154 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Enumeration Methods
0 1 2 3
enum {red, green, blue, pink} e = green;

$display( e ); 1

$display( e.name ); “green”

$display( e.first ); 0
assert( e.last == pink );
Tool-dependent
$display( e.first.name ); assert( e.next == blue );

$display( "%s", e.first ); assert( e.next(2) == pink );

assert( e.next(3) == red );

assert( e.prev == red );

assert( e.num == 4 );

Enumerations provide a way of labelling a set of related constants (referred to as enums) and by default
these values are of type int. The enumeration is defined using the enum keyword. Other integral types
can be declared as the enumeration base type and the rule is that by default, the first declared value (red, 12
in the example above) has the default value of the base type (so 0 in the example). Each neighbouring
enum in the set has an incrementing value, so the value of green is one higher then the value of red and

Other Language Features


so on. Enumeration methods provide a way of accessing the values and are summarised below:

• first() last() – returns the value of the first or last enum of the set

• next(n) prev(n) – returns the value of the enum n places towards the last (or first, respectively) and wraps, if
necessary. By default, n = 1 and can be left out.

• num() – returns the number of members in the enumeration

• name() – returns a string representation of the enum’s label. Remember that the enum itself is a named
value, so name() returns the label, as a string, that is associated with that value

You can also assign specific values to one or more enums in the declaration by simply using something
like red = 24’hFF0000, … the next neighbour must then either be assigned a larger value, or
(automatically) get the next increment of the base type. Although enums can be viewed as labelled
constants, it is a compile error to assign an integral value directly to an enum, however it is allowed to
compare an enum as shown in the slide above. At the time of writing, it’s not possible to chain
enumeration methods in all tools, so e.first.name() in the example above, does not guarantee to work.
Many of the datatypes introduced in this session have methods to access or manipulate values, they can
be called using a ‘.’ notation between the variable and the method – very similar to how methods are
called in SystemVerilog Classes and is a very important feature of object-oriented languages.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 155


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Arrays for Multidimensional Structures


• Use typedef for clarity

typedef logic [7:0] octet_t; Packed array

typedef octet_t [3:0] quadOctet_t; Packed array of 32 bits

typedef quadOctet_t array_t [0:3]; Add an unpacked dimension

Equivalent declarations

typedef logic [3:0] [7:0] array_t [0:3];

array_t v = '{ 32'h00, 32'h0f, 32'hf0, 32'hff };


Assignment pattern / array literal

Typedefs can be very convenient when building up complicated array definitions, as shown on the slide.

12 They can also save you the need to write the same subscript definition more than once.

As we have seen previously, you can write the value of an unpacked array or struct using the assignment
pattern syntax, which has the tic mark ' before the opening curly brace. An assignment pattern can be
Other Language Features

used to write an array literal where all of the elements inside the assignment pattern are themselves
literals.

156 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Initializing an Unpacked Array


• How to set all the elements of an unpacked array to the same value?

int array[8] = '{1, 1, 1, 1, 1, 1, 1, 1};


Array literal

int array[8] = {1, 1, 1, 1, 1, 1, 1, 1};


Concatenation

int array[8] = '{ 8{1} };


Replication in assignment pattern

int array[8] = '{ default:1 };


Default in assignment pattern

int array[8] = '{ int:1 };


Key is a type

int array[8] = '{0:1, 7:1, default:0};


Tool-dependent
Key is an array index

It is possible, but inefficient, to initialize an array with a large number elements by writing an array literal
with a large number of values, alternatively, one can use replication, though the syntax can be awkward.
12
One can easily sepecify a default value for the unspecified elements in an array literal by using the
reserved word, default, followed by a colon and the required value. This is particularly useful where the

Other Language Features


number of elements in the array is unknown.

Where an array literal begins with a list of values, these are assigned in order to the corresponding array
elements. A default can then be used for the remaining values. For example:

int array[0:1023] = '{1,2,3,4,default:0};

Alternatively, the index:value syntax format may be used to specify the value for arrayname[index], in
which case the ordering doesn’t matter. At the time of writing not all simulators supported the use of an
array index as a key, however.

int array[0:1023] = '{default:0,1:1,3:3};

Finally, the type of the element may be used, though this syntax is more appropriate in structs, where the
members may have different types.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 157


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Replication in an Assignment Pattern

Two unpacked dimensions

int twod [3][4] = '{ '{1, 2, 3, 4}, '{1, 2, 3, 4},


'{1, 2, 3, 4}};
Array literal

int k = 1;
initial
twod = '{ 3{ '{k, k+1, k+2, k+3} } }; Assignment pattern

Extra { } for replication

Note that an array literal is an assignment pattern for an array (viz: structure literals which are

12 assignment patterns applied to structs). While an array literal can also contain a replication, it differs
from an an assignment pattern in that all its elements are constant expressions. An assignment pattern
is a more general concept as it can be applied to both arrays and structs and can contain both variable
expressions and default values.
Other Language Features

They both reflect the structure of the array being assigned to, so where the (unpacked) array has more
than one dimension, this is reflected by a nested assignment pattern for eachdimension – as shown
explicitly in the first example. The two dimensional unpacked array twod is assigned an array literal
consisting of 3 nested array literals, each with 4 elements. If you expand the replication in the
assignment pattern of the second example, you will see that it has the same structure. In fact, it is an
error if the assignment pattern or array literal has a different structure to the target of an assignment,
whether or not they both have the same total number of elements.

158 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Packed Arrays and Structures


• For packed arrays and structs, { } means concatenation

logic [7:0] vec;


vec = {1'b1, vec[3:0], 3'b000};
Can concatenate a mix of scalars and arrays

struct packed {
logic a;
A packed struct behaves like a vector
shortint b;
} s;

s = {1'b1, 16'h0}; s.a = 1'b1; s.b = 16'b0;

s = {1'b1, 1'b1}; s.a = 1'b0; s.b = 16'b11;

s = '{a:1'b1, b:1'b1}; s.a = 1'b1; s.b = 16'b1;

A packed array or struct in SystemVerilog occupies contiguous bits in memory i.e. the data is packed
together, thus they behave and can be operated on like vectors in Verilog. Therefore a packed array or
struct can be assigned to by concatenation, using braces without the tick – as shown, as well as being 12
assigned using an assignment pattern. Unlike assignment patterns, concatenations can contain a mix of
scalars and arrays of different lengths.

Other Language Features


Remember that because a packed struct can behave like a vector and that a concatenation results in a
vector it follows that the concatenation {1'b1, 1'b1} will result in the value 2'b11, which when assigned to s
will set the right-most two bits of s.b to the value 2'b11, with all the higher-order bits filled with zeros.

A further point to note is that the struct in the example is 17 bits wide with b occupying the bottom 16 bits
and a occupying bit 16. Thus frequent access of s.a may have a small performance impact compared to
an unpacked struct where the compiler might have the freedom to place each member of s on a
convenient alignment boundary – something similar might, of course, be said of packed and unpacked
arrays.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 159


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Array Querying Functions (1)

3 1 2 Dimension number
logic [7:0] A [1:2][0:15];

assert( $dimensions(A) == 3 );
assert( $high(A) == 2 );
assert( $high(A,1) == 2 );
assert( $high(A,2) == 15 );
assert( $high(A,3) == 7 );
assert( $size(A,3) == 8 );

The array querying functions are system functions (prefixed with a $) that return information about the

12 array and are summarised in the next slide.

We’ve seen examples of arrays with multiple packed and unpacked dimensions, so how are they
numbered? The packed dimensions are easy enough to understand – if you have:
Other Language Features

logic [3:0][7:0] qbyte;

You have a 32 bit vector representing, say, 4 bytes. It is intuitive to think that you would scan the bits of
the lowest byte from bit 0 to 7 before going on to the next byte and so on. This is what is meant by (in this
case) the byte dimension ([7:0]) being the fastest dimension and the [3:0] dimension being the slowest.
SystemVerilog assigns the value 1 to the slowest dimension ([3:0] in this case) and 2 to the [7:0]
dimension. A useful mnemonic is to think of the way time can be expressed as hours:minutes:seconds
with the hours dragging by (leftmost and slowest dimension) while the seconds flash past (righmost,
fastest dimension)

So now we want an unpacked array declared as follows:

logic [3:0][7:0] qbyte_table [0:7][0:15];

We might picture this as a table of 8 rows by 16 columns, each element of the table is a packed array as
before. Now we might want to scan across the columns of one row (like a TV screen) before going on to
the next. Intuitively, we then want to process the packed dimensions as before. So now we have the
unpacked dimensions being slower than the packed dimensions with the leftmost unpacked dimension
being the slowest (1) and the rightmost packed dimension being the fastest (4).

160 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Array Querying Functions (2)

$left The left index of the dimension


$right The right index of the dimension
$low The lowest index of the dimension

$high The highest index of the dimension


$increment +1 if the range descends, -1 if it ascends Beware!

$size The number of elements in the dimension


$dimensions The number of dimensions in the array

In these system functions, dimension defaults to 1, so does not have to be supplied and for fixed sized
arrays (such as the ones here where the size is known at compile time), they behave as constant
functions and can be used in parameters or, indeed, in an array declaration: 12
logic [$size(big_mem_arr, 1)-1:0] other_mem_arr [1:2][0:15];

Other Language Features


• $left/$right(arrayname, dimension) – returns the MSB or LSB respectively of the given array and
dimension.

• $low/$high(arrayname, dimension) – returns the least and highest index, respectively, of the given array
and dimension

• $increment(arrayname, dimension) – returns 1 if $left >= $right and -1 otherwise. This system function is
hardly used at all, if ever.

• $size(arrayname, dimension) – returns the number of elements of the given array and dimension

• $dimensions(arrayname) – returns the number of dimensions of the given array, so will return 1 if handed a
string and 0 if it is, in fact, handed something that is not an array at all i.e. an integer (so called: a scalar
object)

• $unpacked_dimensions(arrayname) – returns the number of unpacked dimensions of the given array

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 161


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

$bits
• How many bits in a variable or type?

logic [7:0] vec, arr[10];

assert( $bits(vec) == 8 );
assert( $bits(arr) == 80 );

typedef struct {
int i;
bit [7:0] j;
} unpacked_t;

assert( $bits(unpacked_t) == 40 );

The system function $bits may be used to determine how many data bits a type or expression has. A 4-

12 state value (0,1,X,Z) as well as a 2-state value counts as one bit.

$bits may also be used for dynamically sized expressions, such as those involving dynamic arrays.
Other Language Features

When using $bits on an expression, it must not call a function that returns a dynamically sized data type
(such as an array querying function – later in this session), nor a dynamically sized data type name.

162 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Bit-stream Casting
$bits used to size a vector
typedef bit [$bits(unpacked_t)-1:0] packed_t;

unpacked_t u_struct = '{ i:1, j:1 };


packed_t vec;

assert( $bits(unpacked_t) == 40 );
assert( $bits(packed_t) == 40 );

//vec = u_struct; Cannot assign to packed from unpacked

vec = packed_t'(u_struct); Bit-stream cast, useful for


serializing data structures
vec = 40'h0000000101

$bits can be used on fixed sized types (whose size is known at compile time) or expressions to return a
constant expression, so $bits(some_fixed_sized_array) can be used in the declaration of an array:
12
logic [$bits(some_fixed_sized_array)-1:0] another_array;

Other Language Features


We introduce here the concept of bit stream types which for our present purpose means any integral
type, packed or unpacked array or stucture, dynamic or fixed length array. The definition is recursive, so a
struct containing a dynamic array of int is a bit stream type and quite understandabley one cannot assign
a data structure of this type directly to a packed type – even if the total number of bits were the same.
What one can do, though, is to use bit-stream casting to do 2 steps:

1. Convert the unpacked type to a generic packed value with the same number of bits (this is the bit stream – it
has no specific format at this stage)

1. Convert the generic packed value to the packed destination type

Both of these steps are accomplished by the single call using the bit stream cast in the example.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 163


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Array Manipulation Methods

• Built-in methods that work for any unpacked array or queue

int arr[10] = '{1, 8, 2, 2, 5, 6, 1, 9, 1, 3};


int q[$];

This defines x Any expression

q = arr.find(x) with (x > 5);


q = '{8, 6, 9}
q = arr.find() with (item > 5);

Implicitly defined

• Useful for manipulating datasets for verification

SystemVerilog provides a number of array manipulation methods, which are used to search and order

12 arrays, or reduce them to a single value.

All these methods operate on unpacked arrays (including dynamic and associative arrays and queues).
Some of them work with packed arrays too.
Other Language Features

The return type is always a queue – even if that queue is empty or contains just a single value.

As an example of an array manipulation method, consider find. In the example shown here, arr is an
unpacked array with 10 elements of type int. q[$] is a queue of ints and is used to hold the value returned
by arr.find.

The find() method takes one optional argument, which designates an element of the array. It returns a
queue – in this case a queue of ints – containing the elements of arr that match the expression in
parenthesis after the word with. This ’with (Expression)’ is mandatory here.

If you omit the argument to find, a default argument called item is created. This is illustrated in the last
line.

164 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Array Locator Methods


int arr[10] = '{1, 8, 2, 2, 5, 6, 1, 9, 1, 3};
int q[$]; 0 1 2 3 4 5 6 7 8 9 Index

q = arr.find(x) with (x > 5); q = '{8, 6, 9}

q = arr.find_index(x) with (x > 5); q = '{1, 5, 7}

q = arr.find_first(x) with (x > 5); q = '{8}


q = arr.find_first_index(x) with (x > 5); q = '{1}
q = arr.find_last(x) with (x > 5); q = '{9}
q = arr.find_last_index(x) with (x > 5); q = '{7}
q = arr.min(); q = '{1}
q = arr.max(); q = '{9}
q = arr.unique(); q = '{1, 8, 2, 5, 6, 9, 3}
q = arr.unique_index(); q = '{0, 1, 2, 4, 5, 7, 9}

Here are the manipulation methods that are used for searching the elements of an array.

with is mandatory 12
• find – elements satisfying the withexpression

Other Language Features


• find_index – index of elements satisfying with

• find_first – first element satisfying the withexpression

• find_first_index – index of first element ...

• find_last – last element satisfying the withexpression

• find_last_index – index of last element ...

with can be omitted

• min – element with the minimum value

• max – element with the maximum value

• unique – elements with unique values

• unique_index – indices of elements with unique value

These last 4 methods can be called accompanied by with, in which case elements are found which satisy
the expression or a null queue otherwise. For example, in the slide, q = min(x) with (x > 5); returns q = ‘{6}

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 165


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Array Ordering Methods

int arr[10] = '{1, 8, 2, 2, 5, 6, 1, 9, 1, 3};

arr.reverse(); arr = '{3, 1, 9, 1, 6, 5, 2, 2, 8, 1}

arr.sort(); arr = '{1, 1, 1, 2, 2, 3, 5, 6, 8, 9}

arr.rsort(); arr = '{9, 8, 6, 5, 3, 2, 2, 1, 1, 1}

arr.shuffle(); Random order

arr.sort() with ( -item ); arr = '{9, 8, 6, 5, 3, 2, 2, 1, 1, 1}

Note: these methods work in-place: they modify the array itself

These methods are used to order an array. Note that reverse and shuffle must not have a with clause,

12 but sort and rsort may do. In these cases, one might want to sort a struct, say, on a particular field:

struct {
byte red;
Other Language Features

byte green;
byte blue;
} colour ;
colour.sort with ( item.red ); // sort using the red field only
colour.sort( x ) with( {x.blue, x.green} ); // sort by blue then green

166 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Array Reduction Methods


Dynamic array

logic [7:0] vecs[] = '{ 1, 2, 6, 6 };

assert( vecs.sum == 15 );
assert( vecs.product == 72 );
assert( vecs.and == 0 );
assert( vecs.or == 'b111 );
assert( vecs.xor == 'b011 );

assert( vecs.sum with (item – 1) == 11 );


assert( vecs.sum with (item * 2) == 30 );

End

These array reduction methods reduce an array to a single value. This value is stored in a queue – which
will end up having one element.

• sum – Sums the array elements


12
• product – Product of the array elements

Other Language Features


• and – Bitwise and

• or – Bitwise or

• xor – Bitwise xor

In each case, if a with clause is specified, the method acts only on the elements that satisfy it.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 167


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

12
Other Language Features

168 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

The Direct Programming Interface

Aim

• Learn how to interface between SystemVerilog


and C using the DPI

Topics covered

• DPI flow and simulator switches


• Importing and exporting tasks and functions
• Passing data between C and SystemVerilog
• Open arrays
• Pure and context tasks and functions

13

Interface
The Direct Programming

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 169


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Why use C with SystemVerilog?


• Existing C functions
• Reference models
• Calculate expected response
• Generate stimulus

• C library functions
• Mathematical calculations
• Advanced and efficient file I/O

• Custom random number generator

• ...

There are many reasons why it is sometimes useful or necessary to call C functions from a SystemVerilog
simulation.

You might have existing functions, written in C, that implement algorithms that you are now implementing
13 in hardware. The C function could act as a reference model in a SystemVerilog test environment, or could
be used to calculate the expected hardware response. Also, functions may be available to generate
appropriate stimulus.
Interface
The Direct Programming

Another reason to use C is for all the standard library functions that it provides. For example the C library
includes many common mathematical functions, and provides for efficient file handling.

In a constrained random verification environment, it might sometimes be appropriate to implement custom


random number generators, for example to follow standard statistical distributions.

These are just a few examples; there are many more reasons why calling C functions from SystemVerilog
may be useful and the DPI makes this relatively easy to do.

170 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Direct Programming Interface (DPI)

• Direct function-call interface between SystemVerilog and C


• Both directions!
or C++ with extern "C" linkage

• Much simpler than PLI/VPI:


• No callbacks Can call VPI from DPI

• No function registration
• No need to traverse object hierarchy to find things
• No synchronisation

• Well specified mapping between SV data types and C data types

• Compiled DPI C code is binary compatible across different simulators on


the same platform – vendors can distribute compiled code

The VPI is comprehensive, but complex. It is primarily intended for experienced C programmers to
provide customised SystemVerilog tools. In addition, creating even a simple VPI application is not trivial,
because of the complexity of the interface.

For “ordinary users” of SystemVerilog, there is sometimes a requirement to call existing C functions
directly from SystemVerilog code, or to extend SystemVerilog’s capabilities by writing simple functions in
13
C. The aim of the DPI is to provide a simple mechanism for doing this, thus avoiding the steep learning

Interface
The Direct Programming
curve needed to use the VPI.

The DPI provides an easy-to-understand interface between C and SystemVerilog. It has been designed
so that compiled DPI C code (object code or libraries) is binary-compatible: once compiled, it can be used
with different vendors’ simulators running on the same platform.

We have been talking about C, because this is likely to be the language used. The SystemVerilog LRM
specifies the C layer of the DPI. It is also possible to use C++, provided the top-level functions use C
linkage. Theoretically, other languages could be used too, but no alternatives to C are specified in the
LRM. The language used does not affect the SystemVerilog code in any way.

Finally, the DPI also provides an alternative means of using the VPI functions, because it is possible to
call them from DPI-imported C functions.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 171


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

DPI Simulation Flow


• Read your tool documentation for details and examples

You write (or procure!) these

System-
Verilog Compiler Simulator

-sv_root
SV compiler may -sv_lib Linked
generate C header .h
-sv_liblist

C Compiler/ Shared Library


Linker
DLL
.so
svdpi.h Provided by the simulator

Binary-compatible between tools

Here is the flow for using the DPI. This shows the flow in fairly general terms; the exact details for a
specific tool will be provided as part of a tool’s documentation. Vendors usually supply application notes
and examples, which provide a good starting point for your own applications.

13 To use the DPI, you need functions written in C, whose arguments and return types are compatible with
SystemVerilog. You will also need to declare the C functions in your SystemVerilog code. We shall be
discussing what all this means shortly.
Interface
The Direct Programming

Some SystemVerilog compilers will automatically generate C header files from the SystemVerilog
declarations. Tools may also be able to generate appropriate SystemVerilog declarations from existing C
code.

The SystemVerilog LRM includes the source code for the header file svdpi.h, which most DPI C functions
will need to include.

The C code must be compiled and linked using a C compiler, such as gcc or the Microsoft C Compiler, to
create a shared object file (UNIX and Linux) or DLL (Windows). You will need to find out which tools your
simulator supports, and which switches are required. Details for the simulator you are using will be
provided in the tool’s documentation.

Finally, when your run the simulation, you will need to tell the simulator where to find the shared libraries
that contain your DPI C functions. The SystemVerilog LRM recommends three simulator command-line
switches, which are described on the next page.

Tools may have options to simply the process. For example, you may be able to list the C source files
with the SystemVerilog files in a single command.

172 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Command-line Switches
• The following simulator switches are recommended in the LRM, not mandated

Default is current
working directory
-sv_root path Location of lib or liblist files
-sv_liblist bootstrap_file File contains object/libraries
-sv_lib object_file Object/library – no extension

-sv_liblist dpilibs.lst -sv_lib myclibs/playlib


-sv_lib projectlibs/lib1
dpilibs.lst
Equivalent
#!SV_LIBRARIES
myclibs/playlib
projectlibs/lib1 No .so or .dll extension

These are the command-line switches, which the SystemVerilog LRM recommends simulators use:

-sv_root directory – the root of the directories mentioned in the other switches. By default, this is the
simulator’s working directory.

-sv_liblist bootstrap_file – the bootstrap_file is a text file containing a list of shared object or library files
13
without the file extension (i.e. without the .so or .dll extension at the end). The first line of the file must be

Interface
The Direct Programming
#!SV_LIBRARIES.

-sv_lib library – where library is the name of shared object or library file without the file extension.

You can have several -sv_lib and -sv_liblist options in the same command line.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 173


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Importing a C Function
module foo;
import "DPI-C" function int count_1s (int v);
int i, ones;
always @(i)
ones = count_1s(i);
...

#include "svdpi.h" Standard header


int count_1s (int v) {
int n = 0;
unsigned u = v;
while (u) { n += u & 1; u >>= 1; }
return n;
} Simply put .sv and .c files on simulator command line

The most common requirement for the DPI is to import C functions, so that they may be called from
SystemVerilog code.

import "DPI-C"
13 To do this, all you need to do is to declare a DPI import function in SystemVerilog. The simplest form of
this is the keyword import followed by the string "DPI-C" (the quotation marks are needed) and then the
function prototype declaration. The function prototype declaration is no different from a standard
Interface
The Direct Programming

SystemVerilog function prototype, although there are one or two restrictions. (A “function prototype” is a
declaration of the function’s name, return type and arguments with no function body and no endfunction
keyword.)

Having declared the DPI import function, you use it just as if the function had been written in
SystemVerilog. From the SystemVerilog viewpoint, an imported function behaves just like a native
SystemVerilog one.

Here is the C code for the C_count_ones function. The SystemVerilog function is called count_ones, so
the C identifier is included in the import declaration. In this example, the SystemVerilog type of the
function’s return value and of the only argument, v, is int. This maps directly to the C type int, so the
corresponding C function returns int and also has one argument, v, of type int.

Types other than int may be used – this will be discussed shortly.

174 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Changing the Imported Function Name


module foo;
import "DPI-C" count = function int count_1s (int v);
int i, ones;
always @(i)
ones = count_1s(i);
...

#include "svdpi.h"
int count (int v) {
int n = 0;
unsigned u = v;
while (u) { n += u & 1; u >>= 1; }
return n;
}

If the C function has a different name from the SystemVerilog function, the name of the C function and an
equals sign is included after "DPI-C". By default, the C function should have the same name as the
SystemVerilog function.

13

Interface
The Direct Programming

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 175


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Mapping Data Types of Arguments


• Small SystemVerilog types have a portable representation in C:

SV C
byte char
shortint short int
How many bits?
int int
longint long long
real double
shortreal float
For storing C pointers chandle void *
string const char *
bit unsigned char
Scalar values
logic/reg unsigned char

• Other types – hard work required on the C side!


• Header svdpi.h contains types and functions

This shows how types are mapped between C and SystemVerilog. Many SystemVerilog types are directly
compatible with corresponding types in C, and it is easy to use them with the DPI. You do need to be
careful, because in C the width of integer and floating types is not defined – int in C may not always be 32
bits – whereas the width of these types is defined in the SystemVerilog LRM.
13 chandle
The SystemVerilog type chandle was included so that DPI tasks and functions could use C pointers. The
Interface
The Direct Programming

values of variables of type chandle are meaningless in SystemVerilog, but could be used to pass a pointer
from one DPI function to another. For example, an imported C function could call the library function
malloc to allocate some memory and return a pointer to the memory. The pointer would be stored in a
SystemVerilog in a variable of type chandle. The pointer could then be passed to another imported C
function.

Scalar (one-bit) values of bit and logic or reg are passed as unsigned char – this is explained on the next
slide. Packed arrays of these types are passed in a different way, which will be explained later.

176 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Exporting a Function to C
module foo;
export "DPI-C" function swap; Just the name, no prototype

// export "DPI-C" C_name = function swap; Changing the name

function byte swap (byte arg); Cannot export class


return { arg[3:0], arg[7:4] ); member functions

endfunction
...

#include "svdpi.h"
char test_swap (char c) {
return swap(c);
}

A DPI import function is one that is written in C and called from SystemVerilog; a DPI export function is
written in SystemVerilog and called from C. A function exported like this would only be called from a C
function that was imported into SystemVerilog, or from a C function that was, directly or indirectly, called
from an imported function.

The syntax for exporting a function is the keyword export, the string "DPI-C" and the name of the exported
13
function. Optionally, the name of the C function may be provided too (C identifier). There is not a function

Interface
The Direct Programming
prototype in a DPI export declaration. In addition the function must be defined in SystemVerilog in the
usual way. The names of the function’s arguments and the types of the arguments and the function’s
return value match as before.

It is not possible to export class member functions.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 177


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Sandwiches and Transparency


import "DPI-C" context function int c_func ( SystemVerilog
string s, logic [7:0] v);
initial
i = c_func("abc", 8'b01xzzx10);

int c_func (const char* s, svLogicVecVal* v) { C


...
Canonical representation
return sv_func(v);
}

export "DPI-C" function sv_func; SystemVerilog


function int sv_func(logic [7:0] v);
...
return 911; Transparent
endfunction

Need a context function to allow the imported function to call an exported function

The SystemVerilog type logic [7:0] maps to the C type svLogicVecVal*, which, although part of the
SystemVerilog language standard, has a simulator-specific representation.
13 When the logic [7:0] value is passed from SystemVerilog to C and back to SystemVerilog, the original
SystemVerilog value is guaranteed to be recovered. Such transparency is a general and deliberate
Interface
The Direct Programming

feature of the DPI: the actual implementation of the DPI call should be transparent from the point-of-view
of the SystemVerilog code.

178 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Importing and Exporting Tasks


import "DPI-C" context task c_task (logic [7:0] v);

() required SystemVerilog
initial
c_task(8'b01xzzx10);

int c_task (svLogicVecVal* v) {


sv_task(v);
return 0; C
}
Important

export "DPI-C" task sv_task;

task sv_task (logic [7:0] v); SystemVerilog


#10;
endtask Task can consume time!

We have been talking about importing and exporting functions. It is also possible to import C functions as
SystemVerilog tasks and to export SystemVerilog tasks.

The C programming language only has functions; there no direct equivalent in C to tasks in
SystemVerilog. Now you might expect that an imported or exported SystemVerilog task would correspond
to a void function in C. This is not so: the C function must return an int. The reason for this will be
13
explained later. For now, just accept that the C function usually returns the int 0.

Interface
The Direct Programming
Imported and exported tasks and functions may have zero, one or more arguments. If there are no
arguments, the parentheses are still required in a DPI import declaration, even though they would not be
required if the task or function were written in SystemVerilog.

When creating a "sandwich" like this where SystemVerilog calls back to SystemVerilog through a DPI
layer, the SystemVerilog task may consume time (or interact with the SystemVerilog scheduler in any
other way it likes).

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 179


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Scalar Bit and Logic Arguments


1'b0 sv_0
1'b1 sv_1
SV C
1'bZ sv_z
1'bX sv_x

import "DPI-C" task print_value(input logic v);

#include "svdpi.h" typedef for unsigned char


int print_value (svLogic v) {
switch (v) {
case sv_0 : printf("%c\n", '0'); break;
case sv_1 : printf("%c\n", '1'); break;
case sv_z : printf("%c\n", 'Z'); break;
case sv_x : printf("%c\n", 'X'); break;
default : printf("???\n");
}
return 0;
}

Scalar values of bit and logic (or reg) are imported and exported as type svLogic, which is defined as
unsigned char in the header file svdpi.h. This header file is defined in the SystemVerilog LRM, and should
be included in most DPI functions.

13 svdpi.h defines the macros (#define – C pre-processor constants) sv_0, sv_1, sv_z and sv_x
corresponding to SystemVerilog logic values.
Interface
The Direct Programming

Remember, this applies to scalar values of bit and logic only.

180 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Packed Arrays
• Packed arrays (logic or bit) are passed using the canonical representation

import "DPI-C" task print_value(input logic [3:0] v);

int print_value (svLogicVecVal* v) { /* ... */ }

0 1 X Z
word 1 avalue 0 0 1 1 0
word 2 bvalue 0 0 0 1 1
31 3 2 1 0

values stored in 32-bit “chunks”

Packed arrays of type bit or logic are passed using a canonical representation, which is identical to that
used to represent four-state values in the VPI (s_vpi_vecval).

Irrespective of the number of packed dimensions in SystemVerilog, the C representation is treated as


having just one dimension and a downwards range – i.e. [N:0] 13
A packed array is represented as an array of one or more elements, with each element representing up to

Interface
The Direct Programming
32 bits. The first element of the array represents the 32 least significant bits. The type of the array is
svBitVecVal for type bit and svLogicVecVal for types logic and reg. This type is composed of two 32-bit
integers, avalue and bvalue. Each SystemVerilog bit is represented by a pair of bits as shown in the slide.

The DPI provides a number of library functions to help you work with the canonical representation; these
functions are declared in svdpi.h. For example, there are functions to perform bit- and part-select
operations on variables of type svBitVecVal and svLogicVecVal. You will find full details in the
SystemVerilog LRM.

The canonical representation of packed arrays was introduced in the P1800 version of SystemVerilog.
Version 3.1a used a different representation. The string "DPI-C" in an import or export declaration means
that this canonical representation is being used.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 181


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Decoding the Canonical Representation


#include "svdpi.h"
int print ( svLogicVecVal* v ) {
int i;
for (i = 0; i < 8; i++) {
#ifdef CADENCE
printf("%d%d", v->a % 2, v->b % 2);
v->a = v->a >> 1;
v->b = v->b >> 1;
#else
printf("%d%d", v->aval % 2, v->bval % 2);
v->aval = v->aval >> 1;
v->bval = v->bval >> 1;
#endif
}
printf("\n");
}

You can access and interpret the bits of the SystemVerilog canonical representation from the C code, but
beware that different simulators use different field names in the structs.

13
Interface
The Direct Programming

182 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

String Arguments
import "DPI-C" function string c_func (
input string is, output string os, inout string ios);

string a = "a_string";
string b = "b_string";
initial
$display( c_func("abc", a, b) );

const char* c_func (const char* is, const char** os,


const char** ios) {
printf("%s\n", is);
printf("%s\n", *ios);
*os = "output_string";
return "return_string";
}

SystemVerilog strings are represented in C using const char *. (i.e. a pointer to const char)

String inputs are passed by value, so the corresponding type for the SystemVerilog function input s_in is
const char * in C. Similarly, the C return type is also const char *.

String outputs and inouts are passed by reference, so the C type for s_out and s_inout is const char **
13
(i.e. a pointer to a pointer to a char. Again it is the pointer itself that is const, not the string being pointed

Interface
The Direct Programming
to.)

The C string corresponding to a function output is undefined when the function is called.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 183


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Open Array Arguments


import "DPI-C"
function void c_func (input logic [15:0] data []);
logic [15:0] data [] = '{ ... };
initial Dynamic array
c_func(data);

#include "svdpi.h"
void c_func (const svOpenArrayHandle h) {
int i;
for (i = svLow(h,1); i <= svHigh(h,1); i++) {
svLogicVecVal* ptr = (svLogicVecVal*)svGetArrElemPtr1(h,i);
int data_in;
data_in = (int)(ptr->aval); // Questa & VCS
data_in = (int)(ptr->a); // IUS
}
}

Imported function arguments may use open arrays. These are arrays that have the ranges of one or more
dimensions specified using the syntax []. This syntax enables the same C function to be called with
different sizes of SystemVerilog array associated with the same argument. Packed and unpacked
dimensions may be open.
13 Open arrays are like multidimensional dynamic arrays, which is why they use the same syntax as
dynamic arrays. Dynamic arrays are in fact passed to DPI functions using arguments that are open
Interface
The Direct Programming

arrays.

Open array type arguments are passed by handle – the corresponding C type is svOpenArrayHandle. The
C header file svdpi.h includes declarations of open array querying functions and functions that you can
use to access the values. Full details can be found in the SystemVerilog LRM.

Note that, unlike other array dimensions, the ranges corresponding to open array dimensions are not
normalised in C. For example, the C code would access the first element of array two (using its handle)
with the index 1, not 0.

184 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Task Return Values


• C functions implementing imported tasks return int – usually 0
• Imported tasks may call exported tasks
• These may take time to execute ... and they may be disabled

task etask; #10; endtask

export "DPI-C" task etask;

import "DPI-C" context task itask();

int itask () {
initial fork
int wasDisabled;
itask; if ( wasDisabled = etask() )
#7 disable itask; svAckDisabledState();
join return wasDisabled;
}

SystemVerilog tasks do not have a return value, so you might expect C functions corresponding to
imported tasks to return void. This is not the case – they must return int. This is to support the possibility
that the imported task might be disabled.

Suppose an imported task calls an exported task. As the exported task is written using SystemVerilog
statements, it may include delays or other timing controls. If so, the imported task may block when it is
13
called. What happens if the imported task is disabled?

Interface
The Direct Programming
The answers is that the corresponding C function returns immediately with a value indicating that it was
disabled. This is illustrated in the next slide.

The imported task itask is declared with the keyword context. This is required if the corresponding C
function is going to call an exported task. The precise meaning of context will be explained later.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 185


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

Task Disable Flow


export "DPI-C" etask; import "DPI-C" context itask();

3 Waits ...
task etask; #10; endtask
2 Call etask
int itask () {
etask();
if ( svIsDisabledState() )
svAckDisableState();
return svIsDisabledState() ;
} 5 etask returns
initial fork
1
itask; Call itask 6 itask acknowledges
#7 disable itask; any disable
4 … disable!
join 7 itask returns

This shows, step-by-step, what happens when an imported task is disabled. It also shows an alternative
(and preferred) way to check whether a task was disabled.

The imported task, itask, is called from SystemVerilog. The simulator calls the corresponding C function.
13 The C function calls the exported task, etask. The simulator calls the corresponding SystemVerilog task.
Interface
The Direct Programming

The SystemVerilog task, etask encounters a delay, #10, and starts waiting.

Whilst etask is waiting, itask gets disabled externally.

As a result, the simulator stops executing etask and the C function etask returns. The disabled status of
that task is available through the DPI function svIsDisabledState(). Normally this function returns zero,
but if it returns 1 the caller (C function itask) knows that etask returned due to a disable. The simulator is
now in the disabled state.

Before returning, itask must acknowledge the disable. It does this by calling the function
svAckDisableState.

itask now returns the value of svIsDisabledState() and control returns to the caller.

Whilst in the disabled state, a function may not call exported tasks or functions.

This protocol must be observed. If not, unexpected errors may occur.

186 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Pure and Context


• A context task or function is “context” = scope

• Allowed to call exported tasks and functions


• Access SystemVerilog data via PLI/VPI
• A pure function is
• Result is just a function of the inputs
• No side-effects (reading/writing files etc.; accessing global variables)

Non-void Inputs only

import "DPI-C" pure function int f (input int i);

import "DPI-C" context function int g (input int i);

import "DPI-C" function void h (inout int i);


End

We have seen that imported tasks that call exported tasks must be declared with the keyword context.
Imported tasks and functions may be declared context, pure or neither context nor pure (the default).

context
A context task or function is allowed to call exported tasks and functions and to access SystemVerilog
data objects (other than its arguments) by calling the PLI or VPI. The context of a SystemVerilog task or
13
function is the hierarchical scope in which it was declared (not called). The context of an exported task or

Interface
The Direct Programming
function called from an imported task or function is the context of the import declaration and not that of the
export declaration.

Special DPI utility routines exist that allow imported tasks and functions to find out and change their scope
(context) and to operate on it.

Imported tasks and functions should only be declared context if they need to be, otherwise simulation
performance may be degraded unnecessarily.

pure
A pure function is one whose return value depends only on the values of its inputs and has no side-
effects. This means it must not perform any file operations, it must not read or write anything (environment
variables, shared memory, …), and it must not access global or static variables. Simulators can optimise
calls to pure functions, because they can make assumptions about their behaviour.

Imported tasks may not be declared pure.

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 187


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

13
Interface
The Direct Programming

188 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Index
$bits 162, 163 Base type 78
$dimensions 160, 161 begin...end 18
$error 22 BFM 128
$fatal 22 bins 151
$high 160, 161 bit 7
$increment 161 Bit-stream casting 163
$info 22 break 14, 20
$left 161 Bus functional model 128
$low 161 byte 30
$monitor 105 C Programming Language 171
$random 137 C++ 171
$right 161 Canonical representation 181
$root 154 case inside 72
$sformat 28 Cast 35, 163
$sformatf 28 chandle 176
$signed 33 char 30
$size 160, 161 clocking
$strobe 105 modport 110
$unit 154 Clocking block 95
$unsigned 33 Multiple clocking 108
$urandom 137 nets 109
$urandom_range 137 Signal alias 107
$warning 22 skew 98
%p 121 Skew 103
[$] 118 Clocking drive 101
==? 71 clockvar 98
Active region 105 Combinational logic 64
Alias (Clocking) 107 context (DPI) 185, 187
always_comb
always_ff
always_latch
14, 66
65, 66
66
continue
covergroup
instantiation using new
14, 20
150
150
14
and 167 coverpoint 150

Index
APB 44 cross 152
Argument 24 cross coverage 152
pass by name 24 Cycle delay 102
Array 39 Data type
associative 123 2-state 30
dynamic 115 bit 30
literal 123 byte 30
Array literal 156 enum 34
Array Manipulation Methods 164 int 30
Array querying functions 160 logic 30
Arrays packed 38
DPI 184 signed 30
assert 21 struct 36
Assignment pattern 36, 37, 121, 123, 156, 158 typedef 34
Associative array 123 Data Types
at_least 151 Synthesis 76
automatic 15, 16 default 157
AVM 11 Default value 31

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 189


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

delete 124 Interface port 47, 51


array 115 Interfaces
queue 120 Import function 132
Design Compiler (Synopsys) 76 Ports and Parameters 52
Direct Programming Interface (DPI) 171 item 164
disable 20 Key 37
DPI 186 Label 18
do...while 20 last 155
DPI 171 Latency 106
tasks 179 len 27
DPI-C 174 logic 6, 7, 32
Drive, clocking 101 longint 30
Drive, synchronous 101 Loop
Dynamic array 115 do...while 20
DPI 184 for 14
enum 34, 78 max 165
Base type 78 Methods
Enumeration Methods 155 Array Manipulation 164
Enumeration type 34 Enumeration 155
eRM 11 min 165
exists 124 modport 56, 57
Exponentiation 85 clocking 110
export 177 import 132
find 164, 165 Modport expression 91
find_first 165 Multidimensional array 40, 82, 156
find_first_index 165 name 155
find_index 165 Named block 18
find_last 165 NBA region 105
find_last_index 165 new
first 155 instantiation of covergroup 150
for 14 new[] 115
foreach 125 next 155
function 23 Non-blocking assignment 105
argument 24 num 155
14 return
void
26
25
Observed region
Open array
105
184
generate 92 OpenVera 5
Index

get_randstate 140 Operators


IEEE 1800 5 assignment 17
iff 66 decrement 17
in covergroup 150 increment 17
import 41, 132 option
DPI 174 at_least 151
import task 133 per_instance 151
Inactive region 105 or 167
Increment 14 OVM 11
Initial value 31 package 41, 85
insert and ports 42
queue 120 vs interface 48
inside 73 packed 38, 39, 159
int 14, 30 Packed array 39, 159
interface 47 DPI 181
generic 59 packed struct 80
vs package 48 Packed struct 159
Interface instantiation 50 packed union 81

190 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

Parameter, Type 86 static 15, 16


Pass by name 24 std 138
per_instance 151 string 27
Pointer (C) 176 DPI 183
pop_back struct 36, 37
queue 120 substr 27
pop_front sum 167
queue 120 SuperLog 5
Port 42 sv_0 180
Port connection 8 sv_1 180
Ports and Parameters, Interface 52 -sv_lib 172, 173
Postponed region 105 -sv_liblist 172, 173
Preponed region 105 -sv_root 172, 173
prev 155 SVA 5
priority 67, 68 svAckDisabledState 186
process 140 svBitVecVal 181
product 167 svdpi.h 174, 180
program 111 svLogic 180
PSL 5 svLogicVecVal 178, 181, 182
pure (DPI) 187 svOpenArrayHandle 184
push_back Synchronous drive 101
queue 120 Synopsys 76
push_front Synthesis
queue 120 of new Data Types 76
Queue 118 SystemVerilog-1800 86
randcase 142 task 23
Random stability 139 argument 24
random stimulus vs void function 25
benefits 136, 148 Test harness 94, 131
Random testing testbench automation 136, 148
constrained/directed 141 Time literal 19
randomize 138 timeprecision 19
randsequence 144 timeunit 19
Reactive region 105 Type cast 25, 35
reg
Re-inactive region
7
105
Type Parameter
typedef
86
34, 37, 156
14
Re-NBA region 105 unique 69, 70, 165

Index
Replication 158 unique_index 165
return 23, 26 Unpacked array 39
reverse 166 unsigned 32, 33
rsort 166 URM 11
RVM 11 UVM 11
Scheduler regions 104, 105 var 8
Scope (DPI) 187 Verilog 5
self 140 VHDL 5
set_randstate 140 VMM 11
shortint 30 void 25
shuffle 166 VPI 187
signed 32, 33 wasDisabled 186
size 124 while 20
array 115 Wild equality 71
queue 120 Wildcard port connection 8
Skew (Clocking) 103 wire 7, 8
sort 166 with 164
srandom 140 xor 167

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 191


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

14
Index

192 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Introduction to SystemVerilog 1.0

14

Index

Copyright © 2003 - 2014 by Doulos. All Rights Reserved 193


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

194 Copyright © 2003 - 2014 by Doulos. All Rights Reserved


Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.
Restricted document for designated employees of Nvidia Corporation.
Provided for Nvidia on 17-May-2019.

You might also like