Manual
Manual
Cadence Trademarks
Trademarks and service marks of Cadence Design Systems, Inc. (Cadence) contained in this document are attributed to Cadence with the appropriate
symbol. For queries regarding Cadence’s trademarks, contact the corporate legal department at the address above or call 800.862.4522.
Other Trademarks
Open SystemC, Open SystemC Initiative, OSCI, SystemC, and SystemC Initiative are trademarks or registered trademarks of Open SystemC Initiative, Inc.
in the United States and other countries and are used with permission.
All other trademarks are the property of their respective holders.
Confidentiality Notice
No part of this publication may be reproduced in whole or in part by any means (including photocopying or storage in an information storage/retrieval
system) or transmitted in any form or by any means without prior written permission from Cadence Design Systems, Inc. (Cadence).
Information in this document is subject to change without notice and does not represent a commitment on the part of Cadence. The information contained
herein is the proprietary and confidential information of Cadence or its licensors, and is supplied subject to, and may be used only by Cadence’s customer in
accordance with, a written agreement between Cadence and its customer. Except as may be explicitly set forth in such agreement, Cadence does not make,
and expressly disclaims, any representations or warranties as to the completeness, accuracy or usefulness of the information contained in this document.
Cadence does not warrant that use of such information will not infringe any third party rights, nor does Cadence assume any liability for damages or costs of
any kind that may result from use of such information.
RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the
Rights in Technical Data and Computer Software clause at DFARS 252.227-7013.
UNPUBLISHED This document contains unpublished confidential information and is not to be disclosed or used except as authorized by written contract
with Cadence. Rights reserved under the copyright laws of the United States.
This document is for the sole use of Sundaresan Chidambaram of Manipal University
Table of Contents SystemVerilog for Verification
Table of Contents
Unions ........................................................................................................................................................ 36
Index .............................................................................................................................. i
Version 9.2
November 6, 2009
Welcome to the “SystemVerilog for Verification” module of the “SystemVerilog Language and
Application” training course. This is the second module of the three-part training course. This
module focuses on the SystemVerilog features that increase the productivity of verification
personnel.
November 6, 2009
This first chapter introduces the training materials: The topics, agenda, and some training materials
conventions.
What’s Coming...
Training Objectives
Introduce the SystemVerilog verification features
Chapter Format
Intro – Slides – Summary – Quiz – Lab
Timing Options
Real classroom: Coffee ... Breaks … Labs … Lunch …
“Virtual” classroom: On your own – but don’t forget!
11/6/2009 SystemVerilog for Verification 3
This training module focuses on the SystemVerilog features that increase the productivity of
verification personnel. It covers the “nuts and bolts” of the features, and also contains helpful hints,
and warnings about potential support and usage issues.
At the end of all but the earliest chapters is a quiz. The quiz is not interactive. Please try to work
the quiz yourself before referring to solutions in the appendix. There is a lab associated with all but
the earliest chapters. Lab practice reinforces the features presented in the slides.
In a real classroom, the instructor and students to some extent negotiate the class timing. In a
“virtual” classroom you have a lot more control over this timing. For an optimal learning
experience, you should study each chapter and do its associated quiz and lab in one session without
significant interruption.
This module focuses on the SystemVerilog features that increase the productivity of verification
personnel. Those features include:
Dynamic data types, such as classes and various kinds of arrays;
Programming constructs, such as programs and final blocks and clocking blocks;
Advanced use of interfaces for transaction-based verification (TBV);
The direct programming interface (DPI); and
Advanced verification methodologies, such as constrained randomization, coverage, assertions
and interprocess synchronization.
The instructor-led version if this module follows the agenda you see here. For self-paced learning,
the agenda is an estimate of how much time you will need to complete the training.
The “Design” module only briefly introduces the subroutine enhancements and interfaces. Those
features are more applicable to verification, so the “Verification” module explores them in greater
detail.
As the “Design” and “Assertions” modules each contain more material than you can comfortably
absorb in a day, your instructor can and should tailor this agenda to meet your actual needs.
Chapter 1
November 6, 2009
Topics
What is SystemVerilog?
This chapter introduces the SystemVerilog scope, purpose, and features. It briefly describes what
SystemVerilog is, explains why it exists, and segments the language into easily adopted subsets. It
then briefly separately highlights some of the major features.
SystemVerilog Introduction
Aim:
Unified hardware design, specification, and verification language
Realization:
Extends and adds to Verilog-2001 constructs …
Extended data types Random constraints
SystemVerilog is the IEEE Std 1800-2005 “IEEE Standard for SystemVerilog — Unified
Hardware Design, Specification, and Verification Language”.
SystemVerilog includes the IEEE Std 1364-2001 “IEEE Standard Verilog® Hardware Description
Language” by reference.
SystemVerilog makes it practical to more productively write readable and reusable code. For the
design engineer, SystemVerilog provides constructs with which to more concisely and correctly
describe hardware. For the verification engineer, SystemVerilog provides constructs with which to
use transaction, randomization, assertion and coverage methodologies.
The IEEE Std. 1364 standardized in 1995 what was previously the proprietary Verilog hardware
description language.
The 2001 update to the Verilog standard added several convenience features, still primarily
targeted at hardware design.
Accellera proposed an update to the Verilog standard that added many design convenience features
and more than tripled the verification features. This slide illustrates just how much SystemVerilog
adds to Verilog. To hopefully minimize confusion, Accellera requested and obtained the new IEEE
standard number 1800 for these new features. The SystemVerilog standard incorporates the Verilog
standard by reference – there is no such thing as “stand-alone” SystemVerilog.
SystemVerilog Segmentation
The SystemVerilog standard incorporates the Verilog standard by reference, rather than repeating it
and thus potentially diverging from the Verilog standard. You can easily segment SystemVerilog
into just those parts you need to know for your product development role.
The training materials focus first on the designer subset. This subset adds constructs with which
you can more clearly and concisely code your RTL design.
In the design verification role, you can take advantage of most of the new verification features,
without getting involved with …
The C++-like object oriented features, or
Importing or exporting C routines.
Of course, for advanced verification, you will want to take advantage of the object oriented
features, such as overloaded operators, classes, polymorphism, and more …
and you may even want to map SystemVerilog functions and tasks to C routines to reuse existing
IP written in C.
Functional
Coverage
Generation Checking
Constrained Random Assertions, Scoreboard
The SystemVerilog verification methodology relies on three building blocks that you can use
separately or together:
You want to stimulate the design with random, yet meaningful combinations of address, data
and other variables;
During simulation, you want to automatically verify that your defined design properties do not
fail to be true; and
After simulation, or potentially during simulation, you want to measure how completely the test
verifies the design functionality, and use those results to direct future test efforts.
Data Abstraction
2-state, enumerated, structured and user-defined types
SystemVerilog Randomization
Automatic random generation of
stimulus ok
ok == randomize(data)
randomize(data) with
with
Range and probability of values can {mode
{mode == small -> data
== small -> data << 100;
100;
be controlled via constraints mode == large -> data
mode == large -> data >> 200;};
200;};
Conditional
Distributed ok
ok == point.randomize()
point.randomize()
Weighted
SystemVerilog supports randcase
randcase
randomization of simple variables or 20
20 :: gen_atm;
gen_atm;
complex object-oriented data 30
30 :: gen_ethernet;
gen_ethernet;
Also additional constructs for 10
10 :: gen_ipv4;
gen_ipv4;
randomization of events and 55 :: gen_crc_error;
gen_crc_error;
sequence of data endcase
endcase
randsequence
randsequence (( main
main ))
main
main :: inc | dec;
inc | dec;
inc
inc :: {{ do_inc();
do_inc(); };
};
dec
dec : { do_dec(); };
: { do_dec(); };
endsequence
endsequence
You can randomize a scope variable, and you can at once randomize selected properties of a class
instance, optionally using constraint blocks. Constraint blocks can include constraint expressions,
that may involve conditional, distributed, and weighted constraints, and may also specify an order
in which to solve the constraints. SystemVerilog also offers the randcase construct for randomly
selecting branches of a case statement, and the randsequence construct, for generating a random
sequence of statements.
A1:
A1: assert
assert property
property (abcd);
(abcd);
An assertion “asserts” that a property of the design holds. You use assertions mainly to verify
design properties. You can also use “covers” to measure how completely the test environment
exercises the properties, and for static formal verification, you can use “assumptions” to generate
setup sequences on design inputs. These are all part of SystemVerilog Assertions.
//
// SV
SV coverage
coverage group:
group: //
// SVA
SVA functional
functional coverage
coverage
covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
clk); property
property full2empty;
full2empty;
Addr:
Addr: coverpoint
coverpoint addr
addr @(posedge
@(posedge clk)
clk)
{{ bins
bins low
low == {{ [0:’h0F],
[0:’h0F], 19
19 };
}; fifo_full
fifo_full ##[1:$]
##[1:$] fifo_empty;
fifo_empty;
bins
bins mid[]
mid[] == {{ 16,
16, 17,
17, 18
18 };
}; endproperty
endproperty
bins
bins high
high == {{ [’h14:’hFF]
[’h14:’hFF] };
}; }} C1
C1 :: cover
cover property
property (full2empty);
(full2empty);
AddrXvalid
AddrXvalid :: cross
cross Addr,
Addr, valid;
valid;
endgroup
endgroup
cg1
cg1 cover_inst
cover_inst == new;
new;
Coverage is a measure of the percentage of verification objectives that have been met. You use this
metric to evaluate the progress of your verification efforts, in order to redirect further efforts
toward areas of the design that are less well tested.
Coverage is in two general categories: tool-instrumented code coverage, that is, of lines,
statements, blocks and expressions; and user-defined functional coverage, that is, of design
behavior. All coverage described by the SystemVerilog standard refers to functional coverage.
Functional coverage measures how well the test environment exercises user-defined design
behavior. Functional coverage requires a structured approach to verification, and, often
painstaking, manual effort.
SystemVerilog C
module
module top;
top; #include
#include <stdio.h>
<stdio.h>
...
... #include
#include <svdpi.h>
<svdpi.h>
import
import "DPI-C"
"DPI-C" context
context c_imp
c_imp ==
function int imp_func(...);
function int imp_func(...); int
int c_imp
c_imp (...)
(...)
{{
res
res == imp_func(...);
imp_func(...); ...
...
...
... }}
endfunction
endfunction ...
...
The Direct Programming Interface (DPI) is an interface between SystemVerilog and a foreign
programming language that maps subroutine calls in either language to subroutine implementations
in the other language. The standard defines the SystemVerilog layer and the foreign language layer
separately: SystemVerilog does not know or care that its subroutines are called or implemented in
some other language. The standard currently defines only a C foreign layer.
97 new keywords
Many design teams have not yet fully adopted the Verilog-2001 standard. As SystemVerilog is
based on Verilog-2001, such teams will need to learn about Verilog-2001 as well as
SystemVerilog.
Having 97 new keywords means that existing Verilog designs may not compile with
SystemVerilog compilers. For example, if you have a signal named “do”, then your code will not
compile in SystemVerilog because “do” is a SystemVerilog keyword.
However, SystemVerilog does not take features away from Verilog users. Verilog-2001 features
are still available and work the same as before.
Chapter 2
November 6, 2009
Topics
Strings
Time literals
Immediate assertions
fork…join enhancements
Design features useful for verification
Strings
New data type string
\v – vertical tab
\a – bell
\xdd – hex ASCII
For Verilog, you can assign a character string literal to a reg vector.
SystemVerilog provides a string type, which is essentially a dynamic array of byte elements with
some methods defined to manipulate it as a character string. You can still do the operations you
would expect to do on a vector reg that happens to hold the ASCII representation of a character
string. Just remember that indexing a string is selecting a byte instead of a single bit.
Verilog provides escaped character sequences for including newline (\n), tab (\t), backslash (\\) and
quote (\") characters, and any octal representation of a character, within a string literal.
To these, SystemVerilog adds the form feed, vertical tab and bell characters, and any hexadecimal
representation of a character.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 4.7
String Operators
All of these string operators are just Verilog operators, so they should look familiar to you.
String Methods
putc() Replaces ith character of the string with the integral task putc(int i, byte c)
value c
str.putc(i, c);
getc() Returns ASCII code of the ith character in the string function byte getc(int i)
x = str.getc(i);
compare() Compares strings with regard to lexical ordering function int compare(string s)
str.compare(s); // compares str to s
icompare() Compares strings with regards to lexical ordering function int icompare( string s)
and is insensitive to character case str.icompare(s); // compares str to s
substr() Returns new string that is a substring formed by function string substr(int i, int j)
characters in position i through j of str.
str.substr(i,j);
The “length” ( len() ) method returns the length of the string. This length does not include any
terminating character.
The “put character” ( putc() ) method replaces the character at the specified location with the
specified ASCII value of a character.
The “get character” ( getc() ) method returns the ASCII value of the character in the string at
the specified location.
The “compare” ( compare() ) method compares the string with the specified string argument in
exactly the same manner as the “string compare” (strcmp) function of the ANSI C string
library, that is, negative if less, zero if equal, and positive if greater.
The “insensitive compare” ( icompare() ) method is like the “compare” method but is not
sensitive to character case.
The “substring” ( substr() ) method returns a new string identical to the substring that starts at
the first specified location and ends at the second specified location.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 4.7
The “to upper” ( toupper() ) and “to lower” ( tolower() ) methods return the same string with all
characters converted to upper or lower case, respectively. They do not modify the original
string.
The “ASCII to binary, octal, decimal and hexadecimal” methods return the value of a string
representation of the value. The conversion scans leading digit and underscore characters and
stops the scan at any other character. It does not accept other Verilog literal syntax. It returns 0
if it finds no valid integer literal.
The “ASCII to real” ( atoreal() ) method does accept all Verilog syntax that constructs a real
literal, and stops at any other character. It returns 0 if it finds no valid real literal.
The “binary, octal, decimal, hexadecimal and real to ASCII” methods construct a string
representation of the argument value.
Time Literals
SystemVerilog provides a time literal construct 100ns
100ns
Explicitly specifies time values 3.55ps
3.55ps
1step
1step
Integer or fixed point value
You write a time literal as an integer or fixed-point number, followed immediately by a time unit
(fs ps ns us ms s step) without an intervening space character. The new step time unit refers to the
simulation step, that is, the simulation precision. SystemVerilog scales the time literal to the
current time unit and rounds it to the current time precision. This implies that you must have a
current time unit and precision specified for the interface, module or program in which the time
literal appears.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 3.5
`timescale
`timescale 1ns
1ns // 100ps
100ps
module testmodule;
module testmodule;
reg
reg a,
a, sel,
sel, y,
y, z,
z, clk;
clk;
initial
initial
begin
begin
#20ns
#20ns sel
sel == 0;
0; //
// blocking
blocking assign
assign
#5.18ns
#5.18ns sel
sel == 1;
1; //
// rounded
rounded to
to 5.2ns
5.2ns in
in this
this example
example
YY == #1step
#1step z;
z; //
// blocking
blocking assign
assign using
using "step"
"step"
end
end
always
always @(posedge
@(posedge clk)
clk)
#5.1ns
#5.1ns aa <=
<= aa ++ 1;
1; //
// non-blocking
non-blocking assign
assign
endmodule
endmodule
You can use a time literal anywhere you would use a unitless integer or real number to represent
time.
This example uses time literals to specify procedural and intra-assignment delays.
Immediate Assertions
A procedural assert statement
Similar to an if statement
always
always @(negedge
@(negedge clock)
clock)
Ignored by synthesis A1:
A1: assert !(wr_en &&
assert !(wr_en && rd_en);
rd_en);
May be labeled
immediate assertion
Access label via %m formatter
27 CADENCE CONFIDENTIAL
11/6/2009 SystemVerilog for Verification 27
Immediate assertions are similar to if statements. The assertion succeeds if the asserted expression
in unambiguously true, and fails if the assertion expression is false or unknown. Upon assertion
failure, the tool must display the following minimum information:
The file name and line number of the assertion;
The assertion label, or if unlabelled, the scope of the assertion;
The severity level of the assertion; and
The simulation time at which the assertion failed.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 17.2
Action Blocks
You can provide an “action”
block to execute upon success, always
always @(negedge
@(negedge clock)
clock)
rw_chk:
rw_chk: assert
assert !(wr_en
!(wr_en &&
&& rd_en)
failure or both $display ("%m:
rd_en)
success");
$display ("%m: success");
else
else branch executes on else
begin
begin
assertion failure $display("read/write
$display("read/write fail");
fail");
err_count++;
err_count++;
->
-> rw_err_event;
rw_err_event;
end
end
always
always @(negedge
@(negedge clock)
clock)
assert
assert (valid)
(valid)
//
// pass
pass block
block omitted
omitted
else
else $display("valid inactive");
$display("valid inactive");
always
always @(posedge
@(posedge clock)
clock)
limit_check:
limit_check: assert
assert (err_count
(err_count << 14)
14)
else
else $display("errors >=
$display("errors >= 15");
15");
28 CADENCE CONFIDENTIAL
11/6/2009 SystemVerilog for Verification 28
You can associate an action block with the assertion. In the action block, you place code to execute
on either the success or the failure of an assertion. The pass block executes if the assertion
succeeds. The fail block executes if the assertion fails. You have the option to include neither,
either or both. It is very common to omit the pass block and just include the fail block.
Note in this example the use of the “percent m” (%m) formatter to display the assertion label.
Severity Levels
The action block may override the
default reporting behavior
$info always
always @(negedge
@(negedge enable)
enable)
assert
assert (valid)
(valid)
$warning else
else
begin
begin
$error $warning("invalid
$warning("invalid enable");
enable");
Default err_count++;
err_count++;
end
end
$fatal
Terminates simulation
29 CADENCE CONFIDENTIAL
11/6/2009 SystemVerilog for Verification 29
Assertion severity is error by default. The simulator reports assertion failure as an error and
“should” return from execution with a non-zero error code. You can override the default severity
by using these system tasks in the action block. You may use these system tasks only in an action
block. The fatal severity has the effect of terminating the simulation. These system tasks accept the
same arguments you would use with the $display system task. Just remember that prior to your
own output you will also see all the other assertion failure information.
$error("short
$error("short CE CE pulse");
Extra code required – as if we had disable CHECK;
disable CHECK;
pulse");
used if end
end
@(posedge
@(posedge CLK);
CLK);
Timing controls end
end
assert
assert (( !CE!CE &&
&& !SCLK
!SCLK ))
Loops else // long
else // long pulsepulse
$error("long
$error("long CE CE pulse");
pulse");
end : CHECK
end : CHECK
always
always @(posedge
@(posedge CLK
CLK iff
iff CE)
CE) sequence
sequence SCYC;
SCYC;
begin : CHECK
begin : CHECK (CE
(CE &&
&& SCLK)
SCLK) ##1
##1 (CE
(CE &&
&& !SCLK);
!SCLK);
repeat
repeat (16)
(16) endsequence
endsequence
begin
begin
assert
assert (( CE
CE &&
&& SCLK
SCLK )) SPI1
SPI1 :: assert
assert property
property (( @(posedge
@(posedge CLK)
CLK)
else // short pulse
else // short pulse !CE
!CE ##1 CE |-> SCYC[*16]
##1 CE |-> SCYC[*16] ##1
##1 !CE
!CE );
);
...
...
@(posedge
@(posedge CLK);
CLK);
assert
assert (( CE
CE &&
&& !SCLK
!SCLK ))
else // short pulse
else // short pulse
...
...
@(posedge
@(posedge CLK);
CLK);
end
end
assert
assert (( !CE
!CE &&
&& !SCLK
!SCLK ))
else
else //// long
long pulse
pulse
$error("long
$error("long CECE pulse");
pulse");
end
end :: CHECK
CHECK
Immediate assertions have limited functionality. They perform only an instantaneous boolean
check, and so require extra code for complex or temporal checks. This extra code can be
substantial, even for a simple protocol. The complexity of the assertion code can be comparable to
that of the design, and require as much debugging.
SystemVerilog also has concurrent assertions. They provide a syntax with which to concisely state
complex multi-cycle design properties. This training module will further discuss only concurrent
assertions.
SystemVerilog
join_none
fork a b c d
fork
Parent process does not block a;b;c;
a;b;c;
join_none
All blocks left running join_none
d;
d;
With the conventional Verilog fork-join, all forked processes must complete before the procedure
can continue.
SystemVerilog adds the join_any and join_none variants:
With join_any, the parent procedure continues when any forked process completes.
With join_none, the parent procedure continues without waiting for any forked processes to
complete. As join_none allows the parent procedure to continue while forked processes are still
running, SystemVerilog adds statements to control forked processes from outside the
fork…join_none statement.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 11.6
completes
fork
fork
The wait fork statement stops a;b;c;
a;b;c;
further execution until all forked join_any
join_any a b c
blocks complete fork d e
fork
d;
Affects all forks in current d;
join_none
join_none
scope wait
fork
e;
e;
f
wait
wait fork;
fork;
f;
f;
The disable fork statement terminates all sub-processes of the procedure that executes the
statement.
Using a fork...join_any with a later disable fork allows a SystemVerilog testbench to spawn several
concurrent processes to wait for separate events, for example separate system interrupts, and as
soon as one event is received, terminate the other outstanding processes to handle the received
event.
The wait fork statement causes the executing procedure to wait until all of its sub-processes have
completed.
With the combination of join_any, join_none, and wait fork, you can model complex concurrent
and serial behavior. The lower example illustrates this. The parent procedure spawns three
subprocesses, and upon the earliest to complete, spawns another subprocess and continues with its
own execution. Note that without timing controls, no statement execution order between the parent
and spawned processes is implied. At a later point where the parent needs the results from all
subprocesses, it waits for all to complete before continuing its own execution.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 11.8
Method Description
static function process self() Return handle of calling process
function state status() Return state of process
enum state { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED };
task await() Wait for a process to terminate
Note: Illegal to wait for self
function void suspend() Suspend a process
function void resume() Resume a suspended process
function void kill() Forcibly terminate a process
A process is a built-in class. As we have not yet discussed classes, you may want to review these
notes after you have studied the chapter about classes. Briefly, a class describes data and methods
that every object of that class has. Class methods typically manipulate the data that belongs to the
class object for which the method is called.
SystemVerilog creates the process object either statically, for example for initial and always
blocks, or dynamically, for fork blocks. A variable of the process type is a handle for one of these
process class objects. You obtain a process’s handle value by calling its self() method from within
the process. The process can then store that handle value somewhere accessible to other processes,
that can then manipulate the state of that process.
Unions
You use a union to store different data types at the same memory location.
The union size is the size of its largest field. Fields of a packed
union must be all
union the same size.
union {{
logic
logic [31:0]
[31:0] udata;
udata; //
// unsigned
unsigned data
data
integer sdata;
integer sdata; //
// signed data
signed data
}} inbus,
inbus, outbus;
outbus; //
// 32-bit
32-bit variables
variables
inbus.udata
inbus.udata == 32’h00f3;
32’h00f3;
outbus.sdata
outbus.sdata = -44;
= -44;
typedef
typedef union
union {{
int
int i;
i;
shortreal
shortreal f;f;
}} num;
num; //
// named
named union
union type
type
num
num n1,
n1, n2;
n2; //
// instances
instances of
of union
union type
type
n1.f
n1.f == 0.0;
0.0; //
// set
set n1
n1 in
in floating
floating point
point format
format
n2.i = -44; // set n2 in integer
n2.i = -44; // set n2 in integer format format
The union construct provides a way to view the same memory location as different data types
without the need to cast the data types. The SystemVerilog union is identical to the C union, with
two exceptions:
First: You cannot provide a tag name for a SystemVerilog structure or union. You can
alternatively use the typedef keyword to declare a type name for the structure or union.
Second: You can further qualify the SystemVerilog union as “tagged”. A SystemVerilog tagged
union is type-checked. The next slide has an example of a tagged union.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 4.11
The IEEE draft standard P1800/D9 proposes to allow fields of a packed union to be different sizes
if the union is also tagged so as to specify what size field is currently in use.
Union Example
Structures can contain unions and unions can contain structures.
typedef
typedef struct
struct {{
union
union {{
int
int i;
i;
shortreal
shortreal f;f;
}} n;
n; //
// anonymous
anonymous union
union type
type
bit
bit isfloat;
isfloat;
}} tagged_s;
tagged_s; //// named
named struct
struct type
type
tagged_s
tagged_s a[9:0];
a[9:0]; //array
//array of
of structure
structure containing
containing union
union
typedef
typedef union
union tagged
tagged {{
int
int i;
i;
shortreal
shortreal f;
f;
}} tagged_u;
tagged_u;
tagged_u value; int j; shortreal
tagged_u value; int j; shortreal g;
g;
value
value == tagged
tagged ff 3.14156;
3.14156;
gg == value.f;
value.f; //
// legal
legal
jj == value.i;
value.i; //
// illegal
illegal
The top example declares an array of a named structure type that contains a field that is a union.
The other structure field is a bit that indicates whether the structure union field is currently being
used as an int type or as a float type.
SystemVerilog provides the tagged union qualifier to serve exactly that purpose. A tagged union
stores both its current value and its current tag. Subsequent expressions accessing the tagged value
must be of the current tag type. This type checking is generally done during run time.
Summary
With the string type, you can create and manipulate messages
without having to worry about string sizes
With time literals, you explicitly specify the time unit, which makes your
code more robust and more readable
Immediate assertions simplify verification and error checking
Concurrent assertions provide more capability, as you will see …
Pause here for a moment, and review what you have learned about the simple SystemVerilog
verification features.
About Lab 1
For this lab you debug a 8X32 synchronous memory design.
The lab introduces the design you will use for several subsequent labs.
It uses no new verification features – you can do this lab any time.
clk
Verification Blocks
Chapter 3
November 6, 2009
Topics
Event scheduler
Programs
Final blocks
Clocking blocks
A simulation timeslot is divided into ordered regions to provide a predictable interaction between
design constructs.
The Verilog event schedule has four regions for each simulation time:
The Active region is for executing process statements.
The Inactive region is for executing process statements postponed with a “pound zero” (#0)
procedural delay.
The NBA region is for updating non-blocking assignments.
The Monitor region is for executing $monitor and $strobe and for calling user routines
registered for execution during this read-only region. You cannot within this region create
additional events.
The first three of these regions are iterative – they can schedule events that require return to the
Active region. When no more events exist for the current simulation time, the simulator executes
Monitor statements and then advances simulation time to the next time for which events are
scheduled. The simulation terminates when no such future events exist.
SystemVerilog adds regions to provide a predictable interaction between assertions, design code
and testbench code. It adds the Observed region in which to evaluate assertions, and the Re-Active
and Re-Inactive regions in which to execute assertion action blocks and testbench programs.
The SystemVerilog event schedule has 13 regions for each simulation time. Some regions used
only by the PLI are not shown on the slide and not described here:
The Preponed region is for sampling signal values before anything in the time slice changes
their values.
The Active region is for executing interface and module process statements. It is the same as
for Verilog.
The Inactive region is for executing interface and module process statements postponed with a
“pound zero” (#0) procedural delay. It is the same as for Verilog.
The NBA region is for updating non-blocking assignments. It is the same as for Verilog.
The Observed region is for assertion evaluation.
The Reactive region is for executing program process statements and assertion action blocks.
The Re-inactive region is for executing program process statements postponed with a “pound
zero” (#0) procedural delay.
The Postponed region is for system tasks that record signal values at the end of the time slice.
The Active through Re-inactive regions are iterative. Most of these regions can schedule events
that require return to the Active region.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 9.3
Programs: program
A new separately-compilable unit similar to a module
module top;
top;
module: clkgen
clkgen cgen(clk);
cgen(clk);
mem_interface
mem_interface intfc(clk);
intfc(clk);
Intended to separate test and design mem_design
mem_design dut (intfc);
dut (intfc);
Cannot instance or embed other units mem_test
mem_test tb
tb (intfc);
(intfc);
endmodule
endmodule :: top
Cannot have always, specify, etc. top
A program cannot contain hierarchy and cannot have an always block, but is otherwise similar to a
module, with some additional restrictions. It is intended to remove testbench stimulus from the
module construct and instead execute those statements in the Reactive region separately from the
execution and update of design code. To further reduce the potential for user-induced clock/data
races, the program construct requires the user to make only blocking assignments to program
variables and only non-blocking assignments to module and interface variables.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 16
final
final begin
begin
if (timeout_error)
if (timeout_error)
$display
$display ("ERROR:
("ERROR: %0t:
%0t: Test
Test Timed
Timed Out",
Out", $time);
$time);
else
else
$display
$display ("INFO:
("INFO: %0t:
%0t: Test
Test Complete",
Complete", $time);
$time);
$display("Error
$display("Error Count:
Count: %d",
%d", error_count);
error_count);
$display("Fifo
$display("Fifo Overflow
Overflow Count:
Count: %d",
%d", fifo_overflow);
fifo_overflow);
end
end
A final block cannot consume simulation time, but is otherwise similar to an initial block. You can
place a final block anywhere you can place an initial block. SystemVerilog executes all final
blocks exactly once at the end of simulation, in an unspecified but repeatable order.
SystemVerilog executes the final blocks upon encountering $finish or upon running out of events
to process. Any final block that itself calls $finish or calls a user-defined system task or function
that terminates the simulation (invokes tf_dofinish() or vpi_control(vpi_finish…)), aborts
execution of the final blocks. After SystemVerilog completes or terminates execution of the final
blocks, it calls any PLI routines you have scheduled for execution at the end of the simulation.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 10.7
clocking
clocking cb
cb @(posedge
@(posedge clk);
clk);
input
input dout;
dout;
output
output reset,
reset, enable,
enable, data;
data;
endclocking
endclocking
A clocking block defines a set of timing, relative to a specified clock, for a set of signals. Any
number of signals may appear in any number of clocking blocks. You declare a clocking block as
an interface, module or program item. You can declare one clocking block in any scope to be the
default clocking block for that scope. You apply the timing by referencing the signal hierarchically
through the clocking block name.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 15
clocking
clocking cb
cb @(posedge
@(posedge clk);
clk);
default timing skew
default
default input
input #1step
#1step for inputs/outputs
output
output #3;
#3;
input to
clocking block input
input dout;
dout;
output
output reset,
reset, data;
data;
output
output negedge enable;
negedge enable;
outputs from SystemVerilog
clocking block endclocking
endclocking explicit keywords in bold
timing skew
A clocking block is both a declaration and instance of that declaration. You do not instantiate a
clocking block.
Pause here for a moment, and examine the clocking block syntax.
--------
clocking_declaration ::=
[ default ] clocking [ clocking_identifier ] clocking_event ;
{ clocking_item }
endclocking [ : clocking_identifier ]
The IEEE draft standard P1800/D9 proposes to move output transitions to a new “Re-NBA” region
after the Re-Inactive region.
clocking
clocking cb
cb @(posedge
@(posedge clk);
clk);
default input #1step
default input #1step
output
output #3;
#3;
clk input
input dout;
dout;
output
output reset,
reset, data;
0 5 10 data;
output
output negedge enable;
negedge enable;
dout reset enable endclocking
endclocking
data
def clocking
clocking cb
cb @(posedge
@(posedge clk);
clk);
two output
output def;
def;
output
output #2
#2 two;
two;
neq
output negedge neg;
output negedge neg;
@(cb) #1 // same result as endclocking
endclocking
@(cb) @(negedge clk)
initial
initial
def begin
begin
...
... //
// timing
timing control
control
two
cb.def<=1;
cb.def<=1;
neq cb.two<=1;
cb.two<=1;
cb.neg<=1;
cb.neg<=1;
@(cb) @(negedge clk) #1
end
end
def
two
neq
11/6/2009 SystemVerilog for Verification 50
The year 2005 standard neglects to specify the clock from which the output skew is referenced.
Most vendors drive outputs with reference to the current clock for assignments made in the same
time slice in which the clock edge occurs.
For this example, an output with the default “pound zero” (#0) skew is driven in the next NBA
region of the current time slice if the assignment is scheduled in the same time slice in which the
positive edge of the clock occurred. The first set of waveforms illustrates this. Likewise, an output
with the “negedge” skew is driven in the next NBA region of the current time slice if the
assignment is scheduled in the same time slice in which the negative edge of the clock occurred.
The second set of waveforms illustrates this.
You can use either of two methods to declare a clocking block to be the default clocking block of
the scope:
You can directly declare a default clocking block; or
You can declare an already declared clocking block to be the default clocking block.
Only one default clocking block can exist in a module, interface or program.
A default clocking block is valid only in the scope declaring it and any nested declarations.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 15.11
@(cb.dout) 5 8 10
@(posedge clk iff cb.dout) clk
To apply the timing of a clocking block, you hierarchically reference the signal through the
clocking block name.
To synchronize a process to a clocking block, you can either:
Directly use the clocking block name in a sensitivity list. This triggers the sensitive process on
the clocking block clock event.
Use clocking block signal inputs, or slices of them, in a sensitivity list. This triggers the
sensitive process on the clocking block signal event.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 15.9, 15.13
The semantics of the cycle delay (##N) operator differ depending upon how you use it:
When used as a procedural or intra-assignment delay in an assignment to a clocking block
signal, it refers to the clock event of the specified clocking block. These assignments must use
the non-blocking operator.
When not used as a procedural or intra-assignment delay in an assignment to a clocking block
signal, it refers to the default clocking block. For this situation, the compiler shall issue an error
if no default clocking block has been declared.
--------
clocking_drive ::=
clockvar_expression <= [ cycle_delay ] expression
| cycle_delay clockvar_expression <= expression
The IEEE draft standard P1800/D9 proposes to make the use of cycle delay timing controls illegal
for intra-assignment delays.
Hierarchical Expressions
A clocking block signal cannot be a hierarchical expression
You can associate a clocking block signal with a hierarchical expression
clocking
clocking cb
cb @(posedge
@(posedge clk);
clk);
default input #1step
default input #1step
output
output #3;
#3;
input
input dout;
dout;
//data
//data associated
associated with
with hierarchical
hierarchical expression
expression
output reset, data = top.dut.data;
output reset, data = top.dut.data; //
// Valid
Valid
//
// output
output top.dut.data;
top.dut.data; // Invalid
// Invalid
output
output negedge
negedge enable;
enable;
endclocking
endclocking
You cannot within a clocking block directly declare a hierarchical signal. You must instead declare
a clocking block virtual signal and associate it with an expression that references the hierarchical
signal. The semantics of this association are much like those for a port connection – you can use
any expression, including slices, concatenations and part selects, that you can legally connect to a
port of that direction.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 15.4
Summary
What can you do with new verification blocks
Clearly separate verification from design code with a program
For potentially reducing user-induced races
Pause here for a moment and review what you have learned about SystemVerilog programs, final
blocks, and clocking blocks.
Quiz
1. What do the input, inout, and output clocking directions signify?
2. How is a specific clocking block defined as the default?
3. Given the clocking blocks, what do these delays represent?
clocking
clocking cb1
cb1 @(negedge
@(negedge clk);
clk); @(cb1);
@(cb1);
output
output posedge
posedge drive;
drive; ##2;
##2;
endclocking
endclocking cb1.drive
cb1.drive <=
<= ##3
##3 1;
1;
clocking
clocking cb2
cb2 @(negedge
@(negedge clk);
clk); ##3;
##3; cb1.drive <=
cb1.drive <= 2’b10;
2’b10;
endclocking ##(j-2)
##(j-2) cb1.drive
cb1.drive <=
<= 1;
1;
endclocking
default
default clocking
clocking cb2;
cb2;
int j = 3;
int j = 3;
Solutions in Appendix A
About Lab 2
For this lab you define a clocking block for the memory testbench and use
the clocking block timing while testing the memory.
clk
Chapter 4
November 6, 2009
Topics
What is a Transaction?
Transactors
Transaction structure
Interface review
Interface transactors
This chapter introduces transaction-based verification and discusses how you can use a
SystemVerilog interface as a transactor.
What is a Transaction?
A transaction is a transfer of data between
design blocks
A transfer may be composed of many
A B
signal transitions over several cycles
Working with data at the transaction level signal representation
raises our abstraction level
ADDR
Transfers are easier to create, debug, AS
maintain and manage
RW
Transactions may have parameters DS
Using transactions allow you to design and debug at a higher level of abstraction. That is, you
develop and analyze your system-level design and test environment using transactions, rather than
wading through a sea of signal waveforms.
Transactions are most useful when you have well-defined interfaces for your design and test
environment. Interface methods implement the transactions.
The use of transactions is not a new concept. Transaction-based verification has been used for
many years. Many HDL verification environments already do transaction-based verification.
Stimulus
Generator Transactor or TVM
do_burst_write
...
proc.do_read Transactions address =
ff001000
proc.do_write data = 00112233
00 11 22 33
... Signals
Design or DUV
Design
ff*
00 11 22 33
Transactor Structure
A transactor has three parts
Transaction interface
Stimulus generator Transactions do_read
(transaction producer) Transaction
Interface do_write
Response checker
(transaction consumer)
do_rmw
Signal interface Transactor
DUV ports Functional
Code
Transactor functionality
Signal
Create data sequences
to drive signal interface Transactor Interface
or TVM
Checks signal interface Signals
sequences for correct
operation
For this example, a higher level wrapper module instantiates the testbench, interface, and design
under verification.
For more complex interfaces and transactions, you may find it easier to verify the design with
direct access to the interface signals rather than relying solely upon interface methods. This permits
more control over the design stimulus, for example, you can craft fork-join blocks “on-the-fly” that
probably do not exist in the regular reusable interface methods.
Most SystemVerilog testbenches utilize this flexible approach, using a combination of interface
methods and testbench functionality.
Utilizing a Modport
You can define a modport to interface
interface spi_if(input
spi_if(input clk);
clk);
prevent the DUV from accessing logic
logic sdi,
sdi, sdo;
the transaction interface logic
logic ce;
ce;
sdo;
You would ideally like to define different views of the interface, to restrict what contents each user
can access, and how that user can access that contents. For an interface, such a view is called a
modport.
This example interface defines one modport for the testbench, that treats the data input signal as an
output and has access to the interface methods, and defines another modport for the design under
verification, that treats the input signal as an input and has no access to the interface methods.
You can define clocking blocks within an interface, and interface methods can then access design
signals using the timing of the clocking block. Testbench modules can use the clocking block
through a modport. The specified direction of the clocking block signals is from the perspective of
the module connected to the modport that uses the clocking block. You would be more likely to use
a clocking block for a module that represents testbench than for a module that represents design
behavior.
Virtual Interfaces
virtual [interface] interface_type identifier;
identifier = interface_instance;
A virtual interface is a variable that represents an interface instance
Your test code assigns actual interface instances to the interface
variable at various time throughout the test
assignment (=) of interface instance, interface variable, or null
comparison (==,!=) to interface instance, interface variable, or null
You test code can pass interface variables to tasks and functions
Your test code procedurally accesses methods and variables (but not
nets) of that interface instance through the interface variable
Each interface instance drives its own nets, through clocking blocks or
continuous assignments, that can reflect variable changes made by test
Use of virtual interfaces promotes code reuse (write once, use often)
A virtual interface is an interface variable that at various times throughout the test can represent
different interface instances. By iterating through an array of interface variables and passing each
element in turn to a test task, your one testbench task can test multiple instances of similar design
blocks, each through its own interface instance.
You can declare virtual interfaces in modules, programs, classes and packages. You can pass
virtual interfaces by value, but not be reference, to tasks and functions.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 20.8
This example defines an inverter interface and an inverter module and a test module. The test
module declares two interface instances and two inverter instances and connects each inverter to its
respective interface. It declares an interface variable and defines a test task to test the inverter
connected to the interface that the interface variable currently represents. The test block, in turn,
assigns each interface to the interface variable, and calls the test task to test the inverter connected
to that interface.
Benefits of TBV
You can verify designs at transaction level instead of signal level
Transaction based verification is all about doing your system-level development and verification
with abstract transactions, without getting involved with an ocean of individual signal transitions.
You can partition test development into stimulus, which deals solely with transactions, and the
transactors, which do have to know about the design implementation. Personnel involved with
stimulus development can be reasonably confident that they can reuse tomorrow, their work done
yesterday, while the block designers continually refine their own designs.
This methodology works only as well as your design team adheres to a top-down design flow,
where the design specification is defined before it is implemented.
Transaction-based verification (TBV) has been used for many years. What is relatively new is the
increasing tool support, in simulators and waveform viewers, for the creation, recording and
analysis of transactions, and recently developed high-level verification languages such as e,
SystemC and SystemVerilog, that support more sophisticated transaction-based verification by
borrowing from the software world, powerful proven concepts such as object-oriented design and
dynamic resource generation.
Summary
Transaction Level Modelling raises abstraction level of the test
Most of the test is not involved with low level signal transitions
SystemVerilog interfaces are ideal for modelling transactors
Pause here for a moment and review what you have learned about how SystemVerilog interfaces
support a transaction based verification methodology.
Quiz
1. What is a transaction?
Solutions in Appendix A
About Lab 3
For this lab you use a program block, an interface and a clocking block to
test a memory design. You move the write_mem() and read_mem()
subroutines into the interface, thus making it a transactor.
read_mem()
clk
Classes
Chapter 5
November 6, 2009
Topics
Class overview
Information hiding
Virtual classes
Polymorphism
11/6/2009 SystemVerilog for Verification 74
This chapter introduces the SystemVerilog Object-Oriented Programming (OOP) features, that is,
classes and their associated features. A class is a dynamic data type, declaring data variables, but
not nets, and methods to manipulate the data, that you can create and delete during run time. A
class is an excellent type for representing such things as ATM cells and ethernet packets, things for
which there might during simulation be an undetermined number of instances.
This section describes SystemVerilog's implementation of classes and only very briefly mentions
Object-Oriented Programming.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.
Class Overview
A class is a user-defined data type
Inheritance
Polymorphism
A class is a user-defined type, that declares data variables, and methods to manipulate the data, and
that you can dynamically create and delete during the simulation. You use classes for such things
as transactions, where during simulation, an undetermined number may come into being and then
disappear.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.3
A class is a user data type. To use a class, you declare a variable of the class type. The variable is a
handle that “points” to a location in memory that holds the class properties. An uninitialized handle
has a null value. You create a class instance by calling a special class method called “new”. The
“new” method is a function that allocates an area of memory for the class properties and places a
pointer to that area in the class object handle variable.
Unlike C++, SystemVerilog automatically deletes the dynamically created object, when either you
explicitly assign the null value to the handle, or that handle “goes out of scope”, for example, when
a subroutine that declares the handle locally, returns.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.4
A SystemVerilog class handle is only somewhat like a C++ pointer. SystemVerilog, for ease of
use, restricts what you can do with a class handle. Things you cannot do include:
Arithmetic operations, for example incrementing and decrementing.
Using with any arbitrary data type.
Assigning the address of a variable.
Dereferencing.
Casting to any arbitrary pointer type.
On the other hand, SystemVerilog automatically deletes class objects that your code can no longer
access, thus helping you to prevent “memory leaks”, where you repeatedly create dynamic objects
and forget to delete them when you no longer need them.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 7.5, 7.26
The class type declaration also declares class members. The members can be variables, called
“properties”, and they can also be tasks or functions, called “methods”, to manipulate the class
properties. You reference the members of a class object by using the “dot” (.) notation.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.5, 7.6
A structure is a user-defined type, representing a collection of data. Structures offer only this
simple data grouping mechanism. You declare nets and variables of the structure type.
A class is a user-defined type, representing a collection of data and methods to manipulate the data.
Classes offer Object Oriented features, such as dynamicism, data encapsulation, class inheritance,
polymorphism, abstract classes and dynamic casting. You declare variables of the class type,
whose value is a “handle” (pointer) to a class object. The handle is initially null. You assign to the
variable the value returned by calling the class’ new() constructor method, which allocates memory
for, and constructs, a class object.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.5, 7.6
myClass
myClass c2
c2 == new;
new;
initial
initial
begin
begin
c2.set(3);
c2.set(3);
$display("c2:
$display("c2: %d",
%d", c2.get());
c2.get());
end
end
11/6/2009 SystemVerilog for Verification 80
You can implement a class method outside of the class declaration. To do this, you must first
prototype the method within the class declaration. A prototype is the extern keyword followed by
the entire method header, exactly as if you were implementing the method, but without the method
code body. You then implement the method, including the code body, outside the class declaration,
exactly as if you were implementing it within the class declaration, but with one difference. As the
method is not a member of the outside scope, but is a class member, you must declare its type as a
type of the class. You do this by prefixing the method name with the class name and the scope
resolution operator.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.22
Class Constructor
Method new is a special class method called a constructor
Defined by default for all classes
You can reimplement it to do initialize your class properties
No return type – otherwise like any other class method
expcon
expcon c2
c2 == new;
new; parcon
parcon c3
c3 == new(5);
new(5);
//
// c2.number
c2.number == 55 //
// c3.number == 55
c3.number
Properties of 2-state types initially take on the 0 value and 4-state types take on the unknown value.
You can initialize them to some other value as you declare them. To initialize properties on a per-
instance basis requires you to reimplement the constructor. The constructor is like any other
function, except that it does not have a return type, not even void.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.7
Class Example
Note the following:
class
class frame;
frame; Initial property value
logic
logic [4:0]
[4:0] addr;
addr;
logic Constructor arguments
logic [7:0] payload;
[7:0] payload;
bit assigned to properties
bit parity
parity == 0;
0;
Constructor calls method to
function
function new
new (input
(input int
int add,
add, dat);
dat); get updated parity value
addr = add;
addr = add;
payload
payload == dat;
dat; Application repeatedly calls
parity
parity == par({addr,
par({addr, payload});
payload}); method to serialize frame
endfunction
endfunction
function
function bit
bit par
par (input
(input logic[12:0]
logic[12:0] ip);
ip);
return (^ip);
return (^ip);
endfunction
endfunction frame
frame one
one == new(add1,
new(add1, data1);
data1);
frame
frame two
two == new(3,
new(3, 4);
4);
function
function logic
logic [13:0]
[13:0] getframe();
getframe();
return({addr,
return({addr, payload, parity});
payload, parity}); for
for (int
(int i=0;
i=0; i<14;
i<14; i++)
i++) begin
begin
endfunction
endfunction @(negedge clk);
@(negedge clk);
endclass
endclass serial
serial == one.getframe()[i];
one.getframe()[i];
end
end
You can initialize class properties, and the constructor can then also update those values, usually
using passed-in argument values. As SystemVerilog does not support method overloading, there
can be only one implementation of each class type’s constructor.
The frame serialization method depicted here is inefficient. A more efficient implementation would
call the getframe method once and save the value in a local variable.
Static Properties
class
class frame;
frame; Exactly one instance of a static
static
static int
int frmcount=0;
frmcount=0; property always exists, not
int
int tag;
tag;
logic
associated with any objects
logic [4:0]
[4:0] addr;
addr;
logic
logic [7:0]
[7:0] payload;
payload; This example uses a static property
bit parity;
bit parity; to provide a unique identifier for each
new frame
function
function new(input
new(input int
int add,
add, dat);
dat);
addr = add;
addr = add; Initially 0
payload
payload == dat;
dat;
parity
parity = par({addr, payload});
= par({addr, payload}); Incremented by constructor
tag
tag == ++frmcount;
++frmcount; one
endfunction
endfunction tag 1
frame
frame one
one == new(1,
new(1, 0);
0); addr 1
...
... payload 0
endclass
endclass frame
frame two
two == new(3,
new(3, 2);
2); parity 1 frmcount: 1
two
tag 2
addr 3
payload 2
parity 1 frmcount: 2
Each class instance has its own unique copy of a “normal” property. Only one copy of a static
property exists, and all class instances access that same copy, in fact, you can access that copy
through the class name without having any instances of the class.
<class_name>::<static_member>
This example utilizes a static property so that it can assign a unique identity to each instance of the
class. The constructor increments the frame count and assigns the new value to each instance’s tag.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.8
Static Methods
A static method knows nothing about the current object – so can access
only static properties.
class
class frame;
frame;
static
static int
int frmcount;
frmcount;
int tag; Access static members in two ways:
int tag;
logic
logic [4:0]
[4:0] addr;
addr; Through any handle of the class
logic [7:0] payload;
logic [7:0] payload; type using the dot operator (.)
bit
bit parity;
parity;
Through the class type name
function
function new(input
new(input int
int add,
add, dat);
dat); using the scope resolution
addr
addr == add;
add; operator (::)
payload
payload == dat;
dat;
parity
parity == par({addr,
par({addr, payload});
payload}); frame
frame one
one == new(add1,
new(add1, data1);
data1);
tag
tag == ++frmcount;
++frmcount; frame
frame two = new(3, 4);
two = new(3, 4);
endfunction int
int frames;
frames;
endfunction
initial
initial begin
begin
two
two == null;
null;
static
static function
function int
int getcount();
getcount(); frames
frames == one.getcount();
one.getcount(); //
// 22
return (frmcount);
return (frmcount); frames
frames = two.getcount(); // 22
= two.getcount(); //
endfunction
endfunction frames
frames == frame::getcount();
frame::getcount(); //
// 22
...
... end
end
endclass
endclass
11/6/2009 SystemVerilog for Verification 84
Only static methods can manipulate static properties, and they cannot manipulate anything other
than static properties. You can access static methods through any handle of the class, or through the
class name.
<class_name>::<static_member>
This example uses a static function to return the current frame count, which is a static property.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.9
You can use the “this” keyword as a handle to the current object. Within SystemVerilog, the only
practical application is to access class identifiers that have been locally redeclared.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.10
Your class type declaration can itself declare handle variables of other class types. This is known
as an aggregate class type.
In this example, the tagframe class type declaration declares a handle variable of the frame class
type. The tagframe constructor calls the frame constructor to initialize the frame object within the
tagframe object. You can say that the tagframe object “has” a frame object, much as a truck “has”
an engine.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.11
Your class type declaration can extend the declaration of another class type. This is known as class
inheritance. The parent class type declaration is known as the superclass or base class, and the
extended child type declaration is known as the subclass or derived class. SystemVerilog supports
inheritance of only a single parent class type.
In this example, the tagframe class type declaration extends the frame class type declaration. The
tagframe constructor calls the frame constructor to initialize the parent class’ part of its variable
set. You can say that the tagframe object “is” a frame object, much as a truck “is” a vehicle, albeit
one with additional properties.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 7.11, 7.12
Simple Inheritance
super class Subclass inherits superclass members:
class
class frame;
frame;
logic
logic [4:0]
[4:0] addr;
addr; Can add more members
logic
logic [7:0]
[7:0] payload;
payload;
bit
bit parity;
parity; Can redeclare superclass methods
function
function new(input
new(input int
int add,
add, dat);
addr = add;
dat); A subclass type is the superclass type –
addr = add;
payload
payload == dat;
dat; syntax to access members is identical
parity
parity == par({addr,
par({addr, payload});
payload});
endfunction
endfunction
e.g. a truck is a vehicle
...
Subclass constructor automatically calls
...
endclass
endclass
superclass constructor
sub- class For this example, entry into
tagframe.new() immediately
class
class tagframe
tagframe extends
extends frame;
static
static int
int frmcount;
frmcount;
frame;
implicitly calls frame.new()
int
int tag;
Why is this a problem?
tag;
function
function new;
tag
new;
tag == ++frmcount;
++frmcount; Error tagframe
tagframe one
one == new;
new; //???
//???
endfunction
endfunction
...
...
endclass one.addr
one.addr == 0;
0;
endclass
11/6/2009 SystemVerilog for Verification 88
An object of the subclass can by default access all the members of the superclass as if they were
members of the subclass. The subclass can redeclare superclass methods and variables, and objects
of the subclass will by default access their own versions of those members. The subclass can also
declare additional property and method members.
Implicit subclass constructors, upon entry, immediately first call the constructors of their
superclass. These implicit constructor calls do not know anything about your constructor
arguments. If you reimplement the superclass constructor to accept arguments, then you must also
reimplement the subclass constructor to provide those arguments to the superclass constructor.
--------
This example illustrates the failure to do that.
sub- class
class
class tagframe
tagframe extends
extends frame;
frame;
static int frmcount;
static int frmcount;
int
int tag;
tag;
tagframe
tagframe one
one == new(add1,
new(add1, data1);
data1);
tagframe two = new(3, 4);
tagframe two = new(3, 4);
function
function new(input
new(input int
int add,
add, dat);
dat);
super.new(add, dat); one.addr
super.new(add, dat); one.addr == 0;
0;
tag
tag == ++frmcount;
++frmcount;
endfunction
endfunction
...
...
In this example, the reimplemented subclass constructor, upon entry, immediately explicitly calls
its superclass constructor to pass “up” the initialization arguments. Use the super keyword within a
subclass to reference the superclass version of the member methods that the subclass has
redeclared.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.16
Multi-Layer Inheritance
You can layer inheritance to frame class
class frame;
frame;
base class
You can layer inheritance to an undetermined number of levels. Each subclass constructor must,
upon entry, immediately call the constructor of its superclass, using the “super” keyword. As there
is no such thing as “super.super”, you can reference only one higher level at a time.
SystemVerilog class members are by default “public”, that is, code external to the class type
declaration can access them. Data encapsulation, or “hiding”, is an important tenet of Object-
Oriented Programming. You can declare class members “private” using the local keyword. Only
member methods of the class type declaration can access those private members. You can declare
other class members using the protected keyword. Only member methods of the class type
declaration and their subclasses can access those protected members.
In this example the frame class parity variable is protected, so only the frame class and its
subclasses can access that variable. Application code cannot access the parity variable. The
tagframe class frame count is local, so only methods declared within the tagframe class can access
the frame count.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.17
You can use a handle of a superclass type to “point” to objects of its subclasses. Remember that
any of the subclass type declarations can redeclare members of its superclass, thus creating a
hierarchy of member method implementations.
For a “normal” method, the compiler selects which version to use based upon the handle type, so a
handle of the superclass type, even if it “points” to a subclass object, accesses the methods declared
in the superclass type.
For a virtual method, the compiler creates a look-up table, so that during simulation, the method
implementation is selected based upon the subclass type of the object that the superclass handle
happens to “point” to during the method call, so a handle of the superclass type, if it “points” to a
subclass object, accesses the methods declared in that subclass type. This mechanism is called
“polymorphism”, literally, “having many forms”.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.20
class
class tagframe
tagframe extends
extends frame;
frame;
Prototype only – no implementation
...
...
function Subclass provides implementation
function logic
logic [13:0]
[13:0] getframe;
getframe;
//define for tagframe
//define for tagframe You instantiate the subclass
endfunction
endfunction
endclass sub-class
endclass
class
class errframe
errframe extends
extends tagframe;
tagframe; frame
frame one
one == new(1,
new(1, 2);
2); //
// error
error
...
... tagframe
tagframe two
two == new(3,
new(3, 4);
4);
function
function logic
logic [13:0]
[13:0] getframe;
getframe; errframe
errframe three
three == new(5,
new(5, 6);
6);
//define for errframe
//define for errframe frm = two.getframe;
frm = two.getframe;
endfunction
endfunction frm
frm == three.getframe;
three.getframe;
endclass
endclass
You can use the “virtual” keyword to declare a class type that cannot be instantiated. A virtual
class type declaration would presumably declare the base variables upon which some other class
type declaration that can be instantiated would build. A class you cannot instantiate is called an
“abstract” class.
Only an abstract class can declare “pure” virtual methods. A pure virtual method is a method
declared with the virtual keyword, and prototyped, but not implemented. As it has no code body,
you can declare it only in a class type that cannot be instantiated. A subclass type declaration must
inherit the pure virtual method and provide its implementation. Only at a level in the class type
declaration hierarchy where all pure virtual methods have been implemented, can you instantiate
an object of that subclass type.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.19
The IEEE draft standard P1800/D9 proposes the “pure” keyword be added to optionally indicate a
pure virtual function.
Class Parameters
You can parameterize a class class
class stack
stack #(parameter
#(parameter type
type st
st == int);
int);
declaration just like you local st data[100];
local st data[100];
parameterize an interface, static
static int
int depth;
depth;
module, or program. A parameter
task
task push(input
push(input st
st indat...
indat... );
can be a value or any arbitrary );
...
type – hence “template class”. ...
endtask
endtask
You can override parameter task
task pop(
pop( output
output st
st outdat
outdat ...
... );
);
values and types on an per- ...
...
endtask
instance basis endtask
endclass
endclass
Methods using type
parameters must work for all //
// int
int stack
stack (default)
(default)
stack
stack intstack
intstack == new;
expected type overrides new;
Each combination of //
// 8-bit
8-bit vector
vector stack
stack
parameter types has its own stack
stack #(logic[7:0]) bytestack
#(logic[7:0]) bytestack == new;
new;
set of any static variables
They can share static
variables from an inherited
non-templated superclass
11/6/2009 SystemVerilog for Verification 94
You can define a parameterized class, for example a queue, for which you can override the width,
depth, and even the type, of the data that it contains, on a per-instance basis.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 7.23
Summary
With the SystemVerilog Object-Oriented testbench features, you can:
Declare dynamic data types
Create and destroy objects during simulation
Covergroups
Pause here for a moment and review what you have learned about how the SystemVerilog object-
oriented testbench features help you to define temporary objects, such as transactions.
Quiz
1. What is the role of a class constructor?
4. What is inheritance?
About Lab 4
For this lab you explore the support
SystemVerilog provides for object-
counter_c
oriented design:
# count : int
Class declaration + new ( int )
+ putval ( int ) : void
Class constructor + getval () : int
Class properties and class
methods
Class aggregates upcounter_c downcounter_c
Random Stimulus
Chapter 6
November 6, 2009
Topics
Random and directed stimulus
Scope randomization
Constraints
randcase
randsequence
This chapter explores SystemVerilog features for randomization of variables and randomly
selecting statements for execution.
4 registers
REG0, REG1, REG2, REG3
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| X | X | X |- opcode -|- reg -|- - - - - - data - - - - - - -|
Let’s take a look at why you might want to generate random stimulus and constrain it to
meaningful sets of values.
The CPU instruction contains an 8-bit data field, a 2-bit register field to address four registers, and
a 3-bit opcode field to specify one of seven immediate instructions.
typedef
typedef enum
enum {ADDI,
{ADDI, SUBI,
SUBI, ANDI,
ANDI, XORI,
XORI, JMP,
JMP, JMPC,
JMPC, CALL}
CALL} opcode_t
opcode_t ;;
typedef
typedef enum
enum {REG0,
{REG0, REG1,
REG1, REG2,
REG2, REG3}
REG3} regs_t
regs_t ;;
opcode_t
opcode_t opcode;
opcode;
regs_t
regs_t regs;
regs;
logic[7:0]
logic[7:0] data;
data;
initial
initial begin
begin
//generate
//generate stimulus
stimulus
opcode
opcode == ADDI;
ADDI;
regs
regs == REG0;
REG0;
data
data == 5;
5;
//pass
//pass toto transactor
transactor
We can declare enumerated types for the opcode and reg values. We assume that a transactor will
translate the opcode, reg and data values into a form where they can be applied to the design with
the correct format and timing, all we have to do is to generate values for the three variables and
pass them to the transactor.
typedef
typedef enum
enum {ADDI,
{ADDI, SUBI,
SUBI, ANDI,
ANDI, XORI,
XORI, JMP,
JMP, JMPC,
JMPC, CALL}
CALL} opcode_t
opcode_t ;;
typedef
typedef enum
enum {REG0,
{REG0, REG1,
REG1, REG2,
REG2, REG3}
REG3} regs_t ;
regs_t ;
opcode_t
opcode_t opcode;
opcode; Use nested loops to
regs_t
regs_t regs;
regs; index through the
logic[7:0] data;
logic[7:0] data;
7168 combinations
initial
initial
for
for (opcode=opcode.first();
(opcode=opcode.first(); opcode<=opcode.last();
opcode<=opcode.last(); opcode=opcode.next())
opcode=opcode.next())
for (regs=regs.first(); regs<=regs.last(); regs=regs.next())
for (regs=regs.first(); regs<=regs.last(); regs=regs.next())
for
for (data=0;
(data=0; data<=255;
data<=255; ++data)
++data) ADDI REG0 00 - FF
@(posedge clk);
@(posedge clk); ADDI REG1 00 - FF
...
... ADDI REG2 00 – FF
ADDI REG3 00 - FF
SUBI REG0 00 – FF
SUBI REG1 00 – FF
SUBI REG2 00 – FF
SUBI REG3 00 - FF
AND1 REG0 00 – FF
...
A simple approach uses nested loops to generate all possible values over the ranges of the three
variables.
The outer loop iterates over the opcode values, the middle loop over the reg addresses, and the
inner loop over the range of data values.
We then measure the structural and functional coverage of the 7168 patterns. Structural metrics,
consisting of coverpoints and cover crosses in a covergroup (see a later chapter), look good – we
have applied every possible value to each variable. Functional metrics, consisting of cover
statements with design properties (see a later chapter), do not look good. We have missed most of
the transitions between values.
Applying all such transitions requires a much more complex set of stimulus loops!
What we really want to do is to generate random sequences of operations, registers and data, and
constrain those random combinations to meaningful combinations.
Class-based randomization
With randomization, you can with minimal testbench code produce large amounts of stimulus that
thoroughly and effectively verifies the design.
With constraints, you can restrict the generated values and value transitions to those that are
meaningful.
SystemVerilog offers two forms of randomization:
Randomization of local variables, covered in this chapter, and
Randomization of class properties, covered in the next chapter.
A truly random sequence of values is largely useless for verification and debug of digital designs.
For verification and debug of digital designs, we need a pseudo-random repeatable sequence.
These sequences are generated algorithmically. The algorithm is typically a function that accepts a
seed reference as an argument, and when called, generates a new seed, from which it returns a the
new random value. Ensuring that the seed has the same initial value, ensures that the random
sequence is the same.
int
int value;
value;
bit
bit [63:0]
[63:0] addr;
addr;
value
value == $urandom();
$urandom(); //
// 32
32 bit
bit random
random number
number
value = $urandom(8);
value = $urandom(8); //
// 32
32 bit
bit random
random number
number with
with seed
seed
addr
addr == {$urandom,$urandom};
{$urandom,$urandom}; //
// 64
64 bit
bit random
random number
number
value
value == $urandom_range(10,2);
$urandom_range(10,2); //
// 32
32 bit
bit random
random number
number from
from 22 to
to 1010
value
value == $urandom_range(16);
$urandom_range(16); // 32 bit random number from 0
// 32 bit random number from 0 to 16 to 16
value
value == $urandom_range(1,9);
$urandom_range(1,9); //
// 32
32 bit
bit random
random number
number from
from 11 to
to 99
If all you want is a random integer value, you can use the new $urandom() system function to
obtain a random unsigned 32-bit integer, or the new $urandom_range() system function to
constrain the value to a range of values. These functions differ from the Verilog $random()
function, in that they return unsigned values rather than signed values, and that the random
sequences are independent of each other.
--------
function int unsigned $urandom [ (int seed) ] ;
function int unsigned $urandom_range(int unsigned maxval, int
unsigned minval=0);
You can randomize only singular integral variables. For example, you cannot randomize unpacked
structures, real variables, or any nets.
--------
[std::]randomize ( [variable_identifier_list] ) [ with
{constraint_expression [; constraint_expression] } ] ;
Random Stability
Each design unit instance, class instance and process thread has its own
independent Random Number Generator (RNG)
Each package and instance of interface, module or program RNG is
seeded with the implementation-dependent initial seed (usually 1)
The RNG of each static thread or class object is upon creation seeded
with the next random value of its parent design unit’s RNG
The RNG of each dynamic thread or class object is upon creation
seeded with the next random value of its parent thread’s RNG
The RNG of thread and class objects are stable to the extent that their
creation order and randomization order is stable
Add new threads and class objects and randomization calls after previous
threads and class objects and randomization calls or explicitly initialize the
RNGs
Each design unit instance, class instance and process thread has its own independent Random
Number Generator (RNG).
Each package and instance of interface, module or program RNG is seeded with the
implementation-dependent initial seed, usually 1.
The RNG of each static thread or class object is upon creation seeded with the next random
value of its parent design unit’s RNG.
The RNG of each dynamic thread or class object is upon creation seeded with the next random
value of its parent thread’s RNG.
The RNG of thread and class objects are stable to the extent that their creation order and
randomization order is stable.
• To maintain random stability, add new threads and class objects and randomization calls
after previous threads and class objects and randomization calls, or explicitly initialize the
RNGs.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.13
initial
initial
self() is a static function of //set
the built-in process class that
//set aa seed
seed at
at the
the start
start
returns the process handle
process::self.srandom(100);
process::self.srandom(100);
aa == randomize(x);
randomize(x);
...
...
end
end
This example uses the self() static function of the process class to obtain a process handle
“pointing” to itself, and through that handle calls its srandom() function to set its random seed.
A SystemVerilog process is a class, but severely restricted. A process is either static, created with
the always or initial keywords; or dynamic, created with the fork keyword. You cannot instantiate a
process, or create it on-the-fly with the new keyword. In fact, you can do very few class-like things
with a process.
--------
class process;
enum state {FINISHED, RUNNING, WAITING, SUSPENDED, KILLED};
static function process self();
function state status();
function void kill();
task await();
function void suspend();
task resume();
endclass
The initial seed of each process’ RNG is the next random value of its parent RNG. This allows you
to declare static processes and spawn dynamic processes after all previous processes have been
declared or spawned, without disturbing the RNG of previous processes. This also allows you to
shuffle entire hierarchies around if you use the srandom() void function to manually set the initial
seed of the hierarchy root.
This example illustrates two important features:
Hierarchical seeding – the RNG of each forked thread is seeded from the next random value of
the parent’s thread RNG.
Thread stability – the sequence of values for x, y and z are dependent upon their order in the
hierarchy, but not dependent on the order of process execution.
Constraint Blocks
Use the with clause to attach a constraint block {…} containing:
Constraint expressions
Constraint expression ordering
//type
//type declarations
declarations as
as before
before
opcode_t
opcode_t opcode;
opcode;
regs_t
regs_t regs;
regs;
logic[7:0]
logic[7:0] data;
data;
int
int ok;
ok;
//
// conditions:
conditions: data
data only
only between
between 32
32 and
and 126
126
ok
ok == randomize(data)
randomize(data) with
with {data>=32;
{data>=32; data<=126;};
data<=126;};
//
// range:
range: opcode
opcode in
in range
range of
of ADDI
ADDI to
to SUBI
SUBI or
or JMP
JMP to
to JMPC
JMPC
ok
ok == randomize(opcode)
randomize(opcode) with { opcode
with { opcode inside {[ADDI:SUBI],[JMP:JMPC]};};
inside {[ADDI:SUBI],[JMP:JMPC]};};
//
// distribution:
distribution: REG0-REG1
REG0-REG1 twice
twice as
as likely
likely as
as REG2-REG3
REG2-REG3
ok
ok = randomize(regs) with { regs dist { [REG0:REG1]:=2, [REG2:REG3]:=1};};
= randomize(regs) with { regs dist { [REG0:REG1]:=2, [REG2:REG3]:=1};};
A constraint block can contain constraint expressions and statements defining an order in which to
solve the constraints. The constraint expression can be almost any integral expression. It cannot
have side-effects, so you cannot use assignment operators in a constraint expression. The functions
you call in a constraint expression are further restricted. Refer to the LRM section 13.4.11 for those
restrictions.
--------
constraint_block_item ::=
solve identifier_list before identifier_list ;
| constraint_expression
Conditional Constraints
Constraint expressions use two constructs for conditional constraints:
implication: expression -> constraint_set
if (expression) constraint_set [else constraint_set]
logic[7:0]
logic[7:0] data;
data;
...
...
//
// Randomize
Randomize data
data with
with values
values << 100
100 when
when mode
mode is
is small
small
//
// Randomize
Randomize data
data with
with values
values >> 200
200 when
when mode
mode is
is large
large
// Don’t constrain data if mode is not small or
// Don’t constrain data if mode is not small or large large
ok
ok == randomize(data)
randomize(data) with
with
{mode
{mode == small
== small ->
-> data
data << 100;
100;
mode
mode == large -> data >> 200;};
== large -> data 200;};
//
// Payload
Payload can
can be
be constrained
constrained in
in the
the same
same way
way with
with if/else
if/else
ok
ok == randomize(data) with
randomize(data) with
{{ if
if (mode
(mode ==
== small)
small) data
data << 100;
100; else
else
if
if (mode == large) data > 200; };
(mode == large) data > 200; };
It is very common to have the constraints upon a variable dependent upon the value of another
variable. You can express this dependency using the SystemVerilog implication operator (->)
and/or the if…else construct. Nothing about these constructs is specific to constraints. We mention
them here because you will almost always use them in constraint blocks of any significant
complexity.
--------
constraint_expression ::=
expression_or_dist ;
| expression –> constraint_set
| if ( expression ) constraint_set [ else constraint_set ]
| foreach ( array_identifier [ loop_variables ] ) constraint_set
for
for (int
(int i=0;
i=0; i<50;
i<50; i++)
i++) for
for (int
(int i=0;
i=0; i<50;
i<50; i++)
i++)
begin
begin begin
begin
randcase
randcase randcase
randcase
20
20 :: gen_atm;
gen_atm; aa :: gen_atm;
gen_atm;
30 : gen_ethernet;
30 : gen_ethernet; aa ++ bb :: gen_ethernet;
gen_ethernet;
10
10 :: gen_ipv4;
gen_ipv4; aa -- bb :: gen_ipv4;
gen_ipv4;
55 :: gen_crc_error;
gen_crc_error; bb :: gen_crc_error;
gen_crc_error;
endcase
endcase endcase
endcase
Probability(gen_crc_error) = 5/65 ~ 8%
You can use the randcase keyword to randomly select a statement for execution.
You can provide different probabilities, that is, weights, for each branch.
The randcase weights are non-negative integral expressions that may be constant or variable. An
item that you do not weight assumes the weight of 1. A weight of 0 indicates that the item shall
never be selected.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.15
You can use the randsequence keyword to execute a random sequence of statements based on rules
called a production list. The argument to the randsequence construct is the identifier of the
production to start with. This defaults to the first listed production.
Each production consists of a function declaration, with optional argument and return type
declarations, followed by a colon, followed by rules for the production, where rules are separated
by the bitwise OR (|) operator.
Each rule is a production list of one or more productions, and may have a weight associated with it,
and may have a code block associated with it.
A code block is data declarations and statements between curley brackets ({}).
Each randsequence construct creates its own automatic scope, which means that production
declarations are not visible outside of the randsequence construct. Furthermore, each code block
creates an anonymous automatic scope, so its declarations are also not visible outside the code
block.
The sequence in this example starts with the main production, which defines one production rule
consisting of the arithmetic (arith), stack (stk), and last productions, in sequence. The arithmetic
production defines the increment (inc) and decrement (dec) alternative rules, each equally likely to
be selected. The stack production defines the push and pop alternative rules, each also equally
likely to be selected.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.16.
randsequence
randsequence (( main
main ))
inc is twice as
main
main :: arith
arith stk
stk last;
last;
likely as dec
arith
arith :: inc:=2
inc:=2 || dec:=1;
dec:=1;
stk
stk :: push:=3
push:=3 || pop;
pop;
inc
inc :: { do_inc(); };
{ do_inc(); }; push is thrice
dec
dec : { do_dec();
: { do_dec(); }; }; as likely as pop
push
push :: {{ do_push();
do_push(); };};
pop
pop : { do_pop();
: { do_pop(); }; };
last
last :: {{ do_check();
do_check(); }; };
endsequence
endsequence
You can weight production alternatives. These are the relative weights of production rules within a
production list, and do not weight the production list itself relative to any other production list.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.16.1
randsequence
randsequence (( main
main )) very limited
main : constructs in
main : repeat(5) arith
repeat(5) arith stk
stk last;
last;
arith productions
arith :: ifif (incr)
(incr) inc;
inc; else
else dec;
dec;
stk
stk : case (which)
: case (which)
0:
0: push;
push;
1:
1: pop;
pop; less limited
endcase;
endcase; constructs in
inc
inc :: {{ do_inc();
do_inc(); }; }; code blocks
dec
dec : { do_dec();
: { do_dec(); }; };
push
push :: {{ do_push();
do_push(); }; };
pop
pop :: {{ do_pop();
do_pop(); }; };
last
last :: {{ do_check();
do_check(); }; };
endsequence
endsequence
A production list may contain a restricted set of procedural constructs. It may contain the case and
if...else constructs to make the production conditional, and may contain the repeat construct to
repeat the production a number of times.
Contrast this with code blocks, which may contain arbitrary procedural constructs.
i == 1 produces B B C
i == 2 produces A
Your code blocks can use the break keyword and the return keyword to abort productions. These
keywords have special semantics within a randsequence construct. Within a randsequence
construct, the break keyword terminates the entire randsequence production, and not just any loop
it happens to be in, and the return keyword terminates only the current production. As every
production declaration is a local function declaration, and every production item is a task call, the
code block of a production item can utilize those arguments and can return a value.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 13.16.2, 13.16.3
randsequence
randsequence (( main
main ))
main
main : arith($urandom)
: arith($urandom) {$display(arith);}
{$display(arith);} stk
stk last;
last;
int
int arith(int
arith(int i)i) :: inc(i)
inc(i) {return
{return inc;}
inc;} || dec(i)
dec(i) {return
{return dec;};
dec;};
stk
stk :: push
push || pop;
pop;
int
int inc(int
inc(int i)i) :: {{ return
return do_inc(i);
do_inc(i); };
};
int
int dec(int
dec(int i)i) :: {{ return
return do_dec(i);
do_dec(i); };
};
push
push : { do_push(); };
: { do_push(); };
pop
pop :: {{ do_pop();
do_pop(); };};
last
last :: {{ do_check();
do_check(); }; };
endsequence
endsequence
The syntax for declaring a production is similar to the syntax for declaring a function. However, a
production is not a function; the production code block can consume time.
The syntax for passing data to a production is similar to a task call.
A production for which you have declared a type can return data. You return the data in the return
statement. The data is available as the production item name after the production item “call” in the
“calling” production.
This example passes a random number to the arithmetic production and displays the returned value.
The arithmetic production further exchanges this value with either the increment or decrement
production. The increment and decrement productions each call a function to implement their
operation.
Summary
With these simple randomization features, you can:
Generate large amounts of stimulus data from compact code
Run longer simulations with more stimulus
that more thoroughly tests the design
Spend more of your own time crafting directed tests of corner cases
You can use these SystemVerilog randomization features to easily generate large amounts of
meaningful stimulus from very compact code, stimulus that more thoroughly tests the more easily
controlled and observed functionality of the design, while allowing you to spend your time crafting
directed tests to verify the less easily controlled and observed functionality of the design.
Pause here for a moment and review what you have learned about the simple SystemVerilog scope
randomization features.
Quiz
Define the output for each value of i:
randsequence()
randsequence()
top
top :: AA B;
B;
AA :: CC DD E;
E;
BB :: CC EE {{ if
if (i
(i ==== 11 )) return;
return; }} D;
D;
CC :: {$display("C"); if(i == 3) return;};
{$display("C"); if(i == 3) return;};
DD :: {if
{if (i(i ==
== 22 )) return;
return; $display("D");};
$display("D");};
EE :: {if
{if (i(i ==
== 00 )) break;
break; $display("E");};
$display("E");};
endsequence
endsequence
i == 0 produces ?
i == 1 produces ?
i == 2 produces ?
i == 3 produces ?
Solutions in Appendix A
About Lab 5
For this lab you randomize the data that the memory testbench writes to the
memory. You use scope variable randomization with an in-line constraint
block. Your constraint expressions will use “inside” expressions and
distributions.
Chapter 7
November 6, 2009
Topics
Randomization of class properties
In-line control
Constraints
Setting seeds
This chapter explores randomization of class hierarchies. For this, SystemVerilog provides
additional capabilities, such as the pre_randomize() and post_randomize() methods, enabling and
disabling randomization on a per-property basis, and enabling and disabling individual named
constraint blocks.
class
class RandClass;
RandClass;
rand
rand bit
bit [1:0]
[1:0] x;
x;
randc bit [1:0] y;
randc bit [1:0] y;
endclass
endclass
rand randc
x: Each valid value (00,01,10,11) y: Each valid value (00,01,10,11)
has an equal probability of being won’t repeat until all 4 values
chosen of 1/4. have been cycled through.
01 11 00 10 // Valid 01 11 00 10 // Valid
01 11 01 10 // Valid 01 11 01 10 // Error
The following are direct quotes of portions of IEEE Std. 1800-2005 section 13.3. These portions
are quoted here because there is no way to more concisely or better explain the rand and randc
keywords:
“The solver can randomize singular variables of any integral type.”
“Variables declared with the rand keyword are standard random variables. Their values are
uniformly distributed over their range.”
“Variables declared with the randc keyword are random-cyclic variables that cycle through all
the values in a random permutation of their declared range. Random-cyclic variables can only
be of type bit or enumerated types and can be limited to a maximum size.”
“To reduce memory requirements, implementations can impose a limit on the maximum size of
a randc variable, but it should be no less than 8 bits.”
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.3
module
module test;
test;
class
class RandClass;
RandClass; x is a random variable
rand
rand bit
bit [1:0]
[1:0] x;
x;
randc
randc bit
bit [1:0]
[1:0] y;
y; y is a random-cyclic variable
endclass
endclass
RandClass
RandClass myRand
myRand == new;
new; Randomizes all rand
and randc variables in
initial
initial class object
begin
begin
int
int success;
success;
success
success == myRand.randomize();
myRand.randomize();
end
end
endmodule
endmodule :: test
test
Every class has a built-in virtual member function called randomize(), that returns an int value of 1
to indicate successful randomization, or 0 to indicate randomization failure. You cannot redeclare
this function.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.5.1
function
function void
void pre_randomize;
pre_randomize;
if
if (super)
(super) super.pre_randomize();
super.pre_randomize();
.. .. ..
endfunction
endfunction
function
function void
void post_randomize;
post_randomize;
if
if (super)
(super) super.post_randomize();
super.post_randomize();
.. .. ..
endfunction
endfunction
The randomize() function of the class automatically calls the void pre_randomize() and
post_randomize() functions of the class. You can redeclare these functions to do whatever you
want to do before and after the class object is randomized.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.5.2
Application of pre_randomize()
class
class RandClass;
RandClass;
rand
rand bit
bit [1:0]
[1:0] x;
x;
randc Overrides built-in
randc bit
bit [1:0]
[1:0] y;
y; pre_randomize()
function void pre_randomize();
function void pre_randomize();
$display("x
$display("x was
was ",x,"
",x," yy was
was ",y);
",y);
endfunction
endfunction
endclass
endclass
RandClass
RandClass myRand
myRand == new;
new;
randomize()
initial
initial automatically calls
begin
begin pre_randomize()
int
int success;
success; before randomizing
success variables
success == myRand.randomize();
myRand.randomize();
$display("x
$display("x now
now ",
", myRand.x,,
myRand.x,,
"y
"y now
now ",
", myRand.y);
myRand.y);
end
end
In this example, the class declaration reimplements the pre_randomize() function to display the
values of its variables just before they are assigned their next random value.
class
class RandClass;
RandClass;
rand x, y are random variables
rand bit
bit [0:1]
[0:1] x;
x; //random
//random variables
variables
randc bit [1:0]
randc bit [1:0] y;
y;
bit
bit [1:0]
[1:0] a,
a, b;
b; //
// state
state variables
variables a, b are not random variables
endclass
endclass
RandClass
RandClass myRand
myRand == new;
new; randomize x and y only.
a and b not randomized.
initial
initial
begin
begin
int
int success;
success;
randomize x only.
success
y, a, b not randomized.
success == myRand.randomize();
myRand.randomize();
success
success == myRand.randomize(x);
myRand.randomize(x); randomize x and a only.
success = myRand.randomize(x,a);
success = myRand.randomize(x,a); y and b not randomized.
success
success == myRand.randomize(a,b);
myRand.randomize(a,b);
end
end
randomize a and b only.
x and y not randomized.
Providing variable reference arguments to the randomize() function randomizes only those
variables, regardless of whether you declare the variables rand or randc. Randomization of state,
that is, non-random, variables, is performed in the normal fashion, that is, not random-cyclic.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.6
Application of rand_mode()
class
class RandClass;
RandClass; x and y are
rand
rand bit
bit [0:1]
[0:1] x;
x; //random
//random variables
variables random vars
randc
randc bit
bit [1:0]
[1:0] y;
y;
bit [1:0]
bit [1:0] a,
a, b;
b; //
// state
state variables
variables
endclass
endclass
disable randomization of
RandClass
RandClass myRand
myRand == new;
new; all random variables of
class object
initial
initial
begin
begin re-enable y
int
int success,
success, state;
state; randomization
myRand.rand_mode(0);
myRand.rand_mode(0);
myRand.y.rand_mode(1);
myRand.y.rand_mode(1); y randomization
state
state == myRand.y.rand_mode();
myRand.y.rand_mode(); is enabled
success=myRand.randomize();
success=myRand.randomize();
state
state == myRand.a.rand_mode();
myRand.a.rand_mode(); only y randomized
end
end
error – a is not a
random variable
In this example, the class handle variable is not a class member variable, so cannot be declared
rand. rand and randc apply only to class member variables. After disabling randomization of all
random properties of the class object, the example re-enables randomization of one of its members.
Subsequent randomization of the class object then randomizes only the one member variable that is
still active.
Constraint Blocks
Constraints restrict the random data generation to meaningful values
A constraint expression can be almost any integral expression
Cannot have side effects – e.g. no assignment operators
class
class RandClass;
RandClass;
rand
rand bit
bit [1:0]
[1:0] x;
x; Constrains x to the
randc bit [1:0]
randc bit [1:0] y;
y; values 01,10,11
constraint
constraint c1
c1 {{ xx !=
!= 2’b00;}
2’b00;}
endclass
endclass
; after constraint
expression but not
RandClass
RandClass myRand
myRand == new;
new; after constraint line
initial
initial
begin
begin Randomize x using
int
int success;
success; constraint block c1
success
success == myRand.randomize();
myRand.randomize();
$display("x
$display("x now
now ",
", myRand.x,,
myRand.x,,
"y
"y now ", myRand.y);
now ", myRand.y);
end
end
You declare a constraint as a class member with the constraint keyword, followed by an identifier,
followed by constraint block items enclosed within a set of curley ({}) braces. Constraint block
items can be constraint expressions and can be statements ordering the constraint solution.
Constraint expressions can be almost any integral expression. They are restricted to not have side
affects, so you cannot use the assignment operators. The use of functions is also restricted. For
these restrictions on functions please refer to section 13.4.11
--------
constraint_declaration ::=
[ static ] constraint constraint_identifier constraint_block
constraint_block ::=
{ { constraint_block_item } }
constraint_block_item ::=
constraint_expression
| solve identifier_list before identifier_list ;
class
class RandClass;
RandClass;
rand
rand bit
bit [1:0]
[1:0] x;
x;
randc c1 constrains x to not be 00
randc bit
bit [1:0]
[1:0] y;
y;
constraint
constraint c1
c1 {{ xx !=
!= 2’b00;}
2’b00;}
endclass
endclass
class
class RandClassX
RandClassX extends
extends RandClass;
constraint
RandClass; c2 constrains x to not be 11
constraint c2
c2 {{ xx !=
!= 2’b11;}
2’b11;}
endclass
endclass
RandClassX
object of extended class
RandClassX myRand
myRand == new;
new;
initial
initial
begin
begin
int
int success;
success; x will be 01 or 10
success
success == myRand.randomize();
myRand.randomize();
$display("x
$display("x now
now ",
", myRand.x,,
myRand.x,,
"y
"y now ", myRand.y);
now ", myRand.y);
end
end
Constraints are class members and are inherited along with all other inherited class members.
This example declares a class type with random variables and constraints on those random
variables, then extends the class to add additional constraints. Objects of the extended class type
utilize both sets of constraints.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.4.2
class
class RandClass;
RandClass;
rand
rand int
int x;
x; c1 constrains x to
randc
randc bit
bit [1:0]
[1:0] y;y; the set 3,7,11-20
constraint
constraint c1 {x inside
c1 {x inside {3,
{3, 7,
7, [11:20]};}
[11:20]};}
endclass
endclass
RandClass
RandClass myRand
myRand == new;
new;
initial
initial
begin
begin
int
int success;
success;
success
success == myRand.randomize();
myRand.randomize();
$display("x
$display("x now
now ",
", myRand.x,,
myRand.x,,
"y now ", myRand.y);
"y now ", myRand.y);
end
end
A constraint expression using the inside operator constrains the value of the random variable to be
inside a list of one or more value ranges. A value range can be a single expression, or two
expressions enclosed by square brackets ([]) and separated by a colon (:).
--------
constraint_expression ::=
expression_or_dist ;
| expression –> constraint_set
| if ( expression ) constraint_set [ else constraint_set ]
| foreach ( array_identifier [ loop_variables ] ) constraint_set
class
class RandClass;
RandClass;
rand
rand int
int x;
x;
randc
randc bit [1:0]
bit [1:0] y;y;
constraint
constraint c1 c1 {x
{x dist
dist {7:=5,
{7:=5, [11:20]:=3,
[11:20]:=3, [26:30]:/1};}
[26:30]:/1};}
endclass
endclass
A constraint expression using the dist operator also constrains the value of the random variable to
be inside a list of one or more value ranges. The dist operator differs from the inside operator, in
that you can weight the value ranges. Using different weighting operators, you can apply the
weight expression either to every value in the range, or to the range as a whole. For any range that
consists of a single expression, either operator has the same effect. The default weight is 1.
--------
expression_or_dist ::= expression [ dist { dist_list } ]
dist_list ::= dist_item { , dist_item }
dist_item ::= value_range [ dist_weight ]
dist_weight ::= := expression | :/ expression
class
class RandClass1;
RandClass1; class
class RandClass2;
RandClass2;
rand
rand int
int x;
x; rand
rand int
int x;
x;
bit
bit mode;
mode; bit
bit mode;
mode;
constraint
constraint c1c1 constraint
constraint c2 c2
{{ {{
mode
mode ==
== 11 ->
-> xx << 100;
100; if
if (mode
(mode ==== 1)
1)
mode == 0 -> x > 10000;
mode == 0 -> x > 10000; xx << 100;
100;
}} else
else
endclass
endclass xx >> 10000;
10000;
}}
endclass
endclass
It is very common to have the constraints upon a variable dependent upon the value of another
variable. You can express this dependency using the SystemVerilog implication operator (->)
and/or the if…else construct. Nothing about these constructs is specific to constraints. We mention
them here because you will almost always use them in constraint blocks of any significant
complexity.
--------
expression –> constraint_set
if ( expression ) constraint_set [ else constraint_set ]
class
class RandClass2;
RandClass2;
rand
rand int
int A[];
A[];
constraint
constraint c1
c1 {{ foreach
foreach (A[i])
(A[i]) (i
(i <=
<= A.size()/2)
A.size()/2) ->
-> A[i]
A[i] <=
<= i;
i; }}
constraint
constraint c2
c2 {{ foreach
foreach (A[i])
(A[i]) (i
(i >> A.size()/2)
A.size()/2) ->
-> A[i]
A[i] >=
>= i;
i; }}
endclass
endclass
You can use a loop variable to “customize” each array element’s constraint. The foreach construct
iterates over the elements of an array. This is especially useful for an array for which you do not
know the size. Of course you can always just use the size() method to obtain the size.
--------
foreach ( array_identifier [ loop_variables ] ) constraint_set
Class variables declared rand are evaluated simultaneously, and then constraints are applied to
select the valid subset of all possible combinations. This can cause almost all candidate solutions to
be discarded. For example, solving for a bit and an int gives 233 different combinations. Only one
of those satisfies the constraint that the int must be 0 if the bit is 1.
You can control the solution order by using the solve…before… statement. If you solve the bit
constraint first, the int will be unconstrained half the time. The other half of the time, the solver
needs to try only 232 combinations, at worst, and intelligent implementations will recognize that
there really isn’t any more solving to do.
Keep in mind the following restrictions:
You cannot order randc variables.
• The solver solves randc variables first, then ordered rand variables, then unordered rand
variables.
You can order only rand variables of integral types.
You must avoid circular dependencies (e.g {solve a before b; solve b before c; solve c before
a;})
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.4.9
You can disable and re-enable all constraints of the class object, or just individual constraints.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.8
Application of constraint_mode()
class
class RandClass;
RandClass; blue constrains x to not 00
rand
rand bit
bit [0:1]
[0:1] x;
x;
constraint
constraint blue {x
blue {x !=
!= 2’b00;}
2’b00;}
constraint
constraint green
green {x{x !=
!= 2’b11;}
2’b11;}
endclass
endclass green constrains x to not 11
RandClass
RandClass myRand
myRand == new;
new;
This example declares two constraint block members of the class. It disables all constraints of the
class object, then enables just one of the constraints of the class object. The other constraint
remains disabled.
this.srandom(seed) RandClass
RandClass myRand
myRand == new(7);
new(7);
or just srandom(seed)
initial
initial
Use to get different random begin
begin
sequences with each run
int
int success;
success;
success
success == myRand.randomize();
myRand.randomize();
Use to preserve random sequences myRand.srandom(9);
myRand.srandom(9);
between hierarchy changes success
success == myRand.randomize();
myRand.randomize();
end
end
This example passes an initial random seed into the class object’s constructor. The constructor calls
the object’s srandom() method to set the random seed, overriding the initial random seed obtained
from its parent module. The test process later changes the object’s seed again, this time calling the
srandom() method through the class variable.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 13.12.3
Summary
With these class randomization features, you can:
Generate large amounts of stimulus data from compact code
Run longer simulations with more stimulus
that more thoroughly tests the design
Spend more of your own time crafting directed tests of corner cases
Pause here for a moment and review what you have learned about the SystemVerilog class
randomization features.
Quiz
The randomize call randomizes which variables?
class ClassC;
rand int intC;
real realC;
rand ClassB objB = new;
endclass
Solutions in Appendix A
About Lab 6
For this lab you randomize the data that the memory testbench writes to the
memory. You use class property randomization with an in-class constraint
block. Your constraint expressions will use “inside” expressions and
distributions. You will weight the distribution of data values and solve for
data values before solving for address values.
Covergroup Coverage
Chapter 8
November 6, 2009
Topics
Introduction to functional coverage
Simple coverage
Cross products
Coverage options
Code coverage automatically measures whether, and potentially how many times, a line, statement,
block or branch has executed. More advanced code coverage can measure whether, and potentially
how many times, an expression term has a chance to control the outcome of the expression. Code
coverage does not automatically check transitions between design states, as the transition space is
extremely large
That sort of check may be important. You, as the designer, may know that two consecutive
SUBTRACT operations followed by a DIVIDE operation has caused an overflow error on previous
designs similar to yours. This is where user-directed functional coverage becomes imperative.
Functional coverage, as the name suggests, checks whether the test exercises the functionality of
the design. You identify the critical combinations and sequences that should be exercised to verify
the design functionality.
covergroup
covergroup cg cg @(posedge
@(posedge clk);
clk); property
property req_gets_gnt;
req_gets_gnt;
Addr:
Addr: coverpoint addr
coverpoint addr @(posedge
@(posedge clk)
clk)
{{ bins
bins low
low == {{ [0:’h0F],
[0:’h0F], 1919 };
}; req
req |-> ##[1:$] gnt;
|-> ##[1:$] gnt;
bins
bins mid[] = { 16, 17, 18 };
mid[] = { 16, 17, 18 }; endproperty
endproperty
bins
bins high
high == {{ [’h14:’hFF]
[’h14:’hFF] };}; }} cover
cover property
property (req_gets_gnt);
(req_gets_gnt);
AddrXvalid : cross Addr,
AddrXvalid : cross Addr, valid;valid;
endgroup
endgroup
cg
cg cg1
cg1 == new;
new;
The simulator measures whether (and optionally how many times) the
test has caused the design variables of interest to hit or sequence
through those scenarios
It is purely a testbench construct – no concern about synthesis!
Working with the design specification, you develop a test plan highlighting those data values,
combinations of values, and sequences of values, whose occurrence provides an indication of how
thoroughly the test exercises the design. You then translate those test plan items into the
SystemVerilog data-oriented functional coverage syntax. The simulator keeps these counts and
saves them in a database for an analysis tool to present to you.
Automatic Bins
The simulator tracks occurrences
of coverpoint values – on each module
module example;
example;
sample clock it increments the logic
logic clk;
clk;
associated “bin”. By default: logic
logic [2:0] opcode;
[2:0] opcode;
logic [7:0] address;
One for each coverpoint value logic [7:0] address;
Up to a preset limit covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
clk);
Named auto[<value>] c1: coverpoint opcode;
c1: coverpoint opcode;
c2:
c2: coverpoint
coverpoint address;
address;
endgroup : cg1
endgroup : cg1
cg1
cg1 cover_inst
cover_inst == new;
new;
...
...
endmodule
endmodule
SystemVerilog by default automatically creates a single bin for every value in the coverpoint
variable range. These are called automatic, or implicit, bins. For an enumerated coverpoint, there is
one bin for each valid value. For an integral coverpoint variable, the number of automatic bins is at
most 2M where M is the number of bits required to represent the variable. When 2M is greater than
the preset limit, the values are distributed as evenly as possible, with the last bin getting any extra
values. Later slides show how to define bins and how to change the preset limit for automatic bin
creation.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.4.2
Explicit Bins
You can define the bins yourself:
To track only a subset of c1:
c1: coverpoint
coverpoint var1
var1 {{
values bins
bins VV == {1,
{1, 2,
2, 5}
5} ;;
To control what values }}
increment which bin
bin V increments for var1 = 1, 2 or 5
bin name
{{ [0:5],
[0:5], 10
10 }} -- values
values 0-5
0-5 and
and 10
10
{{ [0:5],
[0:5], [9:14]
[9:14] }} –– values 0-5 and 9-14
values 0-5 and 9-14
list of value ranges {{ ’h1,
’h1, ’h2,
’h2, ’hF
’hF }} –– values
values 1,
1, 2,
2, 15
15
{{ [1:9], [7:12]
[1:9], [7:12] }} –– range
range overlap allowed
overlap allowed
a range can be a single value {{ [16:$]
[16:$] }} -- range
range 16
16 to
to max
max value
value
occurrence of listed value
increments bin
You can explicitly declare bins enclosed in curley braces ({}) immediately after the coverpoint
identifier. Note that if you do not explicitly specify coverpoint bins or options, you terminate the
coverpoint declaration with a semicolon, but if you do explicitly specify coverpoint bins or options,
you instead terminate each bin or option specification itself with a semicolon.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.4
A scalar bin is one bin that counts occurrences of any of the values in its open range list.
A vector bin is an array of bins, by default one array element for each unique value in the open
range list.
logic
logic [3:0]
[3:0] var1;
var1;
ce:
ce: coverpoint
coverpoint var1
var1 {{
illegal_bins
illegal_bins aa == {{ 0,0, 15
15 };
}; //
// 11 bin
bin for
for illegal
illegal values
values
ignore_bins b = { [13:15]
ignore_bins b = { [13:15] }; }; //
// 11 bin for ignored values
bin for ignored values
bins
bins cc == {{ 2,
2, 33 };
}; //
// 11 bin
bin for
for 2,
2, 33
bins
bins d[2]
d[2] == {{ [9:11],
[9:11], 9,
9, [12:15]
[12:15] };
}; //
// 22 bins
bins - d[0] == {9,10,11,9}
- d[0] {9,10,11,9}
//
// -- d[1]
d[1] == {12}
{12}
bins e[] = { [0:2], 2,
bins e[] = { [0:2], 2, 6 }; 6 }; //
// 33 bins
bins e[1], e[2], e[6]
e[1], e[2], e[6]
bins f
bins f = default;
= default; //
// 11 bin for 4,5,7,8
bin for 4,5,7,8
}}
153 CADENCE CONFIDENTIAL
11/6/2009 SystemVerilog for Verification 153
a) The illegal_bins keyword specifies bins for illegal values. The simulator does not cover these
values even if they appear in other bins, and issues an error upon occurrence of the illegal
values.
b) The ignore_bins keyword specifies bins for ignored values. The simulator does not cover these
values even if they appear in other bins, but does not issue an error upon their occurrence
unless they are also illegal values.
c) A scaler bin is one bin for all the specified values.
d) For a vector bin of a specified size, the values are distributed as evenly as possible by their
appearance in the open range list, with the last bin getting any extra values. Duplicate values
are retained, so can show up in multiple bins. Illegal and ignored values are removed after the
values are distributed.
e) For a vector bin of an unspecified size, the values are distributed each to their own bin. The
standard implies this to mean unique values, with duplicate values not retained. Illegal and
ignored values are removed after the values are distributed. Here, that results in an empty bin
(e[0]), which is removed.
f) The default keyword specifies bins for values that do not appear in any other bins. You cannot
use the illegal_bins or ignore_bins keywords with the default keyword.
module
module example_with_bins;
example_with_bins;
logic
logic clk;
clk;
logic
logic [2:0]
[2:0] opcode;
opcode;
logic
logic [15:0]
[15:0] address;
address; How many bins in
? this covergroup?
covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
Question What are their names?
clk);
c1:
c1: coverpoint
coverpoint opcode;
opcode; Answers in appendix A
c2:
c2: coverpoint
coverpoint address
address {{
bins
bins low[]
low[] == {{ [0:’h0F]
[0:’h0F] }} ;;
bins
bins high = { [’h10:’hFF] }} ;;
high = { [’h10:’hFF]
}}
endgroup
endgroup :: cg1
cg1
cg1
cg1 cover_inst
cover_inst == new();
new();
...
...
endmodule
endmodule
Cross Coverage
You can track cross-products of:
Coverpoints within the covergroup
logic
logic [1:0]
[1:0] veca,
veca, vecb;
vecb;
...
...
Other scope variables covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
clk);
Creates implicit coverpoint c1: coverpoint veca;
c1: coverpoint veca;
Participates in cover cross c2:
c2: coverpoint
coverpoint vecb;
vecb;
crossab:
crossab: cross c1, c2;
cross c1,
No coverage data reported for c2;
endgroup : cg1
variable endgroup : cg1
c1.auto[0]
c1.auto[1]
c1.auto[2]
c1.auto[3]
Use the cross keyword, and provide a:
cross name (optional)
list of coverpoints and/or variables c2.auto[0]
c2.auto[1]
c2.auto[2]
c2.auto[3]
You can declare cover crosses of two or more already declared coverpoints within the covergroup
and/or other integral variables. SystemVerilog automatically creates “virtual” coverpoints for cover
cross variables for which you did not declare a coverpoint. It does not report coverage data for
these “virtual” coverpoints.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.5
reg
reg [1:0]
[1:0] a;
a;
reg [3:0]
reg [3:0] b;b;
reg
reg c;
c;
covergroup
covergroup cgcg @(posedge
@(posedge clk);
clk);
bcp:
bcp: coverpoint
coverpoint bb {{
bins
bins b1
b1 == {{ [9:12]
[9:12] };
}; //one
//one bin
bin b1
b1
bins
bins b2[]
b2[] == {{ [13:15]
[13:15] };
}; //3
//3 bins:
bins: b2[13],
b2[13], b2[14],
b2[14], b2[15]
b2[15]
bins restofb[] = default;
bins restofb[] = default; //9
//9 bins:
bins: [0]
[0] ... [8] not in cross
... [8] not in cross
}}
ccp:
ccp: coverpoint
coverpoint c; c; //
// two
two automatic
automatic bins
bins
AxBxC:
AxBxC: cross a, bcp, ccp;
cross a, bcp, ccp; //
// 32
32 bins = a(4) xx bcp(4)
bins = a(4) bcp(4) xx ccp(2)
ccp(2)
endgroup
endgroup :: cg
cg
This example declares a cover cross of one variable and two previously declared coverpoints:
The variable has four possible values for which four “virtual” bins are automatically created.
The first declared coverpoint has one explicit scalar bin, and one explicit vector bin containing
three elements, for a total of four bins.
The second coverpoint has two possible values for which two bins are automatically created.
The cover cross thus has 32 automatic bins, one for each combination of coverpoint bins that make
up the cross.
Pause here and examine this cross coverage example.
endgroup
endgroup :: cgcg
x1 = <cp2.mid,cp1.A[0], cp2.mid,cp1.A[1],
cp2.high cp2.mid,cp1.A[2], cp2.mid,cp1.A[3]>
cp2.mid x1 x2 = <cp2.low,cp1.A[2], cp2.mid,cp1.A[2]>
cp2.low x2
SystemVerilog by default automatically creates a single bin for every product of the cover cross. It
does not include any default coverpoint bins or any declared as illegal or to ignore.
You can explicitly declare cover cross bins enclosed in curley braces ({}) immediately after the list
of coverpoints to cross. As with coverpoint bins, if you do not explicitly specify cover cross bins or
options, you terminate the cover cross declaration with a semicolon, but if you do explicitly specify
cover cross bins or options, you instead terminate each bin or option specification itself with a
semicolon.
You do not specify an open value range for a cover cross bin. Instead you use the binsof keyword
to select those coverpoint bins that should participate in the cross, optionally filtered to an open
value range with the intersect keyword. You can then perform conjunction, disjunction and
negation operations on the resulting bin selections.
The example declares the cover cross bin “x1” to include the bins of coverpoint “cp2” that contain
the values 8 through 10, so only the “mid” bin of coverpoint “cp2” participates in the “x1” cover
cross bin. The example declares the cover cross bin “x2” to include the “A[2]” bin of coverpoint
“cp1” conjoined with the negation of the “high” bin of coverpoint “cp2”.
Pause here and examine this selection of cross coverage bins.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.5
ignore_bins x4 = binsof(cp1.A[1]);}
ignore_bins x4 = binsof(cp1.A[1]);}
endgroup
endgroup :: cg
cg
x4
As with coverpoint bins, you can declare cover cross bins to be illegal or to be ignored. Coverpoint
bins that participate in a cover cross bin to be ignored are removed from any other cover cross bin
where they may otherwise participate. Coverpoint bins that participate in an illegal cover cross bin
are also removed from any other cover cross bin where they may otherwise participate.
Furthermore, the simulator must issue an error upon the occurrence of any such illegal cross-
product.
This example declares the cross coverage bin “x3” to exclude the bins of coverpoint “cp2” that
contain the values 0 through 2, and the cross coverage ignore bin “x4” that removes from all cross
bins the bins of coverpoint “cp1.A[1]”.
Pause here for a moment to ensure you understand the effect of ignore and illegal bins.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 18.5.2, 18.5.3
Covergroup Options
You can specify options to control the behavior
of covergroups, coverpoints and cover crosses. type option
int a, b;
Each of these constructs has two built-in int a, b;
covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
clk);
structures: c1: coverpoint a {
c1: coverpoint a {
Type-specific (static) options type_option.comment
type_option.comment == "a";}
"a";}
c2: coverpoint b;
c2: coverpoint b;
struct {) endgroup
endgroup :: cg1
cg1
...
} type_option; cg1::type_option.comment
cg1::type_option.comment == "ab";
"ab";
cg1 one = new;
cg1 one = new;
Instance-specific options
instance option
struct { int a, b;
int a, b;
... covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
clk);
} option; c1: coverpoint a
c1: coverpoint a {{
option.auto_bin_max
option.auto_bin_max == 10;}
10;}
c2: coverpoint b;
c2: coverpoint b;
See field definitions on following slides… endgroup
endgroup :: cg1
cg1
cg1
cg1 one
one == new;
new;
one.c2.option.auto_bin_max
one.c2.option.auto_bin_max == 256;
256;
11/6/2009 SystemVerilog for Verification 159
The built-in covergroup base class declares for the covergroup, coverpoint, and cover cross, static
structures named “type_option” with options that you set for the covergroup type, and automatic
structures named “option” with options that you can modify for each covergroup instance. Within
the covergroup declaration, you can reference these fields hierarchically as you would for any class
member structure. You can later procedurally set most instance-specific options for each individual
instance of the covergroup.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.6
You set type-specific options only within the covergroup definition. Type-specific weight, goal and
comment options you set at the covergroup level do not affect the values set at the coverpoint or
cover cross level. You can also set weight, goal and comment options as instance-specific options.
The next slide describes the instance-specific options.
Set the weight field to modify the relative weight of the coverpoint or cover cross when
calculating the enclosing covergroup coverage metric, and the relative weight of the
covergroup when calculating the overall coverage metric, for a type-based coverage report.
Set the goal field to modify the target goal for the element. The standard does not state how the
vendor should use this field.
Set the comment field to provide a string you want included in the coverage report.
Set the strobe field to defer sampling to the end of the time slot, to where the $monitor and
$strobe system tasks execute.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.9
You can set instance-specific options within a covergroup definition, and except for the per_instance option, can also
set them for each individual instance of the covergroup. Instance-specific options other than weight, goal and comment
that you set at the covergroup level provide new default values for coverpoint and cover cross options for which you do
not set a value.
Set the name field to provide a name for the covergroup instance. The simulator generates a unique name for each
covergroup instance for which you do not supply a name.
Set the weight field to modify the relative weight of the coverpoint or cover cross when calculating the enclosing
covergroup coverage metric, and the relative weight of the covergroup when calculating the overall coverage
metric, for an instance-based coverage report.
Set the goal field to modify the target goal for the element. The standard does not state how the vendor should use
this field.
Set the comment field to provide a string you want included in the coverage report.
Set the at_least field to modify the hit count for considering a bin covered.
Set the auto_bin_max field to modify the maximum number of bins to be automatically created.
Set the cross_num_print_missing field to modify the number of uncovered cover cross bins that must be saved to
the coverage database and printed in the coverage report.
Set the detect_overlap field to have the simulator issue a warning when the values of two coverpoint bins overlap.
Set the per_instance field to track coverage data for each covergroup instance as well as each covergroup type.
You may want to track data on a per-instance basis if you differently parameterize each covergroup instance.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.9
These are methods of the covergroup base class that you call for specific covergroup instances. The
get_coverage() method is a static method that you can also call for the covergroup type using the
scope resolution operator.
Use the sample() method to force an immediate sample.
Use the get_coverage() method to obtain the current percent coverage for all instances of the
covergroup type. If you provide the optional reference arguments, the simulator places the
covered bin count and total bin count in the referenced variables.
Use the get_inst_coverage() method to obtain the current percent coverage for only the one
specific instance of the covergroup type.
Use the set_inst_name() method to provide a new instance name for the covergroup instance.
Use the start() and stop() methods to start and stop collecting coverage data for the specific
covergroup instance.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.7
These are system tasks and functions that you can use to store and retrieve coverage information.
With these routines you can display only the overall cumulative coverage calculated by covergroup
type. As these routines are less than optimally useful, vendors typically provide proprietary means
for managing coverage data.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 18.8
Summary
SystemVerilog data-oriented functional coverage with covergroups offers:
Specification of the sampling event
Specification of variables to sample
Automatic and explicit binning of value counts
Explicit scalar and vector bins
Explicit bins for values and for transitions between values
Explicit (default) bins for unspecified values, transitions
Explicit bins, ignore_bins, and illegal_bins
Specification of cross-products
Automatic and explicit binning of cross counts
Filtering and combining cross-product bins
Pause here for a moment and review what you have learned about SystemVerilog data-oriented
functional coverage.
Quiz
bit
bit [7:0]
[7:0] avec,
avec, bvec;
bvec;
covergroup
covergroup cg cg @(posedge
@(posedge clk);
clk);
cpa:
cpa: coverpoint
coverpoint avec
avec
{{ bins
bins a1
a1 == {{ [0:63]
[0:63] };
}; How many bins in this
bins
bins a2 = { [64:127] };
a2 = { [64:127] }; ? covergroup cross c?
bins a3 = { [128:191]
bins a3 = { [128:191] }; };
Question What are their names?
bins
bins a4
a4 == {{ [192:255]
[192:255] };
}; }} Solutions in Appendix A
cpb:
cpb: coverpoint
coverpoint bvec
bvec
{{ bins
bins b1
b1 == {0};
{0};
bins
bins b2 == {{ [1:84]
b2 [1:84] };
};
bins
bins b3
b3 == {{ [85:169]
[85:169] };
};
bins
bins b4 = { [170:255] };
b4 = { [170:255] }; }}
cc :: cross
cross cpa,
cpa, cpb
cpb
{{ bins
bins c1 = ! binsof(cpa)
c1 = ! binsof(cpa) intersect
intersect {[100:200]};
{[100:200]};
bins
bins c2 = binsof(cpa.a2) ||
c2 = binsof(cpa.a2) || binsof(cpb.b2);
binsof(cpb.b2);
bins
bins c3
c3 == binsof(cpa.a1)
binsof(cpa.a1) &&&& binsof(cpb.b4);
binsof(cpb.b4); }}
endgroup
endgroup
About Lab 7
For lab 7 you collect and analyze data-oriented functional coverage of the
memory address and data. You declare and instantiate a covergroup
containing a coverpoint for the address and a coverpoint for the data and a
cover cross of the coverpoint bins. You run the simulation and examine the
resulting coverage data.
November 6, 2009
Topics
Dynamic arrays
Associative arrays
Queues
This chapter explores the SystemVerilog dynamic and associative arrays, and queues.
Dynamic Arrays
Use a dynamic array when the array size must change during the simulation
Declare a dynamic array by leaving an unpacked dimension unsized
Use the new[] operator to allocate and initialize (construct) the array
new[size] – allocates array and initializes to default initial values
new[size](array) – allocates array and initializes from existing array of the same
type (e.g. itself) when setting or changing the array size
Dynamic arrays have the size() and delete() methods
function void delete() // delete all elements (sets size to 0)
logic
logic [7:0]
[7:0] dynarr[];
dynarr[]; //
// dynamic
dynamic array
array of
of 8-bit
8-bit vectors
vectors
int index;
int index;
...
...
dynarr
dynarr == new[8];
new[8]; //initialize
//initialize
for
for (int i=0; i<8; i++) dynarr[i] == i+1;
(int i=0; i<8; i++) dynarr[i] i+1;
index = dynarr.size();
index = dynarr.size(); // 8
// 8
dynarr
dynarr == new[16]
new[16] (dynarr);
(dynarr); //
// resize
resize array
array keeping
keeping values
values
index
index == dynarr.size();
dynarr.size(); //
// 16
16
dynarr.delete();
dynarr.delete(); //
// 00
Use a dynamic array when the array size changes during the simulation. Declare the dynamic array
by leaving an unpacked dimension unsized. Create, and re-create, the array during run time using
the new operator. As an argument to the new operator, you can provide an existing array of the
same type to initialize the new array. Dynamic arrays have the size() and delete() methods, as well
as the standard array manipulation methods (see 5.15) and standard array query system functions
(see 22.6).
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 5.6
The IEEE draft standard P1800/D9 clarifies that you can declare more than one unsized dimension,
thus in effect declaring a dynamic array of dynamic arrays.
parameter
parameter num_vectors
num_vectors == 32;
32;
logic
logic [7:0]
[7:0] dyn[];
dyn[]; //
// dynamic
dynamic array
array declaration
declaration
bit
bit [4:0] addr;
[4:0] addr;
logic
logic [7:0]
[7:0] wdata,
wdata, rdata;
rdata;
initial
initial begin
begin
dyn
dyn == new[num_vectors];
new[num_vectors]; //
// array
array allocation
allocation size:32
size:32
$display("dyn.size():%d",
$display("dyn.size():%d", dyn.size());// will print 32
dyn.size());// will print 32
for
for (int
(int i=0;
i=0; i<num_vectors;
i<num_vectors; i++)
i++) begin
begin
write_mem(i, i+5);
write_mem(i, i+5);
dyn[i]
dyn[i] == i+5;
i+5; //
// dynamic
dynamic array
array write
write
end
end
for
for (int
(int i=0;
i=0; i<num_vectors;
i<num_vectors; i++)
i++) begin
begin
read_mem(i, rdata);
read_mem(i, rdata);
if
if (rdata
(rdata !==
!== dyn[i])
dyn[i])
$display("Error
$display("Error at at Addr:%h
Addr:%h -- Wrote:%h,
Wrote:%h, Read:%h",
Read:%h",
i, dyn[i] rdata);
i, dyn[i] rdata);
end
end
dyn.delete();
dyn.delete(); //
// deallocate
deallocate array
array memory
memory
$display("dyn.size():%d", dyn.size());// should
$display("dyn.size():%d", dyn.size());// should be 0 be 0
end
end
This example declares a dynamic array to store 8-bit logic vectors. During run time, it allocates
space for 32 elements. In a loop, it writes data to a memory component and stores the data in the
dynamic memory. In a second loop, it reads data from the memory component and compares it to
the data in the dynamic memory. It deletes the dynamic memory when finished with it.
Associative Arrays
Use an associative array when the data space is unbounded or sparsely populated
Declare the array by specifying a type instead of a size for its one dimension
The key can be any type for which a relative order can be determined
Or ‘*’ to permit indexing by any integral type
bit
bit [3:0]
[3:0] aa1
aa1 [int];
[int]; //
// associative
associative array
array of
of 4-bit
4-bit 2-state
2-state
//
// with
with index type of int
index type of int
logic
logic [7:0]
[7:0] aa2
aa2 [integer];
[integer]; //
// associative
associative array
array of
of 8-bit
8-bit logic
logic
//
// with
with index type of integer
index type of integer
Use an associative array when the address space is unbounded or sparsely populated. Declare the
associative array by specifying a type instead of a size for its one dimension. The key can be any
type for which a relative order can be determined, that is, any type to which SystemVerilog can
apply a relational operator. Associative array elements do not exist until you assign them. You
assign “pairs” of associated key and data values.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 5.9 – 5.13
The IEEE draft standard P1800/D9 adds that associative arrays using the wildcard index type shall
not participate in a foreach loop or in an array manipulation method that returns an index value.
Associative arrays have the num(), delete(), exists(), first(), last(), next(), and prev() methods, as
well as the standard array manipulation methods (see 5.15) and standard array query system
functions (see 22.6). Pause here for a moment and examine the associative array methods.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 5.10
logic
logic [7:0]
[7:0] assoc[int];
assoc[int]; //
// associative
associative array
array declaration
declaration
bit
bit [4:0] rand_a;
[4:0] rand_a;
logic
logic [7:0] rand_d,
[7:0] rand_d, rdat;
rdat;
initial begin
initial begin
for
for (int
(int i=0;
i=0; i<=31;
i<=31; i++)
i++) begin
begin
success
success == randomize(rand_a,
randomize(rand_a, rand_d);
rand_d); //
// random
random data
data gen
gen
write_mem (rand_a, rand_d);
write_mem (rand_a, rand_d);
assoc[rand_a]
assoc[rand_a] == rand_d;
rand_d; //
// associative
associative array
array assign
assign
end
end
$display("Memory
$display("Memory locations
locations to
to check:%d",
check:%d", assoc.num());
assoc.num());
for (int i=0; i<=31; i++)
for (int i=0; i<=31; i++)
if
if (assoc.exists(i))
(assoc.exists(i)) begin
begin //
// only
only read
read address
address if
if written
written
read_mem(i, rdat);
read_mem(i, rdat);
if
if (rdat
(rdat !=
!= assoc[i])
assoc[i]) //
// check
check against
against array
array data
data
$display("Error
$display("Error atat Addr:%h"
Addr:%h" i);
i);
assoc.delete(i);
assoc.delete(i); //
// Deallocate
Deallocate memory
memory at
at index
index
end
end
$display("array
$display("array size:%d",
size:%d", assoc.num());
assoc.num()); //
// should
should display
display 00
end
end
This example declares an associative array to store 8-bit logic vectors with keys of the int type. In a
loop, it randomizes address and data 32 times, writes the data to a memory component, and stores
the address/data pairs in an associative memory. As the address vector is only 5 bits wide, it is very
likely that some addresses are written multiple times. In a second loop, the example iterates
through the potential address space. For each address, if the associative array has an entry for that
address, it verifies that the memory component has the correct data, then deletes the entry for that
address. Deleting entries individually is inefficient.
This algorithm is more efficient. It utilizes associative array methods to iterate through only the
assigned entries. It deletes the entire associative array at once when finished with it.
Queues
Use a queue “array” where insertion and extraction order are important
Declare a queue by using $ as the size of its one dimension:
int q_int[$];
int q_int[$:200]; // optionally limit the queue size
Use $ to reference the end of the queue: myInt = q_int[$];
Queues have the size(), insert(), delete(), pop_front(),
pop_back(), push_front() and push_back() methods
integer
integer q_integer[$];
q_integer[$]; //
// queue
queue of
of integers
integers
logic
logic [15:0]
[15:0] q_logic
q_logic [$];
[$]; //
// queue
queue of
of 16-bit
16-bit logic
logic
int
int q_int[$:2000];
q_int[$:2000]; //
// queue
queue of
of int
int –– max
max size
size of
of 2000
2000
time
time q_time
q_time [$:10];
[$:10]; //
// queue
queue of
of time
time –– max
max size
size of
of 10
10
Use a queue when insertion and extraction order are important. Declare the a queue by using the
“dollar” ($) character as the size of its one dimension. You can optionally limit the queue size by
appending a colon (:) followed by a constant expression after the “dollar” character. You can use
the “dollar” character to represent the end of the queue in subsequent array subscript operations.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 5.14
Queue Methods
Queues have the size(), insert(), delete(), pop_front(), pop_back(), push_front() and push_back()
methods. Unlike the associative array, you cannot delete an entire queue in one statement. An
index value that lies outside the current queue bounds or is otherwise invalid causes a read
operation to return the default value for the queue type. An index value that lies outside the current
queue bounds plus one or is otherwise invalid causes a write operation to issue a warning and not
do the write. Indexing the element just past the current queue end for a write operation is the
equivalent of a push_back() operation.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 5.14.2
The IEEE draft standard P1800/D9 proposes that the delete() function delete the entire queue if no
index argument is provided.
initial
initial begin
begin
q_int.push_front(0);
q_int.push_front(0); //
// {0}
{0}
q_int.push_back(1);
q_int.push_back(1); //
// {0,1}
{0,1}
q_int.push_front(2);
q_int.push_front(2); //
// {2,0,1}
{2,0,1}
q_int.insert(1,
q_int.insert(1, 3);
3); //
// {2,3,0,1}
{2,3,0,1}
q_int.insert(3, 4);
q_int.insert(3, 4); //
// {2,3,0,4,1}
{2,3,0,4,1}
q_int.delete(2);
q_int.delete(2); //
// {2,3,4,1}
{2,3,4,1}
q_int.insert(2,5);
q_int.insert(2,5); //
// {2,3,5,4,1}
{2,3,5,4,1}
data
data == q_int.pop_back();
q_int.pop_back(); //
// {2,3,5,4}
{2,3,5,4} data
data == 11
data
data == q_int.pop_front();
q_int.pop_front(); //
// {3,5,4}
{3,5,4} data
data == 22
while
while (q_int.size()
(q_int.size() >> 0)
0) //
// checking
checking queue
queue size
size
data
data == q_int.pop_back();
q_int.pop_back(); //
// loop
loop executes
executes 33 times
times
q_bit.push_front(8’h01);
q_bit.push_front(8’h01); //
// {’h01}
{’h01}
q_bit.push_back(8’h45);
q_bit.push_back(8’h45); //
// {’h01,’h45}
{’h01,’h45}
q_bit.push_front(8’h89);
q_bit.push_front(8’h89); //
// {’89,’h01,’h45}
{’89,’h01,’h45}
end
end
Queue methods support inserting and extracting elements. This example illustrates use of several of
the methods.
initial
initial begin
begin
q_int
q_int = {0,q_int};
= {0,q_int}; //
// {0}
{0}
q_int
q_int == {q_int,1};
{q_int,1}; //
// {0,1}
{0,1}
q_int
q_int == {2,q_int};
{2,q_int}; //
// {2,0,1}
{2,0,1}
q_int = {q_int[0],3,q_int[1:$]};
q_int = {q_int[0],3,q_int[1:$]}; //
// {2,3,0,1}
{2,3,0,1}
q_int
q_int == {q_int[0:2],4,q_int[3]};
{q_int[0:2],4,q_int[3]}; //
// {2,3,0,4,1}
{2,3,0,4,1}
q_int = {q_int[0:1],q_int[3:4]};
q_int = {q_int[0:1],q_int[3:4]}; //
// {2,3,4,1}
{2,3,4,1}
q_int
q_int == {q_int[0:1],5,q_int[2:3]};
{q_int[0:1],5,q_int[2:3]}; //
// {2,3,5,4,1}
{2,3,5,4,1}
data
data = q_int[$]; q_int
= q_int[$]; q_int == q_int[0:$-1];
q_int[0:$-1]; //
// {2,3,5,4}
{2,3,5,4} data
data == 11
data
data == q_int[0];
q_int[0]; q_int
q_int == q_int[1:$];
q_int[1:$]; //
// {3,5,4}
{3,5,4} data
data == 22
while
while (q_int.size()
(q_int.size() >> 0)
0) begin
begin //
// checking
checking queue
queue size
size
data
data == q_int[$];
q_int[$]; //
// loop
loop executes
executes 33 times
times
q_int
q_int == q_int[0:$-1];
q_int[0:$-1]; end
end
q_bit = {8’h01,q_bit};
q_bit = {8’h01,q_bit}; //
// {’h01}
{’h01}
q_bit
q_bit == {q_bit,8’h45};
{q_bit,8’h45}; //
// {’h01,’h45}
{’h01,’h45}
q_bit = {8’h89,q_bit};
q_bit = {8’h89,q_bit}; //
// {’89,’h01,’h45}
{’89,’h01,’h45}
end
end
A queue is an array, so if you really want to, you can still use unpacked array indexing and
concatenation to insert and extract elements. As this example illustrates, you probably don’t really
want to. The queue methods are much more friendly.
The iterator (default identifier item) references the current element at each
iteration. It is visible only within the expression. See following slides...
11/6/2009 SystemVerilog for Verification 179
Built-in methods support searching, ordering, and reducing arrays. These methods apply to any
unpacked array, with the exception that locator methods do not apply to associative arrays that use
the wildcard index type and ordering methods do not apply to associative arrays at all. The locator
methods return a queue and the reduction methods return a single value.
Within the “with” expression you may refer to the current array element, using the “item” identifier
by default, or using whatever name you pass to the method.
Within the “with” expression you may refer to the current element’s index by using the iterator’s
index() method and passing to it the dimension number for which you want the current index. The
dimension number defaults to 1, that is, the first dimension.
Array locator methods apply to any unpacked array except associative arrays using the wildcard
index type. The min(), max(), and both unique methods further require that relational operators be
defined for the expression to be evaluated. Where the “with” clause is optional, this expression is
by default the array element itself.
This example returns a queue of indexes of the strings whose last character is unique.
Method Description
reverse() Reverse array elements.
Specifying with is an error.
sort() Sort array elements (ascending).
rsort() Sort array elements (descending).
shuffle() Randomize array element order.
Specifying with is an error.
The optional with clause specifies an alternative expression for evaluation, e.g.:
typedef struct {string nm; int unsigned id; shortreal GPA;} student;
student students[];
...
students.rsort() with (item.GPA);
Array ordering methods apply to any unpacked array except associative arrays. The sorting
methods further require that relational operators be defined for the expression to be evaluated for
sorting purposes. As this expression is by default the array element itself, for some array types you
will want to specify some other appropriate expression.
This example declares a dynamic array of an unpacked struct type. For sorting purposes, it uses the
value of the GPA field of each element.
Method Description
sum() Sum of expressions
product() Product of expressions
and() Conjunction of expressions (bitwise)
or() Disjunction of expressions (bitwise)
xor() Parity of expressions (bitwise)
The optional with clause specifies an alternative expression for evaluation, e.g.:
int ia2d[2][2] = ’{default:2};
int j;
...
j = ia2d.product with (item.product); // 16
Summary
Features of SystemVerilog arrays allow efficient and flexible definition,
storage and access to large test data sets
Dynamic arrays useful for contiguous data which varies in size during
simulation
Track an undetermined number of dynamic objects
Pause here for a moment and review what you have learned about SystemVerilog arrays.
Quiz
Match the array that best meets each design need:
Solutions in Appendix A
About Lab 8
For lab 8 you implement a “scoreboard” for the memory write transactions.
You implement it, using a:
dynamic array
associative array
queue array
Interprocess Synchronization
Chapter 10
November 6, 2009
Topics
Non-blocking event trigger
Event sequences
Event variables
Mailboxes
Semaphores
module
module test();
test();
event
event e;
e;
bit
bit clk
clk == 1’b0;
1’b0;
always
always @clk
@clk
#5
#5 clk
clk <=
<= ~clk;
~clk;
always
always @e
@e
$display($time);
$display($time);
initial
initial begin
begin
->>
->> #3
#3 e;
e;
->>
->> @(posedge
@(posedge clk)
clk) e;
e;
end
end
endmodule
endmodule
SystemVerilog adds a non-blocking event trigger (->>) that schedules the event for the NBA
region. You can use intra-assignment delays just as you do with the non-blocking assignment
operator.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.5.2
module
module test();
test(); module
module test();
test();
event
event e;
e; event
event e;
e;
integer
integer ii == 0;
0; integer
integer ii == 0;
0;
always
always @e
@e always
always @e
@e
$display("i
$display("i is
is %d",i);
%d",i); $display("i
$display("i is
is %d",i);
%d",i);
initial
initial initial
initial
begin
begin begin
begin
ii <=
<= 1;
1; ii <=
<= 1;
1;
->
-> e;
e; //
// blocking
blocking ->>
->> e;
e; //non-blocking
//non-blocking
end
end end
end
endmodule
endmodule endmodule
endmodule
output i is 0 output i is 1
The non-blocking event trigger serves the same general purpose as the non-blocking assignment –
to prevent a race between a block that triggers the event and a block that waits for the event in the
same delta cycle.
“hangs” completes
module
module test;
test; module
module test;
test;
event
event e1,
e1, e2;
e2; event
event e1,
e1, e2;
e2;
initial
initial initial
initial
begin
begin begin
begin
$display("fork");
$display("fork"); $display("fork");
$display("fork");
fork
fork fork
fork
@e1;
@e1; @e1;
@e1;
->
-> e1;
e1; ->>
->> e1;
e1;
->
-> e2;
e2; ->
-> e2;
e2;
@e2;
@e2; wait(e2.triggered);
wait(e2.triggered);
join
join join
join
$display("join");
$display("join"); $display("join");
$display("join");
end
end end
end
endmodule
endmodule endmodule
endmodule
An event is not persistent. If a process “waits” for an event after it occurs, the process can go on
waiting forever.
The first example illustrates this. The forked blocks can be scheduled in any order. It is very likely
that one of the event controls is too late – its associated event has already occurred.
The second example utilizes the non-blocking event trigger, which schedules the event for the
NBA region, after the event control has had a chance to “wait” for that event. The example also
illustrates use of the triggered event property, which persists to the end of the time step. If the “e2”
event has already occurred when the “wait” statement executes, the triggered property is still true.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.5.4
A process can wait for a specific sequence of events. The first listed event can alternatively be the
triggered state of an event. The wait_order statement has characteristics very similar to a
concurrent assertion. Occurrence in turn of each event implies that the next event in the list has not
yet occurred and will occur before any of the later events in the list. Re-occurrence of an event has
no additional effect. Events out of order constitute an error by default. You can change this in an
associated action block.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.6
Event Variables
A SystemVerilog event variable is a handle “pointing” to a synchronization queue.
You can assign and compare these handles to each other.
Assignment causes both to “point” to the same queue
module
module test;
test;
event
event e1,
e1, e2;
e2;
initial
initial
fork
fork
#1
#1 e2
e2 == e1;
e1; //
// e1,
e1, e2
e2 both
both now
now have
have e1
e1 synchronization
synchronization queue
queue
#2
#2 @e1
@e1 $display
$display ("e1
("e1 triggered");
triggered");
#2
#2 @e2
@e2 $display
$display ("e2
("e2 triggered");
triggered");
#3 -> e2;
#3 -> e2; //
// trigger e2
trigger e2 (and
(and also
also e1)
e1)
join
join
endmodule
endmodule
output e1 is triggered
e2 is triggered
A SystemVerilog event variable is a handle that “points” to a synchronization queue. The queue is
a list of processes “waiting” for the event.
This example declares two event variables and then assigns one to the other. This makes both event
variables “point” to the same list of processes. Processes can access this synchronization queue
through either variable.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.7
module
module test;
test;
event
event e1,
e1, e2;
e2;
initial
initial
fork
fork
#1
#1 @e1
@e1 $display
$display ("e1
("e1 triggered");
triggered");
#1
#1 @e2
@e2 $display
$display ("e2
("e2 triggered");
triggered");
#2
#2 e2 = e1; // e1, e2 both
e2 = e1; // e1, e2 both now
now have
have e1
e1 synchronization
synchronization queue
queue
//
// e2 synchronization queue is
e2 synchronization queue is lost
lost
#3
#3 ->
-> e2;
e2; //
// trigger
trigger e2e2 (and
(and also
also e1)
e1)
join
join
endmodule
endmodule
output e1 is triggered
What the standard refers to as “event merging” is really just the merging of the event variables.
The source synchronization queue is assigned to the target event variable, so that both variables
“point” to the same queue. If no other variable still “points” to the synchronization queue of the
target variable, processes on that queue can wait forever, and the queue can be reused.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.7.1
module
module test;
test;
event
event e1;
e1;
initial
initial
fork
fork Event e1 is set to null
#1
#1 @e1
@e1 $display
$display ("e1
("e1 triggered
triggered once");
once"); Synchronization
#2 -> e1; // trigger e1 queue can be released
#2 -> e1; // trigger e1
#3
#3 e1
e1 == null;
null; //
// e1
e1 synchronization
synchronization queue
queue lost
lost
#4 @e1 $display ("e1 triggered twice");
#4 @e1 $display ("e1 triggered twice"); At time 3 e1 is null. This
#5 -> e1; // nothing happens may not block at all or
#5 -> e1; // nothing happens may block forever
join
join
endmodule
endmodule Triggering null event
e1 has no effect
Assigning the null value to an event variable assigns a "null" synchronization queue. This
disassociates the variable from its previous synchronization queue. When a synchronization queue
is no longer associated with any event variable, the simulator can reuse the queue. The effect of
waiting on a null event is undefined. An implementation may choose to wait forever or not wait at
all, without warning, and triggering the null event has no effect.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.7.2
Mailboxes
Mailboxes are a message-based synchronization mechanism
Used for passing messages where order is important (FIFO)
Any process puts mail into the mailbox
Blocking: put(anytype message)
Non-Blocking: int try_put(anytype message)
Mailbox Methods
Method Description Syntax
new() Mailbox constructor which optionally specifies function new(int bound = 0);
maximum size Note by default no maximum size.
num() Returns the number of messages currently in function int num();
the mailbox
put() Places a message in the mailbox task put (<message>);
– blocks if mailbox full
try_put() Places a message in a mailbox function int try_put (<message>);
– returns 0 if mailbox full
get() Retrieves a message from the mailbox – task get (ref <variable>);
blocks if mailbox empty
– error if type mismatch
try_get() Retrieves a message from the mailbox function int try_get (ref <variable>);
– returns +int if successful
– returns 0 if empty
– returns -int if type mismatch
peek() Copies a message from the mailbox task peek (ref <variable>);
– blocks if mailbox empty
– error if type mismatch
try_peek() Copies a message from the mailbox function int try_peek (ref <variable>);
– returns +int if successful
– returns 0 if empty
– returns -int if type mismatch
6
sender continues as 4 7 0 receiver gets
receiver gets mail mail at time 7
5 8 1
9 2
At time 1, the sender starts putting six messages into the mailbox, at intervals one time unit apart.
The mailbox can hold at most four messages, so the sender blocks at time 5.
At time 0, the receiver attempts to retrieve a message from the mailbox. At that time, the mailbox
is empty, so the receiver waits for seven time units, and then retrieves six messages, at intervals
one time unit apart.
As the receiver takes messages out of the mailbox, the sender resumes putting messages into the
mailbox.
Typeless Mailbox
common declarations A mailbox can hold messages of different types
class
class pkt;
Blocking get() or peek() with
pkt;
rand
rand bit
bit [4:0]
[4:0] addr;
addr;
rand
rand logic [7:0] data;
logic [7:0] data; incompatible variable type results in
endclass
endclass runtime error
mailbox
mailbox mbox
mbox == new;
typedef enum
new;
Non-blocking try_get() or try_peek()
typedef enum
{pass,
{pass, fail,
fail, hung}
hung} state_t;
state_t;
with incompatible variable type returns a
...
... negative integer
Possible solution – try all types?
sender
string
string pstring;
pstring;
pkt
pkt ppkt
ppkt == new;
new; receiver
state_t
state_t pstatus;
pstatus; string gstring;
string gstring;
...
... pkt
pkt gpkt;
gpkt; ??
pstring
pstring == "test
"test one";
one"; state_t
state_t gstatus;
gstatus; ok
ok == mbox.try_get(gstring);
mbox.try_get(gstring);
assert(ppkt.randomize);
assert(ppkt.randomize); ...
... if
if (ok
(ok << 0)
0) begin
begin
mbox.put(pstring);
mbox.put(pstring); mbox.get(gstring);
mbox.get(gstring); ok
ok == mbox.try_get(gpkt);
mbox.try_get(gpkt);
mbox.put(ppkt);
mbox.put(ppkt); mbox.get(gpkt);
mbox.get(gpkt); if
if (ok
(ok << 0)
0) begin
begin
mbox.put(pstatus);
mbox.put(pstatus); mbox.get(gstatus);
mbox.get(gstatus); ok = mbox.try_get(gstatus);
ok = mbox.try_get(gstatus);
...
... ...
... ...
...
Mailbox Parameters
common declarations You can type-parameterize a mailbox
class
class pkt;
pkt;
bit
bit [4:0]
[4:0] addr;
addr; Define type upon declaration
logic [7:0] data;
logic [7:0] data; Holds messages of that and
endclass
endclass
typedef
equivalent types
typedef enum
enum
{pass,
{pass, fail,
fail, hung}
hung} state_t;
state_t; Compiler detects type mismatch
mailbox
mailbox #(string) smbox == new;
#(string) smbox new;
mailbox #(pkt)
mailbox #(pkt) pmbox = new;
pmbox = new;
mailbox
mailbox #(state_t)
#(state_t) tmbox
tmbox == new;
new;
...
... sender
string
string pstring;
pstring;
pkt
pkt ppkt
ppkt == new;
new; receiver
state_t
state_t pstatus;
pstatus; string gstring;
string gstring;
...
... pkt
pkt gpkt;
gpkt;
pstring
pstring == "test
"test one";
one"; state_t
state_t gstatus;
gstatus;
assert(ppkt.randomize);
assert(ppkt.randomize); ...
...
smbox.put(pstring);
smbox.put(pstring); smbox.get(gstring);
smbox.get(gstring);
pmbox.put(ppkt);
pmbox.put(ppkt); pmbox.get(gpkt);
pmbox.get(gpkt);
tmbox.put(pstatus);
tmbox.put(pstatus); tmbox.get(gstatus);
tmbox.get(gstatus);
...
... ...
...
You can simply use a separate mailbox for each message type.
You specify the one type a mailbox may accept, by overriding its type parameter when you declare
the mailbox variable.
The compiler can now detect any attempt to store or retrieve messages of an incompatible type.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 14.4
Semaphores
Semaphores are a key-based synchronization mechanism.
Used for mutual exclusion, controlling access to shared resources, and
process synchronization.
A process requests semaphore key(s) before accessing the resource
Requesting more keys than a semaphore currently has can block until
sufficient keys are returned
Blocking: get(int keyCount=1)
Non-blocking: int try_get(int keyCount=1)
Semaphore Methods
The new() constructor constructs a semaphore with the specified number of keys. The default value
of its argument is 0 – no keys.
The blocking get() requests one or more keys and suspends the calling process if the key or keys
are not available. The blocked process resumes when sufficient keys to meet its needs are put into
the semaphore.
The non-blocking try_get() returns 0 if the key or keys are not available.
The put() method puts keys into the semaphore. The user is responsible for key management, for
example, to ensure that a process returns only those keys that it previously retrieved.
In this example:
At time 0, process P1 requests a key, and gets it, and accesses the resource.
At time 1, process P2 requests a key and blocks, waiting for the key.
At time 2, process P1 completes its resource access and returns the key.
• Process P2 now unblocks, gets the key, and accesses the resource.
At time 3, process P2 completes its resource access and returns the key.
Remember the user is responsible for defining, managing and utilizing the resource. Common user
errors include:
Writing procedural statements that access the resource without first obtaining the requisite
number of keys.
Writing procedural statements that fail to return keys after completing the resource access.
Writing procedural statements that return keys to the semaphore that they never took out of the
semaphore.
Summary
This chapter explored interprocess synchronization:
Enhanced events
Help prevent race conditions
Provide more control over order of execution
Mailboxes
Multiple-type FIFO operations with built-in blocking synchronization
Semaphores
Multi-purpose synchronization mechanism with built-in blocking and request
“weighting” with keys
Pause here for a moment and review what you have learned about interprocess synchronization.
Quiz
1. Where would you use the non-blocking event trigger?
Solutions in Appendix A
Chapter 11
November 6, 2009
Topics
DPI Overview
Data types
Compilation
This chapter describes the Direct Programming Interface. It shows you how to make task and
function import and export declarations, and how to compile and use the foreign language code
with your SystemVerilog simulation.
module
module top;
top; #include
#include <stdio.h>
<stdio.h>
...
...
$MySystemTask
$MySystemTask ...
... int
int MySystemTask
MySystemTask ...
...
...
... ...
...
The PLI is a true application programming interface. Through it you can access and navigate the
full design hierarchy, and can synchronize a C application to any point of simulation time or to any
simulation event. With this power comes difficulty of use, which typically limits its use to a
handful of experts in any company.
module
module top; #include
top; #include <stdio.h>
<stdio.h>
... #include
... #include <svdpi.h>
import
import "DPI-C"
"DPI-C" context
context c_imp
c_imp == import <svdpi.h>
function
function int
int imp_func(...); int
imp_func(...); int c_imp
c_imp (...)
(...)
{{
export
export "DPI-C"
"DPI-C" c_exp
c_exp == ...
...
function exp_func;
function exp_func; }}
function
function int
int exp_func
exp_func ... extern
... extern int
int c_exp
c_exp (...);
(...);
...
... ...
...
endfunction
endfunction export
The direct programming interface is a “lightweight”, compared to the PLI, interface between
SystemVerilog and foreign programming languages. It maps SystemVerilog subroutine calls to
foreign language implementations, and SystemVerilog subroutine declarations to foreign language
calls. The standard clearly separates the specification of the SystemVerilog “layer” from the
specification of each foreign “layer” so that accommodation of additional foreign languages does
not change the SystemVerilog view of the interface. The standard currently specifies only the C
foreign layer.
DPI Characteristics
The SystemVerilog and foreign language layers are independent
Each layer does its own data interpretation and conversion
Each layer follows its own syntax and semantics rules
e.g. for declaring and calling subroutines
The mapping between SystemVerilog and foreign language data types is specified by the foreign
layer. Here we present the mapping specific to the C foreign language.
The mapping of simple SystemVerilog data types such as byte, int, real and string is
straightforward, as they have obvious C counterparts. The logic type maps to an unsigned char on
the C side to accommodate the four states. For consistency, the bit type also maps to an unsigned
char. Your C code should not use the character value directly, but should instead include svdpi.h
and use the text macros representing the four states. The SystemVerilog LRM describes the name
and core functionality of svdpi.h and permits vendors to differ in their implementations.
--------
typedef signed __int8 int8_t;
typedef uint8_t svScalar;
typedef svScalar svBit;
typedef svScalar svLogic;
#define sv_0 0
#define sv_1 1
#define sv_z 2
#define sv_x 3
C
SystemVerilog #include
#include <stdio.h>
<stdio.h>
module
module top;
top; #include
... #include <svdpi.h>
<svdpi.h>
...
import
import "DPI-C"
"DPI-C" context
context c_imp
c_imp == DPI int
int c_imp
c_imp (...)
(...)
function
function int
int imp_func
imp_func (...);
(...); {{ ...
...
}}
SystemVerilog enumerated types are accessible on the C side as the enumeration base type, but the
enumeration value names are not available.
SystemVerilog packed types are accessible on the C side as arrays of the svBitVecVal type or
svLogicVecVal type as described in svdpi.h.
SystemVerilog unpacked arrays, structures and unions having only the simple types map to their
corresponding C arrays, structures and unions of their corresponding C types.
--------
typedef unsigned __int32 uint32_t;
typedef uint32_t svBitVecVal;
typedef struct vpi_vecval {
uint32_t a;
uint32_t b;
} s_vpi_vecval, *p_vpi_vecval;
typedef s_vpi_vecval svLogicVecVal;
SystemVerilog
module
module parity_calculator()
parity_calculator()
...
...
import
import "DPI-C"
"DPI-C" function
function int
int parityf
parityf (input
(input int
int a);
a);
...
...
initial C
initial #include
#include <stdio.h>
<stdio.h>
...
... #include <svdpi.h>
parity #include <svdpi.h>
parity == parityf(var);
parityf(var);
...
... int
int parityf
parityf (int
(int a)
a) {{
endmodule
endmodule ...
...
return
return parity;
parity;
}}
SystemVerilog
module
module parity_calculator()
parity_calculator()
...
...
import
import "DPI-C"
"DPI-C" function
function int
int parityf
parityf (input
(input int
int a);
a);
...
...
initial C
initial #include
#include <stdio.h>
<stdio.h>
...
... #include <svdpi.h>
parity #include <svdpi.h>
parity == parityf(var);
parityf(var);
...
... int
int parityf
parityf (int
(int a)
a) {{
endmodule
endmodule ...
...
return
return parity;
parity;
}}
As the SystemVerilog and C layers are compiled and elaborated or linked separately, there is no
opportunity for the DPI to check function declarations or verify data type compatibility. It is
therefore critical that you match the import declaration to the C function with respect to the
function name, return type and argument types.
A name mismatch will probably result in only a “symbol not found” error. A type mismatch error
can be more serious and can lead to unpredictable effects as the C layer misinterprets the
SystemVerilog arguments.
By default, you call an imported subroutine using the same name as the C function. You can
specify a different SystemVerilog name for the C function if you provide the C linkage name in the
import declaration. This is useful when the C function name is a reserved word or illegal identifier
in the SystemVerilog namespace. It also allows you to import a C function multiple times with
different SystemVerilog names.
The C linkage name must of course be a legal C identifier, and if not a legal SystemVerilog
identifier, must be appropriately escaped.
You can place import declarations in the compilation unit scope, packages, interfaces, modules or
programs. Placing it in a package simplifies sharing it among multiple design and test units.
You can import any C function. It is common to import functions from the C math library.
SystemVerilog returns the function results by value, so restricts the function return types to the
"small" types. It passes small arguments by value and large arguments by reference, which on the
C side means by pointer.
C functions imported as a SystemVerilog task must return the void type.
You can give a C function imported as a SystemVerilog task or function the context property. You
can give a C function imported as a SystemVerilog function the pure property. Following slides
describe these properties.
An imported subroutine that is not context and not pure can access
variables only in its own name spaces and can make calls to the operating
system, to for example, access the filesystem.
The context property makes the imported task or function aware of the context, that is the scope, of
its declaration. Do not confuse this with the scope of its invocation. A “context” import can,
through exported subroutines or the PLI, access simulation objects in its own context. To access
objects in other contexts, it needs to change its context. The svdpi.h header defines the
svSetScope() routine for setting the context.
An imported subroutine that is not context and not pure can access variables only in its own name
spaces and can make calls to the operating system, to for example, access the filesystem.
Pure Functions
import "DPI-C" pure [ c_identifier = ] prototype ;
The SystemVerilog implementation can optimize calls to pure functions.
You can provide imported functions with the pure property if:
They return a non-void value
They have at least one input and no output or inout arguments
The pure property informs the implementation that the imported function returns a non-void value
and has at least one input argument and no output or inout arguments and has no side-effects
whatsoever. The implementation can optimize the function call. The pure property cannot apply to
imported tasks.
SystemVerilog
module
module dpi_test()
dpi_test()
...
...
export
export "DPI-C"
"DPI-C" function
function parityf;
parityf;
...
...
initial
initial C
...
... #include
#include <stdio.h>
<stdio.h>
function
function int
int parityf(input
parityf(input int
int a);
a); #include
#include <svdpi.h>
<svdpi.h>
...
... extern
extern int
int parityf(int
parityf(int a);
a);
endfunction
endfunction ...
endmodule ...
endmodule int
int ii == parityf(val);
parityf(val);
...
...
11/6/2009 SystemVerilog for Verification 219
By default, you call an exported subroutine using the same name as the SystemVerilog subroutine.
You can specify a different SystemVerilog name for the C function if you provide the C linkage
name in the export declaration. This is useful when the SystemVerilog subroutine name is a
reserved word or illegal identifier in the C namespace. As every call of an exported SystemVerilog
task or function is associated with a context, you can export a SystemVerilog task or function
multiple times to the same C identifier if the exports are declared in different scopes and have the
same signature. that is, return type, name, and argument types.
The C linkage name must of course be a legal C identifier, and if not a legal SystemVerilog
identifier, must be appropriately escaped.
Exported subroutines:
Accept the same argument data types as imported subroutines
You can place export declarations in the compilation unit scope, packages, interfaces, modules or
programs. Placing it in a package simplifies sharing it among multiple design and test units.
An export declaration can export only the subroutines declared in its own scope, and can export a
subroutine at most once. An export declaration is illegal within a class type declaration and so
cannot export a class method.
An imported subroutine with the “context” property can call an exported subroutine. This is how
you create a time layer in C. The time is actually consumed by the exported SystemVerilog task
called by an imported task. A SystemVerilog function cannot of course call a SystemVerilog task
regardless of what language actually implements the task.
import
import "DPI-C"
"DPI-C" context
context c_func
c_func ==
function
function int import_c_func ((
int import_c_func
input
input bit
bit bin1,
bin1,
inout
inout bit bin2
bit bin2 );
);
export
export "DPI-C"
"DPI-C" sv_func
sv_func == function
function export_sv_func;
export_sv_func;
function
function int
int export_sv_func(input
export_sv_func(input bit
bit eb1,
eb1, #include
#include <stdio.h>
<stdio.h>
inout
inout bit
bit eb2
eb2 );
); #include <svdpi.h>
if (eb1 != eb2)
#include <svdpi.h>
if (eb1 != eb2)
...
...
endfunction extern
extern int
int sv_func(svBit,
sv_func(svBit, svBit*)
svBit*)
endfunction
initial
initial begin
begin int
int c_func
c_func (svBit
(svBit b1,
b1, svBit
svBit *b2)
*b2)
#2
#2 res_top
res_top == import_c_func(b1,
import_c_func(b1, b2);
b2); int
int res;
res;
$display("res_top
$display("res_top :: %d\n",
%d\n", res_top); printf(" b1
res_top); printf(" b1 == %d,
%d, b2
b2 == %d\n",
%d\n",
end
end b1,
endmodule b1, *b2);
*b2);
endmodule res
res = sv_func(b1, b2);
= sv_func(b1, b2);
printf("
printf(" b1
b1 == %d,
%d, b2
b2 == %d\n",
%d\n",
b1,
b1, *b2);
*b2);
return res;
return res;
11/6/2009 SystemVerilog for Verification
}} 222
The SystemVerilog initial block calls the “import_c_func”, which is linked to the C function
named “c_func” in the import declaration. This function is imported with the context property so
that it can in turn call exported SystemVerilog subroutines.
The C implementation of the “c_func” function calls the “sv_func” function that it has previous
declared to have external linkage. This function is linked to the SystemVerilog function named
“export_sv_func” in the export declaration.
The imported subroutine may not call any more exported subroutines
You code this handshake for all imported subroutines that call exported subroutines.
No handshake mechanism exists for disabled exported subroutines.
The SystemVerilog statement calling an imported subroutine may become disabled. This is
important if the imported subroutine calls exported subroutines, for a disabled statement must have
no further visible effect in the SystemVerilog layer.
Upon return from the exported subroutine call, SystemVerilog advises the imported subroutine that
its call has been disabled. An exported task call returns 1 rather than 0 to flag the disable. The
imported subroutine can also call svIsDisabledState() to check its status.
A disabled imported subroutine must immediately cease any activity in the SystemVerilog layer
and must acknowledge the disable. An imported task returns 1 rather than 0, and an imported
function calls svAckDisabledState().
Pause here to examine this disable handshake protocol.
function
function void
void voidExportFunc();
voidExportFunc();
disable
disable disableMe;
disableMe;
$display("exportTask
$display("exportTask exit");
exit"); //
// no
no display
display
endfunction : voidExportFunc
endfunction : voidExportFunc
export
export "DPI-C"
"DPI-C" function
function voidExportFunc
voidExportFunc ;;
import
import "DPI-C"
"DPI-C" context function void voidImportFunc()
context function void voidImportFunc() ;;
initial
initial
begin
begin :: disableMe
disableMe
voidImportFunc();
voidImportFunc();
end
end
endmodule
endmodule :: test
test #include
#include <stdio.h>
<stdio.h>
#include
#include "svdpi.h“
"svdpi.h“
extern
extern void
void voidExportFunc();
voidExportFunc();
void
void voidImportFunc()
voidImportFunc() {{
voidExportFunc();
voidExportFunc();
printf("svIsDisabledState()
printf("svIsDisabledState() is
is %d\n",svIsDisabledState());
%d\n",svIsDisabledState());
if
if (svIsDisabledState())
(svIsDisabledState()) svAckDisabledState();
svAckDisabledState();
}}
An imported subroutine that calls an exported subroutine must comply with the disable protocol.
That protocol requires the imported subroutine, upon return from calling an exported subroutine, to
check whether it, the imported subroutine, was disabled. If it is disabled, it may not call any more
exported subroutines and must acknowledge the disable.
This example calls an imported function from within a named block. The imported function in turn
calls an exported function. The exported function disables the named block, causing the imported
function call to terminate abnormally. Upon return from calling the exported function, the imported
function must check its disabled status, and if disabled, must acknowledge it.
Pause here to examine this disable of the call to an imported function.
task
task exportTask();
exportTask();
#1
#1 disable
disable disableMe;
disableMe;
#1
#1 $display("exportTask exit");
$display("exportTask exit"); //
// no
no display
display
endtask : exportTask
endtask : exportTask
export
export "DPI-C"
"DPI-C" task
task exportTask
exportTask ;;
import
import "DPI-C" context task
"DPI-C" context task importTask()
importTask() ;;
initial
initial
begin
begin :: disableMe
disableMe
importTask;
importTask;
end
end
endmodule
endmodule :: test
test #include
#include <stdio.h>
<stdio.h>
#include
#include "svdpi.h"
"svdpi.h"
extern
extern int
int exportTask();
exportTask();
int
int importTask()
importTask() {{
int
int disabled
disabled == exportTask();
exportTask();
printf("exportTask
printf("exportTask returned
returned %d\n",disabled);
%d\n",disabled);
printf("svIsDisabledState()
printf("svIsDisabledState() isis %d\n",svIsDisabledState());
%d\n",svIsDisabledState());
return
return disabled;
disabled;
}}
An imported subroutine that calls an exported subroutine must comply with the disable protocol.
That protocol requires the imported subroutine, upon return from calling an exported subroutine, to
check whether it, the imported subroutine, was disabled. If it is disabled, it may not call any more
exported subroutines and must acknowledge the disable.
This example calls an imported task from within a named block. The imported task in turn calls an
exported task. The exported task disables the named block, causing the imported task call to
terminate abnormally. Upon return from calling the exported task , the imported task must check its
disabled status, and if disabled, must acknowledge it.
Pause here to examine this disable of the call to an imported task.
Compilation
SystemVerilog recommends two alternatives for including the linked C object code:
By directly providing the library name with the -sv_lib option
simulator
top.v top top
C compiler linker
taskfunc.c taskfunc.o myLib.so
The actual usage methodology is vendor dependent, but is likely to be something like this:
You separately compile your C code and link it into a shared object library with a platform-
specific extension, such as .sl or .so for UNIX variants and .dll for Windows.
You compile, elaborate and simulate your SystemVerilog code as usual, and provide the
shared-object library to the simulator.
The standard recommends that command line options be provided for this purpose, and even
suggests names for them:
The -sv_lib option provides a pathname for the shared object library minus its extension – the
tool provides the correct extension for the platform.
The -sv_root option provides a single directory path that is prefixed to any relative path
specified by the -sv_lib option.
DPI Summary
Advantages Potential Issues
Easy to use No arbitrary data types
Standard interface SystemVerilog types only
if no packed types Packed array and struct layout is
Doesn’t require new simulator dependent upon platform and
executable implementation
Pause here for a moment and review what you have learned about the direct programming
interface.
Quiz
1. Write a function import statement to import the exp2f function (from the
cmath library) with the base2exp SystemVerilog identifier. Assume the
C function has no side effects. The C function prototype is:
float exp2f(float x);
Solutions in Appendix A
About Lab 12
In lab 12, you replace a C++ transaction-level model with an equivalent
SystemVerilog model that you create. To do this, you determine the
appropriate SystemVerilog data type for the function arguments, write and
export the functions, and on the C++ side declare the functions to have
external linkage.
system_c :
system
system_c.h packet_c :
packet
packet_c.h
producer_c : consumer_c :
producer consumer
producer_c.h consumer_c.h
Chapter 12
November 6, 2009
Topics
What is an assertion?
This chapter introduces assertion-based verification: what it is, what it does, and why you might
want to adopt the methodology.
What is an Assertion?
An assertion is a check embedded in, or
bound to, a design unit
During simulation, assertions watch to
see:
Testbench
If a specific condition occurs, or
An assertion is just a statement that something is true. What’s important about an assertion is that it
asserts that some design property holds, and the simulator reports the failure of that design property
to hold. Stating the assertion is the easy part. Crafting useful design properties is more difficult.
However, it’s far easier than debugging a broken system without the help of assertions.
The graphic illustrates where you can place an assertion. Note that you can place them on design
inputs and design outputs and design interconnect and in the design blocks. You do not typically
see assertions on a testbench.
DUV
Assertions monitor and report:
Expected behavior
Assertion-based verification is a verification methodology that utilizes assertions. This slide pretty
much reiterates what we have just said. It does also point out that assertions are not limited to just
simulation tools. Formal verification tools, that do their verification statically, typically with very
little helper test stimulus, if any, also use assertions.
`ifndef
`ifndef SYNTHESIS
SYNTHESIS
always @GNT
always @GNT
@(negedge
@(negedge clk)clk)
if
if (( GNT
GNT !=!= 4’h0
4’h0 ))
begin: GNT_CHK
begin: GNT_CHK
integer
integer cnt,cnt, idx;
idx;
cnt =
cnt = 0 ;0 ;
for
for (( idx
idx == 0;0; idx
idx <=
<= 3;
3; idx
idx == idx+1
idx+1 ))
if
if (( GNT[idx]
GNT[idx] !=!= 1’b0
1’b0 )) cnt
cnt == cnt
cnt++ 1;
1;
if
if ( cnt > 1 ) $display ( "ERROR: Multiple GNT"
( cnt > 1 ) $display ( "ERROR: Multiple GNT" );
);
end
end
`endif
`endif
Verification personnel writing Verilog testbenches have been using an informal type of assertion
right along. They write a process, that upon appropriate events, checks that some expression has
the desired value, and if not, takes appropriate action. They can even write a check that spans
multiple cycles, although that is more difficult, and thus rare.
The use of assertions is not new. What is relatively new is the standardization of language
constructs with which you can concisely express temporal design behaviors, and consistent support
of those constructs, with failure messages, statistics collection and reporting, and user-friendly
debug features.
http://www.dac.com
A quick revisit to the DAC archives will reveal that use of assertions is pretty much mainstream
now.
RTL Implementation has properties the When Read and Write pointers
Functional Property
Designer RTL designer intended it to have. both 0, the FIFO indicates empty.
A system architect develops abstract properties that represent the desired behavior of the system at
the functional level. At this point the actual implementation is not known.
The block designer develops objective properties that represent the desired behavior of the system
at the register level. It is important to verify this reinterpretation of the architect’s properties.
As an IP designer, the RTL designer will want to embed input assertions in the design to detect
future misuse of the design.
The verification engineer focuses on design functional coverage points, which should include that
the testbench actually exercised the asserted properties. Thus, functional coverage is a fundamental
part of an Assertion Based Verification methodology.
You can place assertions anywhere in your design hierarchy, enabling you to monitor design
behavior locally and highlight problems as soon as they occur at the source of the problem.
Conventional testbenches need to propagate a problem to the design outputs to detect it, and then
you have to trace the error back through the design, and probably back through time as well, to
determine the source. Assertions have the potential to detect problems during the early stages of
testbench development, when the testbench might not yet be sufficiently robust to even propagate a
problem to the outputs.
You can have the simulation terminate upon failure of critical assertions, thus permitting other
projects to use the compute cycles that would otherwise be wasted.
Writing design properties encourages you to think more clearly about your design. This itself can
highlight problems before you even finish the property. Overall verification efficiency is improved
by designers producing better designs to begin with.
Your embedded assertions travel with your design as it is used in different projects, warning
against unexpected or incorrect use of your design block.
Your embedded assertions also travel with your design from tool to tool, for example from
simulation to formal verification, avoiding duplication of the verification effort.
The simulator can make only those checks that the test exercises
The most prominent issue with the Assertion Based Verification methodology is that you, the user,
must craft the design properties. It’s very easy to construct a property that is not really 100% true.
It’s even easier, perhaps very common, to miss nuances of design behavior, some corner cases, that
really ought to be checked. There is also the issue of “how do you know when your property set is
sufficient”.
The simulator can check only those properties that the testbench exercises, so testbench quality is
critical. This demands coverage metrics, which help to solve the “how do you know when you have
tested enough” question, but do nothing for the “how do you know when your property set is
sufficient” question.
Summary
Complex properties are difficult to write using Verilog
Documents interfaces
Pause here for a moment, and review what you have learned about assertion-based verification.
Quiz
1. What is an assertion?
Solutions in Appendix A
Chapter 13
November 6, 2009
Topics
Concurrent assertion structure
Multi-cycle sequences
Repetition in sequences
This chapter introduces SystemVerilog Assertions: how to declare and assert design properties,
some property operators, and some sequence operators with which you can construct more
complex properties.
Concurrent Assertions
Embedded in functional code and ignored by synthesis
We have previously learned that an immediate assertion is a procedural statement that some
boolean condition is true. The assertion is checked when the procedural statement executes.
You typically place concurrent assertions outside procedural blocks to execute concurrently with
the procedural blocks and utilize their own clock event.
A property is a design behavior that may span multiple cycles. It is a design requirement, similar to
a synthesis constraint that the design must work with a 250MHz clock. SystemVerilog has special
operators you use to develop concise property expressions to represent complex multicycle design
behavior.
rw_chk
label : assert property ( [clocking_event]
@(negedge clock) !(wr_en
property_spec && rd_en)
property_expr );
((A
((A ++ B)
B) >=
>= 42)
42) &&
&& (C
(C ||
|| D);
D);
A property expression can be simply a boolean expression. Use parentheses where needed to
appropriately group the operands and to enhance readability.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 17.4
ASSERT1
ASSERT1 :: assert
assert property
property (( @(EN1
@(EN1 or
or EN2)
EN2) EN1
EN1 ||
|| EN2
EN2 );
);
assertion
parentheses
property
property PROP1;
PROP1;
@(EN1
@(EN1 or EN2)
or EN2) EN1
EN1 ||
|| EN2;
EN2;
endproperty
endproperty
ASSERT1
ASSERT1 :: assert
assert property
property (PROP1);
(PROP1);
always label
assertions
Labels are used to reference the assertion
11/6/2009 SystemVerilog for Verification 247
You can specify the property within the assertion statement itself. Be sure to label the assertion
statement so that upon assertion failure the tool reports a name that is meaningful to you.
Otherwise the tool reports the scope of the assertion.
You can alternatively declare and name the property. You can parameterize a declared property.
You can instantiate the declared property as an operand in a property expression that uses property
operators. If you assert the named property and omit the assertion statement label, the tool can
upon assertion failure report the property name.
The assertion label and property name must be valid Verilog identifiers. They reside in the module
name space and must not clash with other identifiers in that name space.
--------
property_declaration ::=
property property_identifier [ ( [ tf_port_list ] ) ] ;
{ assertion_variable_declaration }
property_spec ;
endproperty [ : property_identifier ]
property
property PCLK1;
PCLK1;
@(posedge
@(posedge CLK)
CLK) EN1
EN1 ||
|| EN2;
EN2;
endproperty
endproperty
property
property PCLK2;
PCLK2;
@(EN1
@(EN1 or
or EN2)
EN2) EN1
EN1 ||
|| EN2;
EN2;
endproperty
endproperty
property
property PCLK3;
PCLK3;
@(negedge
@(negedge ADDR_EN)
ADDR_EN) ADDR
ADDR <=
<= 7;
7;
endproperty
endproperty
All assertions must be clocked. You can specify the clock in a sequence expression that the
property uses, or in the property specification.
The clocking event is typically just the Verilog event control. The parenthesized syntax also
accepts a sequence name and the SystemVerilog “if-and-only-if” (iff) clock qualifier. For a
sequence name, it is the endpoint of the sequence that clocks the property. Of course that sequence
itself must also be clocked.
--------
event_expression ::=
[ edge_identifier ] expression [ iff expression ]
| sequence_instance [ iff expression ]
| event_expression or event_expression
| event_expression , event_expression
A SystemVerilog simulation time instant is divided into several regions. The regions separate
various activities in order to help avoid races between the writing and reading of a variable.
Assertion evaluation occurs in the Observed region, after all current variable updates complete, but
uses sampled values from the Preponed region to evaluate the sequence and property expressions.
This means that you can safely use the same clock edge to clock the assertions that you use to
update the variables. Assertion evaluation uses the pre-clock values.
This diagram omits several regions used only for PLI callbacks.
EN_1HOT_CLK succeeds
Clock asynchronous properties on any change of any signal used in the property expression.
Clock synchronous properties on the appropriate edge of the appropriate clock signal.
Property expression evaluation uses the sampled values, that is, the values before the clock event.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 17.3
Assertion Placement
You can declare a property in a
compilation unit scope, package,
interface, module, program or module
module mod1
mod1
clocking block. (input
(input logic
logic CLK,
CLK,
...
...
You can assert a property only output
output logic
logic EN1,
EN1, EN2);
EN2);
within an interface, module or
`ifndef
`ifndef USING_OLD_TOOL
program, or always or initial block. USING_OLD_TOOL
property
property PROP1;
PROP1;
@(posedge
@(posedge CLK)
CLK) EN1
EN1 ||
|| EN2;
EN2;
endproperty
endproperty
What to do about old tools that do
ASSERT1:
ASSERT1: assert
assert property
property (PROP1);
not know about SystemVerilog? `endif
`endif
(PROP1);
...
Does the tool pre-define a text ...
macro?
Or use bind construct (later
chapter) with new tool to bind
separate assertion modules
11/6/2009 SystemVerilog for Verification 251
You can declare a property, as a type, in the compilation unit scope, package, interface, module,
program or clocking block. You can assert the property only in an interface, module, or program,
or in an always or initial block.
SystemVerilog does not predefine a macro that you can use to “hide” code from compilers that do
not support it. Perhaps the compiler itself defines a macro you can use to bypass compilation of the
SystemVerilog assertions.
SystemVerilog provides a “bind” construct with which you can externally, perhaps from the
testbench, instantiate assertion components within a module without modifying the module
description.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Sections 17.11, 17.13
Sequences
You cannot write a complex temporal property
using only the property operators sequence
@(negedge
@(negedge CLK)
CLK)
Special operators that construct sequences AA ##1
##1 BB ##1
##1 C;
C;
facilitate writing properties that span time
Sequences are building blocks of properties CLK
Matching
Composition
You cannot write a complex temporal property using only the property operators. The only
property operator that connects two cycles is the next-cycle implication (|=>) operator, and you
cannot chain this operator because the resulting expression is a property and the left operand must
be a sequence.
A sequence is a series of boolean conditions over successive cycles.
The “pound pound” (##) token is the cycle delay operator. Here we are using it to construct the
sequence “A followed by B followed by C” in successive evaluations. Here the inputs are boolean
signals, but they could be boolean expressions or even sequences of boolean expressions.
This chapter later more fully describes the cycle delay operator.
SystemVerilog provides two sequence implication operators. This is the “same cycle” implication
operator. For this operator, the consequent property must hold in the same cycle in which the
antecedent sequence completes. This operator is a property operator, that is, the resulting
expression is a property expression which you can use as an operand in yet another property
expression.
For this example, the left side sequence and right side property are both non-temporal, so you can
replace this same-cycle implication with a simple boolean expression.
--------
property_expr ::= ... | sequence_expr |-> property_expr | ...
SystemVerilog provides two sequence implication operators. This is the “next cycle” implication
operator. For this operator, the consequent property must hold in the cycle after that in which the
antecedent sequence completes. This operator is a property operator, that is, the resulting
expression is a property expression which you can use as an operand in yet another property
expression.
--------
property_expr ::= ... | sequence_expr |=> property_expr | ...
property
property STATE;
STATE;
@(negedge
@(negedge CLK)
CLK) (A
(A ##1
##1 B)
B) |=>
|=> C;
C;
endproperty
endproperty
1 2 3 4 5 6 7 8
CLK
C
1-active 1-inactive 2-active 2-enabled 2-succeeded 3-active 3-enabled 3-failed
4-active 4-inactive
The cycle delay expression can be an integral constant expression or a range between two integral
constant expressions. Here we have a three cycle delay. The property holds in any given cycle if
either “A” is true in that cycle or if “A” is false and four cycles later “B” is true. Note that this
example uses next-cycle implication.
A delay of zero is valid and useful. A zero delay between the end of the left sequence and the start
of the right sequence means that the left sequence ends and the right sequence begins in the same
cycle. This is called sequence fusion.
--------
IEEE Std. 1800-2005 SystemVerilog LRM, Section 17.5
SystemVerilog provides three sequence repetition operators for your convenience. This slide
illustrates the consecutive repetition operator, which simply repeats its boolean for whatever many
cycles is the constant integral expression or range between two constant integral expressions. You
can use this consecutive repetition operator with a boolean expression, an instance of a declared
sequence, and with an inline sequence specification enclosed within parentheses.
--------
sequence_expr ::=
...
| expression_or_dist [ boolean_abbrev ]
| ( expression_or_dist {, sequence_match_item } ) [ boolean_abbrev ]
| sequence_instance [ sequence_abbrev ]
| ( sequence_expr {, sequence_match_item } ) [ sequence_abbrev ]
...
This example specifies a range for the consecutive repetitions. The property holds in any given
cycle if “A” is either false in that cycle, or true in the next cycle, or false for the next two to four
cycles and true in the cycle after that.
This slide provides an example of how difficult it is to craft a property that truly represents your
intentions. If the specification stated that “A” is low for between two and four cycles, then this
property would not reflect that specification, as the property as defined also holds for all cycles if
“A” is low at cycle zero and stays low. Note the difference between “is low” and “goes low”.
SystemVerilog has several more advanced property and sequence operators and features. The
“SystemVerilog Assertions” training covers these plus has coding guidelines and
recommendations, functional coverage and working examples
Summary
SystemVerilog assertions are based on Verilog boolean conditions
SVA has many features for defining multi-cycle and repeated sequences
e.g. consecutive repetition [*N]
Pause here for a moment and review what you have learned about SystemVerilog assertions.
Quiz
Code this property and assert it:
If the req…ack handshake occurs …
gnt is true on only the 2nd cycle after the req…ack handshake
end is true on only the 6th cycle after gnt is true
clk
req
req
ack ack
gnt
gnt A data B
end
data
end
Solutions in Appendix A
Chapter 14
November 6, 2009
Congratulations upon finishing this SystemVerilog Verification training! We hope the information
in this training module met your verification and system-level design needs. You should expect to
become truly proficient with these constructs only after several months of practical application.
What’s Next?
— SystemVerilog Language and Application —
Design Verification Module Assertion
Module Module
Day 1 Day 2 Day 3 Day 4 Day 5
Overview Overview Random Interprocess Introduction to Assertion Based
Stimulus Synchronization Verification (ABV)
Data Types Verification
Features Class-Based Direct Immediate Assertions
Procedures
Randomization Programming
Verification Simple Boolean Assertions
Operators Interface (DPI)
Blocks Coverage
Sequences
User-Defined Assertion-Based
Transaction- Arrays and
Data Types Verification Sequence Composition
Based Queues
Hierarchy & Verification SystemVerilog Advanced Features
Connectivity Assertions
Classes Coding Guidelines
Subroutines
Functional Coverage
Interfaces
Practical Application
Static Verification
Successful completion of the Verification training qualifies you to now move on to the
SystemVerilog Assertions training. To complete this additional training will take approximately
another two days of your effort.
Additional Resources
You can obtain additional information from the following sources:
Standards
IEEE Std. 1800-2005 http://www.ieee.org
Advocacy groups
Accellera http://www.accellera.org
EDA.org http://www.eda.org
News groups
http://groups.google.com/group/comp.lang.verilog
http://groups.google.com/group/SystemVerilog
SystemVerilog Publications
"SystemVerilog for Verification". Spear, C., Springer (http://www.springer.com), 2006.
“SystemVerilog Assertions Handbook”, Cohen, B. (et al), VhdlCohen Publishing
(http://www.abv-sva.org), 2005.
Cadence Documentation
SystemVerilog Reference (http://sourcelink.cadence.com)
Quiz Solutions
Appendix M2A
November 6, 2009
3. Verification Blocks
1. What do the input, inout, and output clocking directions signify?
The clocking directions specify whether the clocking applies to sampling the signal,
driving the signal, or both sampling and driving the signal.
clocking
clocking cb1
cb1 @(negedge
@(negedge clk);
clk); @(cb1);
@(cb1); //
// @(negedge
@(negedge clk);
clk);
output
output posedge drive;
posedge drive; ##2;
##2; //
// repeat(2)
repeat(2) @(cb2);
@(cb2);
endclocking
endclocking cb1.drive
cb1.drive <=
<= ##3
##3 1;
1; //
// repeat(3)
repeat(3) @(cb1)
@(cb1)
clocking
clocking cb2
cb2 @(negedge
@(negedge clk);
clk); ##3;
##3; cb1.drive <= 2’b10; //
cb1.drive <= 2’b10; // repeat(3)
repeat(3) @(cb2);
@(cb2);
endclocking
endclocking ##(j-2)
##(j-2) cb1.drive
cb1.drive <=<= 1;
1; //
// repeat(j-2)
repeat(j-2) @(cb1)
@(cb1)
default
default clocking
clocking cb2;
cb2;
int j = 3;
int j = 3;
4. Transaction-Level Modeling
1. What is a transaction?
A transaction is a transfer of data between design blocks.
2. What components produce and consume transactions?
A] RTL Design. B] System-level Design. C] Testbench. D] B & C.
Testbenches and system-level designs utilize transactions.
3. True of False: A transactor converts between a transaction interface and
a signal interface.
True: A transactor converts between a transaction interface and a
signal interface.
4. What features of SystemVerilog interfaces most prominently support
transaction-based verification?
SystemVerilog interface methods and clocking blocks prominently
support transaction-based verification.
5. Classes
1. What is the role of a class constructor?
The class constructor initializes the class properties.
2. What is the difference between a static and non-static property?
One instance of a non-static property exists for each instance of the class type.
One instance of a static property exists and is not associated with any class instance.
3. What is the difference between a static and non-static method?
A static method of a class can access only the static members of the class.
4. What is inheritance?
A subclass inherits the members of its superclass and can redeclare them.
5. What is the difference between a protected and a local class member?
A subclass method can access a protected member of its superclass.
Only class member methods can access local members of a class.
6. Give an example of polymorphism.
An implementation of a virtual method is selected dynamically based upon the
subclass object that the superclass handle “points” to.
6. Random Stimulus
Define the output for each value of i:
randsequence()
randsequence()
top
top :: AA B;
B;
AA :: CC DD E;
E;
BB :: CC EE {{ if
if (i
(i ==== 11 )) return;
return; }} D;
D;
CC :: {$display("C"); if(i == 3) return;};
{$display("C"); if(i == 3) return;};
DD :: {if
{if (i(i ==
== 22 )) return;
return; $display(
$display( "D");};
"D");};
EE :: {if
{if (i(i ==
== 00 )) break;
break; $display("E");};
$display("E");};
endsequence
endsequence
i == 0 produces C D
i == 1 produces C D E C E
i == 2 produces C E C E
i == 3 produces C D E C E D
8. Coverage (question)
module
module example_with_bins;
example_with_bins;
logic
logic clk;
clk;
logic
logic [2:0]
[2:0] opcode;
opcode;
logic
logic [15:0]
[15:0] address;
address; How many bins in
? this covergroup?
covergroup
covergroup cg1
cg1 @(posedge
@(posedge clk);
Question What are their names?
clk);
c1:
c1: coverpoint
coverpoint opcode;
opcode;
c2:
c2: coverpoint
coverpoint address
address {{
bins
bins low[]
low[] == {{ [0:’h0F]
[0:’h0F] }} ;; opcode:
bins
bins high = { [’h10:’hFF] }} ;;
high = { [’h10:’hFF] 8 implicit
}} “c1.auto[0]” to “c1.auto[7]”
endgroup
endgroup :: cg1
cg1
address:
cg1
cg1 cover_inst
cover_inst == new();
new(); 16 explicit vectored
“c2.low[0]” to “c2.low[F]”
...
... 1 explicit scalar
“high”
endmodule
endmodule
8. Coverage
bit
bit [7:0]
[7:0] avec,
avec, bvec;
bvec;
covergroup
covergroup cgcg @(posedge
@(posedge clk);
clk);
cpa:
cpa: coverpoint
coverpoint avec
avec {{
bins
bins a1
a1 == {{ [0:63]
[0:63] };
}; How many bins in this
bins
bins a2 = { [64:127] };
a2 = { [64:127] }; ? covergroup cross c?
bins
bins a3
a3 == {{ [128:191]
[128:191] };
Question What are their names?
};
bins
bins a4
a4 == {{ [192:255]
[192:255] };}; }}
cpb:
cpb: coverpoint
coverpoint bvec
bvec {{ c1: 4
bins b1 = {0};
bins b1 = {0}; <a1,b1>, <a1,b2>,
bins
bins b2
b2 == {{ [1:84]
[1:84] };
}; <a1,b3>, <a1,b4>
bins
bins b3
b3 == {{ [85:169]
[85:169] };}; c2: 7
bins
bins b4 = { [170:255] };
b4 = { [170:255] }; }} <a2,b1>, <a2,b2>,
cc :: cross
cross cpa,
cpa, cpb
cpb {{ <a2,b3>, <a2,b4>,
<a1,b2>, <a3,b2>,
bins
bins c1
c1 == !! binsof(cpa)
binsof(cpa) intersect
intersect {[100:200]};
{[100:200]}; <a4,b2>
bins
bins c2
c2 == binsof(cpa.a2)
binsof(cpa.a2) || || binsof(cpb.b2);
binsof(cpb.b2);
bins
bins c3
c3 == binsof(cpa.a1)
binsof(cpa.a1) && && binsof(cpb.b4);
binsof(cpb.b4); }} c3: 1
<a1, b4>
endgroup
endgroup
clk
req
req
ack ack
gnt
gnt A data B
end
data
end
Index
i
This document is for the sole use of Sundaresan Chidambaram of Manipal University
ii
This document is for the sole use of Sundaresan Chidambaram of Manipal University
iii
This document is for the sole use of Sundaresan Chidambaram of Manipal University
polymorphism, 93
J post_randomize(), 126
join_any, 32 pre_randomize(), 126
join_none, 32 process
synchronization, 186-205
with event, 188-194
L with mailbox, 195-199
with semaphore, 200-202
linkage name (DPI), 215, 220
local class property, 91 programs, 44
$exit, 44
property
M assertion, 242-261
class, 78, 83, 91
mailbox , 195-199 local, 91
methods, 196 protected, 91
parameters, 199 static, 83
methods protected class property, 91
array manipulation, 179-182 ps (time literal), 25
array locator, 180 pseudo-random, 105
array ordering, 181 pure function (DPI), 216-218
array reduction, 183
associative array, 171-174
class, Q
external, 80
static, 84 queue, 175-178
virtual, 92-93
dynamic array, 169-170 R
mailbox, 196
in a modport, 65-66 rand, 124
queue array, 187-190 rand_mode, 129-130
semaphore, 201 randc, 124
ms (time literal), 25 randcase, 113
random number generator, 105, 108
randomize(), 107, 125
N randomization
new $urandom(), 106
class, 76, 81, 87 $urandom_range(), 106
covergroup, 147, 149 class object, 122-143
dynamic array, 169-170 rand, 124
mailbox, 227 randc, 124
semaphore, 201 rand_mode(), 129-130
ns (time literal), 25 constraint, 14, 104, 111-112, 131-140
null conditional, 112, 135
class variable, 76-77 constraint_mode(), 138-139
event variable, 194 dist (distributions), 111, 134
virtual interface, 67 inside operator, 111, 133
inheritance, 132
iterative, 136
O solve-before, 137
weights, 111, 134
Object-Oriented (OO) design, 75 randomize(), 107, 125
option, 164, 166 random number generator, 105, 108
output seed, 110, 140
in clocking block, 47-49 stability, 108
scope variable, 98-112
P srandom(), 109, 140
randsequence, 115-122
parameter productions, 114-118
class type, 79, 94 break, 117
mailbox, 199 case, 116
module, 35, 182 if-else, 116
property, 247 passing values, 118
PLI, 216 repeat, 116
iv
This document is for the sole use of Sundaresan Chidambaram of Manipal University
S
s (time literal), 25
scheduler (event) 42-43
scope randomization (variable), 98-121
semaphore , 200-202
methods, 201
sequence
repetition, 255-256
severity level, 29
skew, 48-49
solve-before, 137
srandom(), 109, 140
static
class method, 84
class property, 83
std, 107
step (time literal), 25
struct
vs. class, 79
string, 21-24
svdpi.h, 211-215, 219-220, 222, 224-225
synchronous drive, 53
T
task
context, 247-248, 252
Transaction Level Modeling (TLM), 58-72
transaction, 60
transaction based verification (TBV), 61
transaction verification model (TVM), 61
transactor, 61
as interface, 63-68
benefits, 69
transactor, 62-66
using interface, 63-66
triggered (event property), 190
type_option, 159-160
U
us (time literal), 25
V
virtual
class, 93
interface, 67-68
v
This document is for the sole use of Sundaresan Chidambaram of Manipal University
vi