Specman e Reuse Methodology - Developer's Manual
Specman e Reuse Methodology - Developer's Manual
Specman e Reuse Methodology - Developer's Manual
(eRM) Developer
Manual
Version 4.3.5
Legal Notice
Copyright © 2002-2004 Verisity Design, Inc. All rights reserved.
Trademarks
Verisity, the Verisity logo, eAnalyzer, eCelerator, eRM, Invisible Specman, LicenseE,
Pure IP, Specman, Specman Elite, SpeXsim, SpeXtreme, SureCov, SureLint, SureSolve,
sVM, Verification Advisor, Verification Alliance, Verification Vault, Verification
Viewport, Visualization Toolkit, vManager, vPlan, Xbench, Xchange, Xcite, XoC, Xpert,
Xsim, and Xtreme are either trademarks or registered trademarks of Verisity Design, Inc.
in the United States and/or other jurisdictions. All other trademarks are the exclusive
property of their respective owners.
Confidentiality Notice
Verisity confidential; do not distribute. The contents of this document constitute valuable
proprietary and confidential property of Verisity Design, Inc. No part of this information
product may be reproduced, transmitted, or translated in any form or by any means,
electronic, mechanical, manual, optical, or otherwise without prior written permission
from Verisity Design, Inc.
Information in this product is subject to change without notice and does not represent a
commitment on the part of Verisity. The information contained herein is the proprietary
and confidential information of Verisity or its licensors, and is supplied subject to, and may
be used only by Verisity’s customers in accordance with, a written agreement between
Verisity and its customers. Except as may be explicitly set forth in such agreement,
Verisity does not make, and expressly disclaims, any representations or warranties as to the
completeness, accuracy, or usefulness of the information contained in this document.
Verisity does not warrant that use of such information will not infringe any third party
rights, nor does Verisity assume any liability for damages or costs of any kind that may
result from use of such information.
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1-1
1.1 About This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-1
1.2 About the eRM Release Library (erm_lib) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-2
1.3 About eVCs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-3
1.3.1 What Are eVCs? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-3
1.3.2 eVCs vs. Regular Verification Environments (VEs) . . . . . . . . . . . . . . . . . . . . 1-4
1.3.3 eVCs as Plug-and-Play Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-5
1.3.4 eVC Reuse Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-5
1.4 Conventions in This Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-6
iv e Reuse Methodology
Contents
e Reuse Methodology v
Contents
vi e Reuse Methodology
Contents
6 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6-1
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-1
6.1.1 Messaging Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2
6.1.2 Messaging Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-3
6.1.3 Message Logger Struct Deprecation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-4
6.1.4 About This Chapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-5
6.2 Basic Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-6
6.2.1 Basic Message Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-6
6.2.1.1 Example Message Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-6
6.2.1.2 Format of Message Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-6
6.2.1.3 Message Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-7
6.2.2 Basic Message Loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-8
6.2.2.1 message_logger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-8
6.2.2.2 sys.logger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-8
6.2.2.3 Message Tags and Message Destinations . . . . . . . . . . . . . . . . . . . . . 6-9
6.2.2.4 How Loggers Relate to Message Actions . . . . . . . . . . . . . . . . . . . . 6-10
6.2.2.5 Multiple eVCs with Loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-11
6.2.2.6 Configuring Loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-12
6.2.3 Basic Messaging Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-13
6.2.3.1 How eVC Writers Configure Messaging . . . . . . . . . . . . . . . . . . . . . 6-13
6.2.3.2 How Integrators Configure Messaging . . . . . . . . . . . . . . . . . . . . . . 6-15
6.2.3.3 How Users Configure Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . 6-15
e Reuse Methodology ix
Contents
x e Reuse Methodology
Contents
e Reuse Methodology xi
Contents
9 Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9-1
9.1 Supporting Multiple Resets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-1
9.1.1 Reset Assumptions and Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-1
9.1.1.1 Primary Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-1
9.1.1.2 Secondary Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-2
9.1.2 Reset Methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-2
9.1.3 rerun() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-4
9.1.4 Reset Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-5
9.2 Instance Names and IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-6
9.2.1 About Instance Names and IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-6
9.2.2 Enumerated Logical Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-7
9.2.3 Scalar IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-8
9.2.4 Use of Instance Names and IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-8
9.2.5 Instance Names: XBus Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-9
9.2.5.1 eVC Developer Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-9
9.2.5.2 eVC User Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-11
9.3 Adding Scoreboards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-12
9.3.1 What Is a Scoreboard? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-12
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Index-1
Most of the guidelines herein apply to any scale or type of verification environment. Since e Verification
Components (eVCs) are an ultimate form of reusable verification environment, the majority of this book
relates to creating eVCs.
A key factor for reusing code is arranging it as an independent and easy-to-use code package. When
developing verification code in e, the reusable package is typically organized as an eVC (e Verification
Component). For this reason, eRM speaks a lot about eVCs: standardizing eVC development practices,
defining a common eVC user model, and ensuring that eRM-compliant eVCs are plug-and-play. In
short, this manual also explains how to develop verification components that will provide eVC users
with consistent and superior verification experiences.
The intended audience for this book includes eVC developers, verification environment developers, and
technical managers responsible for these environments.
evc_util This package includes the infrastructure syntax, types, and utilities needed
for writing eVCs. It is the core module of the eRM library.
Documents This directory contains all of the main eRM documentation. It also contains
(in the erm_docs eRM presentations in PDF format.
directory)
Golden eVCs These are example eVC packages, constructed according to eRM
guidelines. They are designed to teach eRM methodology and concepts and
to provide a starting point for eVC development. Three examples are
included. One represents a bus-based environment (vr_xbus), one represents
a serial data stream environment (vr_xserial), and one represents an SoC
environment that incorporates vr_xbus and vr_xserial (vr_xsoc). You can
find the three golden eVCs in directories with their respective names
(vr_xbus, vr_xserial, vr_xsoc).
Example These are minipackages that demonstrate use of some specific features
directories provided by the eRM library, such as sequences and packaging. They all
have an ex_ prefix (for example, ex_atm, ex_soc, ex_c_bus).
Labs This directory includes labs designed to teach eRM.
(in the lab directory)
vt_util This is the eRM Utility package.
Shareware These packages offer useful functionality. They are, however, often
packages provided at a lower level of productization and support than the official
utilities. The shareware packages are all marked with a shr_ prefix (for
example, shr_install).
Templates This directory contains templates for eVC user guides (in FrameMaker and
(in the templates MS Word formats) and for eVC training classes (in PowerPoint format).
directory)
Each of these packages and subdirectories has its own PACKAGE_README.txt or README.txt file
in it, giving details on the content.
Each eVC consists of a complete set of elements for stimulating, checking, and collecting coverage
information for a specific protocol or architecture. You can apply the eVC to your device under test
(DUT) to verify your implementation of the eVC protocol or architecture. eVCs expedite creation of a
more efficient test bench for your DUT. They can work with both Verilog and VHDL devices and with
all HDL simulators that are supported by Specman ®.
You can use an eVC as a full verification environment or add it to a larger environment. The eVC
interface is viewable and thus can be the basis for user extensions. We recommend doing such
extensions in a separate file. Maintaining the eVC in its original form facilitates possible upgrades.
eVC implementation is often partially encrypted, especially in commercial eVCs where authors want to
protect their intellectual property. Most commercial eVCs require a specific feature license to enable
them.
This manual takes a fairly liberal approach to the question of what is an eVC. For the purposes of this
manual, an eVC is a significant, productized, modular piece of e code that can be used to verify
something and that has a single owner who is responsible for it (supporting it and possibly selling it).
Notes
• An eVC can depend on another eVC. For example, there could be a TCP/IP eVC that uses (imports)
an Ethernet eVC. That TCP/IP eVC could still be developed and sold separately.
• An eVC must be significant and productized. These are not exact terms, but clearly a 200-line
scoreboard module does not qualify as an eVC. eVCs are usually 5,000 to 20,000 lines in size, and
they embody significant knowledge and work (so that people would be willing to pay money for
them).
Regardless of whether the combined VE is itself an eVC or not, when we integrate n eVCs into a VE, we
have n+1 pieces: the n eVCs and the explicitly added code for the VE. The plug-and-play considerations
described below relate to all n+1 items.
For example, there should be no name collisions between eVCs, but there should also be no name
collisions between any of the eVCs and the VE. Therefore names like pci_checks.e would not be
appropriate, because users might want them too.
Note The recommendations in this manual represent the best practices for those using Specman .
Therefore, they apply not just to eVCs but also to regular VEs. For example, unified tracing (as is
recommended for plug-and-play eVCs) is very useful when applied to blocks within a big VE, even if
neither the blocks nor the VE are ever meant to be reused.
To enable this capability, eVCs (and indeed general VEs, which could potentially be turned into eVCs)
should be written as if they are part of a universal verification environment.
Note All of the following recommendations are described in much greater detail throughout the rest of
this manual.
Typeface Represents
courier font Indicates code. For example:
do burst_response keeping {
courier bold Used to highlight important sections of code, like actions. For example:
do burst_response keeping {
bold The bold font indicates keywords in descriptive text. For example, the
following sentence contains keywords for the show ini command and the
get_symbol() routine:
You can display these settings with the show ini setting command or
retrieve them within e code with the get_symbol() routine.
italic The italic font represents user-defined variables that you must provide.
For example, the following line instructs you to type the “write cover” as
it appears, and then the actual name of a file:
[ ] square brackets Square brackets indicate optional parameters. For example, in the
following construct the keywords “list of” are optional:
[ ] bold brackets Bold square brackets are required. For example, in the following construct
you must type the bold square brackets as they appear:
Typeface Represents
vrst-tool> Denotes the prompt for the Verisity tool you are running, including
Specman Elite, vManager, SpeXsim, or SpeXtreme.
C1>, C2>, … Denotes the SpeXsim prompt (VHDL, Verilog or mixed-HDL designs).
Also denotes a third-party Verilog simulator prompt.
Each reusable verification component should be packaged in its own directory, which:
• Environment packages
These are packages that define an env root unit (a child of any_env, see below), which can be
instantiated in your verification environment.
Env packages can be divided further into eVC packages and shareware env packages, such as a
small ATM shareware.
• Utility packages
These are packages that do not define a unit to be instantiated but rather are helpful utilities that
supply various services, for example, a visualization utility.
Utility packages also divide into shareware utilities and officially supported utilities. One specific
officially supported utility package is the evc_util package, which should be imported by each eVC
(see “evc_util Package” on page 2-4).
There may be other kinds of packages. ESI adaptors, CE tools, and more may end up being bundled as
packages, thus enjoying the various facilities packages have (such as an easy way to see their
names/titles/versions, standard installation procedure, and so on).
Each package has a package name. Because we want to mix and match packages from various of
sources, we should strive to make package names unique. (Two packages cannot be loaded together if
they have the same name.)
We use the notation package to denote the package name (for example, “vr_xbus”), and PACKAGE to
denote the uppercase version of it (for example, “VR_XBUS”).
By convention, all e file names in the package should start with the package name. For example, the top
file could be:
package_top.e // for example, vr_xbus_top.e
Furthermore, all types should start with the package name. For example:
package_cell
Note Fields in package-defined structs are not recommended to start with the package name. For
example:
type vr_xbus_kind: [x, y, z];
struct vr_xbus_transaction {
kind: vr_xbus_kind;
};
The company-prefix is a two to four letter prefix, such as “vr” for Verisity and “arm” for ARM. We
suggest contacting erm_admin@verisity.com to make sure your preferred prefix is unique.
The proper-name is the name of the corresponding protocol, and so on, for example, “atm”.
We recommend that the package name be all lowercase. It should follow the same convention as a name,
that is, start with a letter and contain only letters, digits, and underscores.
Some prefixes have special meaning. Table 2-1 on page 2-4 lists some general reserved prefixes.
shr Shareware that does not use a company name prefix. For example, if a user wants to
donate a register creation package as shareware, s/he can call it “shr_register”.
When installed, packages reside in library directories (libraries, for short) and must contain a file called
LIBRARY_README.txt. Libraries are then named in the $SPECMAN_PATH. Thus, a library is any
directory in your $SPECMAN_PATH that contains a file called LIBRARY_README.txt.
/usr/joe/private_lib/
LIBRARY_README.txt /project/specman/proj_lib/
LIBRARY_README.txt /cad/evc_lib/
co_atm/ LIBRARY_README.txt
vr_ahb/ shr_visualizer/
PACKAGE_README.txt PACKAGE_READM PACKAGE_READM evc_util/
demo.sh E.txt E.txt PACKAGE_READM
e/ demo.sh demo.sh E.txt
docs/ e/ e/ demo.sh
examples/ docs/ docs/ e/
verilog/ examples/ examples/ docs/
... misc/ examples/
In this example, $SPECMAN_PATH contains three package libraries: a private library, a project-wide
library, and a company-wide library. These libraries will be searched in that order (as is usually the case
with $SPECMAN_PATH).
/project/specman/proj_lib
• LIBRARY_README.txt
• vr_ahb/
• PACKAGE_README.txt
• demo.sh
• e/
• docs/
• examples/
• misc/
• evc_ve
• vr_pci/
• PACKAGE_README.txt
• demo.sh
• e/
• docs/
• examples/
• misc/
• evc_ve
• ...
/usr/joe/private_lib/
• LIBRARY_README.txt
• shr_atm/
• PACKAGE_README.txt
• demo.sh
• e/
• examples/
• shr_visualizer/
• PACKAGE_README.txt
• demo.sh
• e/
• …
Using the example $SPECMAN_PATH as described above, if you type the following, in Specman :
load shr_atm/e/shr_atm_top.e
Note If multiple libraries in your $SPECMAN_PATH contain the same package name, then only the
first one in $SPECMAN_PATH is used. Use the show package -on_disk -full command to display
redundant packages.
• LIBRARY_README.txt file
This file must exist. By convention, it contains a description of the library on a single line, as
follows:
* Title: This is the project-wide shareware library.
Note that this construction is similar to the title line of the PACKAGE_README.txt file described
below.
• Various package directories
The package directories, one per package, are described in “Package Directory” below.
• A versions directory
By convention, a directory called versions should exist in a library directory. Whenever a new
version of the package arrives, it is first untarred as a subdirectory of the versions directory and
then copied under the package name to the library directory.
For a detailed description of this file, see “PACKAGE_README.txt File” on page 2-19.
To have the demo run, you should be able to type the following command from any work directory:
% 'sn_which.sh package/demo.sh'
Usually, there will be several examples in the package/examples directory. demo.sh should be able to
run one of them (perhaps loading an e file called demo.e).
Note demo.sh can use the predefined sn_which.sh shell script (see “sn_which.sh Shell Script” on page
2-11) to avoid setting the $PATH environment variable (assuming $SPECMAN_PATH is set correctly to
go through the desired library directories).
2.3.2.3 e Directory
This directory contains all essential e code files belonging to the package. These could all reside flatly in
this directory, or be organized in subdirectories within the e directory as needed.
The e directory should contain the package_top.e file, which imports the other necessary files. It can
either import all necessary files, or it can import other top files of the package that, in turn, import all
necessary files.
The package writer must decide whether to have only downward imports (that is, from top files to other
files) or to have each file import all files it depends on.
See “Accessing Files” on page 2-10 for an explanation of how to write the actual import actions.
If an example consists of more than one file, it may make sense to create subdirectories for each
example.
Examples should import package files using the package-relative notation to make the package usable
from any location. For example, the file test1.e in the foo package might look like this:
<'
import foo/e/foo_top;
extend foo_packet {
...
};
'>
See “Using Package-Relative File Names” on page 2-10 for more information.
Notes
• Files and structs in the examples directory do not have to follow the naming conventions. They are
not really part of the package e code.
• The demo.sh file described in “demo.sh File” on page 2-8 does not reside in the examples directory,
but normally it imports files from the examples directory.
Note If the Verilog files are part of a particular example, they should be under that example in the
examples/ directory.
@ex_atm/load.ecom
This is different from the previous way of organizing eVCs. Previously, you had to set your
$SPECMAN_PATH for source and doc directories of each eVC you used. This meant that you had to
change $SPECMAN_PATH whenever you wanted to use a new verification component. This could be
tedious if you wanted to explore many utilities or pieces of shareware.
Using package-relative names, any file in any library in your $SPECMAN_PATH is equally accessible.
This means that you can use a new eVC by including it in a library. Use the show packages -on_disk
command to see the packages that can be loaded.
You can put the new version of the package foo under private_lib, and it will be found first. (The other
version of foo later in the $SPECMAN_PATH is “shadowed” by the first one and is not used.)
Then, it will find the old bar, unless you also put a newer version of bar in your private_lib.
Importing files within the same directory tree (for example, the e directory) can be done either using
package-relative names, or (more conventionally) assuming the directory of the importing file. Thus, the
file bar/e/bar_top.e could import files as follows:
import xx; // xx.e is in same directory as importing file
import yy; // yy.e is in same directory as importing file
import checks/cell_check; // cell_check.e is in e/checks/
Note Never use the “../xx” notation for relative pathnames.
“sn_which.sh file” looks for file in (and only in) your $SPECMAN_PATH and writes the full path name
to stdout. If file is not found, an error message is written to stderr. For example, “sn_which.sh foo/bar.x”
would print “/usr/me/shareware/foo/bar.x”, assuming “/usr/me/shareware” is in your
$SPECMAN_PATH and this is the first such match.
cc `sn_which.sh foo/c_files/xx.c`
Note sn_which.sh is limited to searching for files in your $SPECMAN_PATH. It ignores all other
features like the Specman specman -pre_command.
define VR_XBUS_VERSION_0_2;
Note For each major release, use the OR_LATER statement in the define. For example:
define VR_XBUS_VERSION_3_0_OR_LATER;
When you define several major releases, the OR_LATER defines build up. Thus, vr_xbus_top.e might
look as follows:
vr_xbus_top.e - Top file for the vr_xbus package
<'
define VR_XBUS_VERSION_3_1;
define VR_XBUS_VERSION_2_OR_LATER;
define VR_XBUS_VERSION_3_OR_LATER;
import evc_util/e/evc_util_top;
import vr_xbus_types;
...
'>
1. Make sure that the information in your PACKAGE_README.txt file and any other documentation
such as release notes or user guide is up to date. In particular, pay attention to the data following the
“Version” and “Modified” headers.
2. Pack the new version of your package, using the approach in either “Simple Method to Pack the
Version” on page 2-14 or “Robust Method to Pack the Version” on page 2-14.
Note There is an install/release scripts package in the shr_install/docs directory of the erm_lib.
You can use these scripts to automate the processes of releasing and installing packages.
3. Distribute the new version of your package using whatever means you prefer, such as by uploading
it to your FTP site or emailing it to users.
Versions Directory
By convention, you should have a directory called versions under the library directory. Whenever a new
version of the package arrives, it is first untarred as a subdirectory of the versions directory and then
copied under the package name to the library directory.
1. Copy the directory containing one or more packages into the corresponding versioned directory
(called, for example, package_version_num_num). For example:
% cp -r vr_xbus ../versions/vr_xbus_version_3_1
Note We suggest the convention of putting the versioned directory in the “versions” subdirectory
of the library.
2. Tar that versioned directory from the directory just above it, and gzip the result. For example, if the
package you want to make resides in somepath/versions/vr_xbus_versions_3_1:
% cd somepath/versions
% tar xvf vr_xbus_version_3_1.tar vr_xbus_version_3_1
% gzip vr_xbus_version_3_1.tar
1. Save the gzipped, tarred file to the “versions” directory of the library to which you want to add it.
3. Copy the versioned directory to the library under the package name. For example.
% cd ~/my_lib
% cp -r versions/vr_xbus_version_3_1 vr_xbus
Alternatively, you can create a symbolic link from your library to your versions directory rather
copying the directory.
Assuming that library is in your $SPECMAN_PATH, you can now run the demo from anywhere.
For example:
% cd ~/work
% ~/my_lib/vr_xbus/demo.sh
evc_util 0.7
vr_xbus .. 1.1
vr_xserial 0.9 ..
vt_util 0.5 .. 1.2
Note If the version field of a required package is left empty, any version will be accepted.
For an explanation of the dependency syntax, see “Version Numbers and Ranges” below.
To specify a list:
• Separate the list members with commas and contain them all inside brackets.
For example:
{2.5,3.0 .. 3.12,4.0 ..} //Only the specified versions and ranges
Note Range lists can include both specific versions and ranges of versions.
The Package Compatibility Analyzer runs automatically as part of the evc_util package. It can run in
either interpreted or compiled mode. You can also disable it.
Table 2-2 describes the notifications generated by the Package Compatibility Analyzer.
Default
Condition Notification Severity
All notifications include the required version and the path to the PACKAGE_README.txt file that
specifies the compatibility requirement. In Specview, the path to the PACKAGE_README.txt file is
also a hyperlink.
Notification Examples
*** Warning: WARN_SPECMAN_VER_INCOMP: Package ex_soc is incompatible with
Specman version 4.2
Requirement: Specman 4.3
See: file:/erm_lib/ex_soc/PACKAGE_README.txt
4.1b3
Requirement: Specman 4.0 ..
See: file:/erm_lib/evc_util/PACKAGE_README.txt
Header Format
The format of headers should be
* header-name: text
Or:
* header-name:
.... Lines of texts extending until the next header ...
All header lines start with an asterisk (*). The header name is always followed by a colon (:).
A header line can contain text in addition to the header or it can contain only the header.
Headers are considered to be case- and blank-insensitive, but we recommend following the examples in
these respects.
Mandatory Headers
The following headers just appear in the PACKAGE_README.txt file:
• Title
• Name
• Version
If any of these headers are missing or empty or if the value of Name does not match the package
directory name, the PACKAGE_README.txt is considered invalid. (See “Checking Package Legality”
on page 2-22.)
Standard Headers
The PACKAGE_README.txt file should contain standard headers. Verisity predefines the following
headers:
• Title
• Name
• Version
• Category
• Modified
• Support
• Comments to
• Documentation
• Description
• Installation
• Release notes
• To demo
• Requires
PACKAGE_README.txt Examples
Following are sample PACKAGE_README.txt files.
Note Comments starting with “--” are metacomments. They should not appear in the file.
* Name: vr_ahb_evc
-- Must be the same as the package name
* Version: 3.3
-- Can also be e.g.: 0.1 (Experimental Version)
* Modified: 14-Jul-2001
-- Please use dates in exactly that format
* Category: eVC
-- Can be eVC, shareware, or utility for now
* Support: evc_support@verisity.com
-- Where to send requests for support, questions, etc..
* Documentation: docs/user_man.pdf
-- File name containing the documentation
-- Note: File names should be specified relative to the
-- package dir
* Description:
* Directory structure:
e/ - All e sources
...
...
...
* Installation:
To install it:
....1.
....2.
In this example there is no separate documentation directory. In fact, the whole shareware consists of a
directory that contains this PACKAGE_README.txt file and a sub-directory called e/ with a single .e
file.
------------ Start of PACKAGE_README.txt file ---
* Title: A utility to print your unit hierarchy
* Name: vr_hier_unit
* Modified: 13-Nov-2001
* Category: Shareware
* Description:
2. Load vr_hier_unit/e/vr_hier_unit_top.
/any_lib/any_package/e/any_package_top.e
Finally, each any_env in the unit tree is matched with the package in which it is defined. If it is not
defined in any package, this is regarded as an error.
Note The defining package of an any_env unit is used in some of the env's methods. See “any_env Unit”
on page 2-23.
For example:
unit vr_xbus_env like any_env {
...
};
Thus, any_env acts like a super-unit, encapsulating the whole verification component.
Each specific any_env definition (such as the vr_xbus_env above) should occur inside some package,
and that package is automatically associated with that env.
At the end of generation of sys, Specman shows a banner for each any_env instance. By default, this
shows the title and version number of the corresponding package, but this can be changed by the eVC
writer (see below).
Thus, the log file contains information about which eVCs (and shareware components), and specifically
which versions, participated in the run.
-------------- vr_xsoc_env_u-@0
The Verisity XSoc Verification Environment - version 0.2 (experimental)
(c) Verisity 2002 XSoc
instance : ENV
-------------- E path: sys.xsoc_env
-------------- vr_xbus_env_u-@1
The Verisity XBus eVC - version 0.5 (experimental)
(c) Verisity 2001
Bus : XBUS
1 eVC-supplied masters
0 shadowed DUT masters
0 eVC-supplied slaves
1 shadowed DUT slaves
eVC-supplied arbiter
-------------- E path: sys.xsoc_env.xbus_evc
-------------- vr_xserial_env_u-@2
The Verisity XSerial eVC - version 0.5 (experimental)
(c) Verisity 2002
eVC instance : XSERIAL_A_RX
has RX ACTIVE agent
-------------- E path: sys.xsoc_env.xserial_A_evc
-------------- vr_xserial_env_u-@3
The Verisity XSerial eVC - version 0.5 (experimental)
(c) Verisity 2002 eVC instance : XSERIAL_B_RX
has RX ACTIVE agent
-------------- E path: sys.xsoc_env.xserial_B_evc
<'
unit any_env {
show_banner() is empty;
// Print the banner for this instance of the env.
// By default, shows a single line, with get_title() and get_version().
// User can modify it, adding e.g. copyright notice and a short
//description
// of the configuration of this instance. We suggest indenting all the
// extra information by four spaces.
show_status() is empty;
// Print the current status of this instance of the env.
// By default, empty. To be filled by the user.
add_wave_info() is empty;
// Add waveform info for that package.
// By default, empty. To be filled by the user.
};
'>
There can be several related children of any_env in one package. If they need different titles, that could
be one situation where you would want to override the default implementation, in this case, of
get_title().
This shows all currently loaded packages. If the current simulation environment contains instances of
any_env defined in the package, then the banner of those instances is also shown.
If -full is specified, the status of each instance is also shown (as specified by any_env.show_status()).
If wildcard-name is specified, only packages with package name matching the wildcard name are
shown. For example, you could show all shareware packages as follows:
show packages shr_*
This shows all the current packages in libraries along the $SPECMAN_PATH.
If -full is specified, you get information about illegal packages along the path.
If wildcard-name is specified, only packages with package name matching the wildcard name are
shown.
Note The experimental vt_util package (still beta) also gives a GUI way to see loaded packages and
packages on disk. (For more information, see “The vt_util Windows” in The vt_util Package located in
$SPECMAN_HOME/erm_lib/vt_util/docs/vt_util.pdf.)
0. Package: evc_util
Version: 0.3
Comments to: support@verisity.com
1. Package: vr_xserial
Version: 0.5 (experimental)
Comments to: support@verisity.com
Title: The Verisity XSerial eVC
See: file:/cad/evc_lib/vr_xserial/PACKAGE_README.txt
-------------- vr_xserial_env_u-@2
The Verisity XSerial eVC - version 0.5 (experimental)
(c) Verisity 2002
eVC instance : XSERIAL_A_RX
has RX ACTIVE agent
-------------- E path: sys.xsoc_env.xserial_A_evc
-------------- vr_xserial_env_u-@3
The Verisity XSerial eVC - version 0.5 (experimental)
(c) Verisity 2002
eVC instance : XSERIAL_B_RX
has RX ACTIVE agent
-------------- E path: sys.xsoc_env.xserial_B_evc
2. Package: shr_ram
Version: 0.1
Comments to: support@verisity.com
Title: Sparse RAM Model (shareware)
See: file:/cad/evc_lib/shr_ram/PACKAGE_README.txt
3. Package: vr_xbus
Version: 0.5 (experimental)
Comments to: support@verisity.com
Title: The Verisity XBus eVC
See: file:/cad/evc_lib/vr_xbus/PACKAGE_README.txt
-------------- vr_xbus_env_u-@1
The Verisity XBus eVC - version 0.5 (experimental)
(c) Verisity 2001
Bus : XBUS
1 eVC-supplied masters
0 shadowed DUT masters
0 eVC-supplied slaves
1 shadowed DUT slaves
eVC-supplied arbiter
-------------- E path: sys.xsoc_env.xbus_evc
4. Package: vr_xsoc
-------------- vr_xsoc_env_u-@0
The Verisity XSoc Verification Environment - version 0.2 (experimental)
(c) Verisity 2002
XSoc instance : ENV
-------------- E path: sys.xsoc_env
eRM allows for shipping whole libraries. Nevertheless, we do have a few recommendations:
• This is not to be confused with the situation of receiving packages from multiple vendors, in
which case multiple packages can be placed in a common library. For examples, you might buy
eVCs from multiple vendors and place them all in one library called “commercial_evcs”.
For example, suppose you define a new shareware package that draws graphs (call it shr_graph), with a
main struct called shr_graph. You can instantiate it under sn_util with the following code in
shr_graph_top.e:
extend sn_util {
shr_graph: shr_graph;
init() is also {shr_graph = new};
};
As a minimum, we recommend placing the following three subdirectories at the top level of the eVC
directory.
evc/e/ This directory should contain all source code for the eVC.
evc/examples/ This directory should contain examples of how to use the eVC.
evc/docs/ This directory should contain all documentation for the eVC.
Other subdirectories may be appropriate in some cases. For example, with an eVC that is designed to
facilitate integration of a design block, a subdirectory named evc/rtl/ might be used to contain the RTL
code (in VHDL or Verilog) for the design block.
3.1.1 evc/e/
All eVC source files should be placed under evc/e/. As a minimum, an eVC must have a file named
evc/e/evc_top.e. This file should import all other files required for correct loading of the eVC. As such,
users of an eVC can load it by placing the following line at an appropriate point in their code.
import evc/e/evc_top;
evc/e/evc_top.e
evc/e/evc_configuration_a_top.e
evc/e/evc_configuration_b_top.e
or:
evc/e/evc_top.e
evc/e/evc_master_top.e
evc/e/evc_slave_top.e
Note There must still be a file named evc/e/evc_top.e to satisfy the requirements for packaging.
Source Subdirectories
For larger eVCs, we recommend that source files be split into subdirectories under the evc/e/ directory.
Each subdirectory should contain an appropriately named _top.e file that is imported by the
evc/e/evc_top.e.
Subdirectory names should be short and should describe an area of the eVC (usually a subcomponent or
aspect of the eVC). For example (showing the top level files for each subdirectory):
evc/e/master/evc_master_top.e
evc/e/interrupts/evc_interrupts_top.e
evc/e/tcp/evc_tcp_top.e
evc/e/coverage/evc_coverage_top.e
Notes
• We recommended that the /evc/e subdirectory names do not include the evc_ prefix.
• All files in an eVC release must have unique names. If there are multiple references to the same file,
the file is loaded only once. If there is an attempt to load a file with the same name but different
location as an already loaded file, Specman issues an error message.
3.1.2 evc/examples/
Sample config and test files should be placed in evc/examples/. As a minimum, this should include:
• A template configuration file that users can use as a starting point for building an eVC configuration
• A fully working demonstration/example of the use of the eVC
Users should be able to run the full demonstration (including creating/compiling simulator libraries, and
so on) by sourcing a script named demo.sh. This script should be placed at the top level of the eVC
directory structure (for example, evc/demo.sh).
3.1.3 evc/docs/
All documentation for the eVC should be placed under evc/docs/. This normally includes both release
notes and a user guide. Where documents change according to the version of the eVC, we recommend
indicating the current version as part of the filename. For example:
evc/docs/evc_release_notes_version_1_2.pdf
evc/docs/evc_user_guide_version_1_2.pdf
Where a large number of documents are provided, these should be placed in an appropriate subdirectory
structure under evc/docs/.
Although a number of different documentation formats are appropriate for eVC documentation, we
recommend providing documentation in TXT or PDF formats as these are likely to be readable by the
largest number of users.
Guideline Details
Standard names for feature files An appropriate name of the feature, for
example, checker, cover, monitor, sequence
Natural domain names for entities Examples are master, slave, burst
Table 3-2 Files Owned by eVC Developer (users should not modify)
evc_monitor.e Definition of central monitor unit, when relevant (for example, buses)
evc_agent_monitor.e Definition of agent monitor unit, when relevant (for example, serial i/f)
Table 3-2 Files Owned by eVC Developer (users should not modify) (continued)
evc_checker.e Protocol and data checks. Can be divided into agent/topic files
vr_xserial vr_xbus
vr_xserial_types; vr_xbus_types;
vr_xserial_env; vr_xbus_env;
vr_xserial vr_xbus
vr_xserial_agent; vr_xbus_agent;
vr_xserial_bfm; vr_xbus_bfm;
vr_xserial_bfm_tx; vr_xbus_bfm_arbiter;
vr_xserial_bfm_rx; vr_xbus_bfm_master;
vr_xserial_monitor; vr_xbus_bfm_slave;
vr_xbus_monitor;
vr_xserial_frame; vr_xbus_trans;
vr_xserial_coverage; vr_xbus_protocol_checker
vr_xserial_log; vr_xbus_log;
vr_xserial_sequence_tx; vr_xbus_coverage;
vr_xserial_sequence_rx; vr_xbus_master_sequence;
vr_xserial_protocol_checker vr_xbus_slave_sequence;
vr_xserial_top; vr_xbus_top;
vr_xserial_config_template vr_xbus_config_template
test_1 test_1
An eVC should never instantiate anything under sys. This is important because an eVC may later be
instantiated under a larger eVC. Instead, all subcomponents of the eVC should be instantiated under a
unit named evc_env_u. The user’s evc_config.e file will then instantiate evc_env_u at an appropriate
point or points in the user’s verification environment.
To promote reuse of code within an eVC, structs and units should not be instantiated in the file that
declares them. For example, if the file evc_master.e declares a unit evc_master_u, then no instance of
this unit will be created in this file. The instance of the unit evc_master_u might be created in the file
evc_env.e.
Additionally, any file can optionally import other files on which it depends. This can be useful for
documenting dependencies and solving cyclic dependencies.
All imports in eVC code are performed using either local or package-relative filenames:
• Local filenames refer to files in the same directory as the importing file or in a subdirectory below
it. For example:
import evc_another_file.e; // File in same directory
import ./sub_dir/evc_another_file.e; // File in subdirectory
Note Imports within a package should be local (as much as possible). This is the safest way to
make sure that the package will be loaded from one location, even if you maintain multiple versions
of the package.
Note “../” should never be used as part of an import statement, because this can lead to undesired
results if there are symbolic links in your directory path. For example:
import ../evc_another_file.e; // This kind of path specification
// should not be used
• eVC-relative filenames refer to files relative to the top-level of the eVC. For example:
import evc/e/evc_top.e; // Import the top level file of the eVC
import evc/e/evc_env.e; // Import file that declares env unit
Note Imports of other eVCs or utilities are always performed using eVC-relative filenames. This
is to make sure that the package is found, regardless of the locations of the importing file and the
imported package (SPECMAN_PATH takes care of it all). For example:
import evc_util/e/evc_util_top.e; // Import the evc_util utility
In such cases, files may be organized into clusters of related files. A cluster should be loaded at one time,
allowing tighter dependencies between the files that make up the cluster. Within a cluster, some of the
guidelines on file organization can be relaxed (out of practicality). For example:
• Units can instantiate themselves in other units declared within the cluster where the relationships
between the units in the cluster is fixed by the eVC.
• Files can depend cyclically upon each other.
Several clusters can exist within an eVC, and the eVC as a whole can be considered as a cluster. Where
a file cluster is used, we recommend placing it in a subdirectory with a _top.e file. The evc_top.e file can
then import the file cluster by importing its _top.e file.
This situation can be solved using the cyclic import feature in Specman . If evc_env.e imports
evc_agent.e, and evc_agent.e imports evc_env.e, then Specman detects the cyclic dependency between
them and loads both files together as if they were one file. These files are considered part of the same file
cluster. We recommend that the appropriate _top.e file imports both files.
Notes
• Mutually extending structs/units in cyclic files might introduce problems.
• We recommend keeping the use of cyclic imports to a minimum. When there are many cyclic
dependencies, it leads to large clusters that can be difficult to manage.
Example
user_config.e
<'
import top;
extend sys {
env: env_u is instance;
};
'>
env.e
<'
import agent;
unit env_u like any_env {
agent.e
<'
import env;
unit agent_u {
env_bp: env_u;
};
'>
top.e
<'
import env;
import agent;
'>
See Also
• “An Alternative Approach to Cyclic Dependencies” on page 3-9
A similar approach can be used to solve cyclic method dependencies. Using this approach, you declare
the methods as empty in an initial file, then extend the methods to add the method bodies in subsequent
files.
Example
units.e
<'
unit env_u like any_env {};
unit agent_u {};
'>
agent.e
<'
import units;
extend agent_u {
env_bp: env_u;
};
'>
env.e
<'
import units;
import agent;
extend env_u {
agent: agent_u is instance;
keep agent.env_bp == me;
};
'>
top.e
<'
import units;
import agent;
import env;
'>
Because each eVC relates to different protocols, architectures, and designs, one eVC can vary quite
significantly from another. Even so, there is commonality in the design of various eVCs. In this chapter,
we discuss the commonality and some of the main differences between typical eVCs. The examples we
look at relate to communication protocols, such as a serial interface or an on-chip bus. Later, we will see
that an eVC for an SoC is constructed according to similar guidelines.
The end of this chapter lists extensive terminology definitions and a complete legend for the architecture
diagrams presented in this book.
DUT
Bus Logic Serial Logic
This DUT has two external interfaces: a bus interface and a serial interface. Because each of these
interfaces can interact externally, we can attach an eVC to exercise and interact with each of them.
We start by looking at the serial interface. This interface is composed of a receive port and a transmit
port. The eVC attached to this interface is the vr_xserial eVC. The dual-agent implementation for the
XSerial eVC is shown in Figure 4-2 below. (The actual implementation of the XSerial eVC is slightly
different. It is shown in Figure 4-3 on page 4-4.)
xserial_env
RX Agent TX Agent
Config:
... Config Config
Sequence Sequence
Driver Driver
seq seq
DUT
Bus Logic Serial Logic
The XSerial eVC is encapsulated in the rectangle marked “xserial_env”. This rectangle represents an
instance of a unit of type vr_xserial_env_u (which inherits from any_env, as described in “any_env Unit”
on page 2-23). For each port of the interface, the eVC implements an agent. These agents can emulate
the behavior of a legal device, and they have standard construction and functionality. Each env also has
a group of fields, marked in Figure 4-2 as Config. This allows for configuration of the env’s attributes and
behavior.
RX agent A receive agent that can collect data from the DUT’s transmit port
TX agent A transmit agent that can send data to the DUT’s receive port
These agents are constructed in a standard manner. They have the following components:
Config A group of fields that allow configuration of the agent’s attributes and
behavior
Signals A group of unit members that represent the HW signals that the agent must
access as it interacts with the DUT. Currently, the signals are implemented
as string fields, all prefixed with “sig_”.
Sequence Driver A unit instance that serves as a coordinator for running user-defined test
scenarios (implemented as sequences). The sequence drivers are explained
in detail in Chapter 5 “Sequences: Constructing Test Scenarios”.
BFM Bus Functional Model—a unit instance that interacts with the DUT and both
drives and samples the DUT signals.
Monitor A unit instance that passively monitors (samples) the DUT signals and
supplies interpretation of the monitored activity to the other components of
the agent. Monitors can emit events when they notice interesting things
happening in the DUT or on the DUT interface. They can also check for
correct behavior or collect coverage.
In Figure 4-2, notice that the BFMs have bidirectional arrows to the DUT. This signifies the fact that they
can both drive and sample DUT signals. Monitors have unidirectional arrows pointing from the DUT to
them. This signifies that they can only sample data from the DUT. Monitors cannot drive DUT signals.
The actual implementation of the XSerial eVC is slightly different than the architecture shown in Figure
4-2. Instead of a dual-agent architecture, a single-agent architecture was chosen (see Figure 4-3 below).
xserial_env
TX_AND_RX Agent
Config:
... Config
Signal Map
Sequence
Driver
seq
RX TX TX
Mon Mon BFM
DUT
Bus Logic Serial Logic
Both the dual-agent and the single-agent architectures are valid implementations. Depending on the
specifics of the protocol the eVC deals with, you might prefer one over the other.
In the XSerial protocol, the RX direction is completely passive and involves no driving of signals. Thus
there is no need to have a BFM and a sequence driver in the RX agent. However, the TX agent behavior
also depends on flow control frames received by the RX agent. This means that the RX agent must
communicate frequently with the TX agent to tell it when it has received such frames.
In the XSerial protocol, the XSerial eVC could have two agents—an RX agent and a TX agent—where
the RX agent is significantly more simple than the TX agent. If the flow control mechanism involves a
high level of interaction between the two directions, implement a single-agent eVC to model the flow
control mechanism efficiently. The single agent covers both the TX and RX directions. The single agent
contains all of the monitors, BFMs, and sequence drivers required for both directions.
Most important, notice the difference between structs and units, and the difference between unit
instantiation as opposed to pointers between structs and units. The fact that unit instantiation is depicted
by enclosing one unit rectangle within another serves to express the important relations between the
main entities in an eVC architecture.
Notice also that in Figure 4-2 the DUT was drawn in gray at the bottom of the picture. This convention
is maintained throughout the rest of this manual.
A more complete legend is supplied at the end of this chapter in the figure “Full Legend for Architecture
Diagrams” on page 4-26. The more complete legend will be helpful when examining the diagrams in
Chapter 5 “Sequences: Constructing Test Scenarios”.
Generally, the monitor handles most passive activity, and the BFM handles all active interactions with
the DUT. For example, the monitor might collect transactions and then emit an event for each
transaction received. Upon this event, the BFM could be responsible for sending an acknowledgment
back to the DUT.
We recommend using the monitor rather than the BFM to collect transactions that come from the DUT.
This can happen in the following ways:
• In many protocols, each agent in the eVC has both a BFM and a monitor. (See the XBus golden eVC
for an example.)
• In some protocols, the Receive side might not have any active components, so there is no BFM or
sequence driver at all. In such cases, the Receive monitor can be placed inside a single agent that
combines Receive and Transmit activities. This is especially convenient if the Receive and Transmit
sides interact. (See the XSerial golden eVC for an example.)
• To the extent possible, one centralized clock should be used and it should be located in the env.
(Similarly, any other frequently occurring event should be centralized.) The primary reason for this
is to eliminate unnecessary performance overhead.
• On the other hand, if the protocol defines that agents can have different clocks (for example, different
speeds), then each agent should have its own clock.
Centralized clocks should be implemented by maintaining a backpointer from the agents to the
enclosing env and referring to events in the env as env.clock. This comes at the expense of modularity.
We consider this a reasonable tradeoff, because in most cases the agent and the env can be considered a
unit cluster. This means that the interactions and dependencies between them are close enough to
assume that they will always be used together.
Note Sequence drivers will always have their own clock. This is necessary to simplify the user
interface by allowing a uniform way to extend the sequence body() TCMs without needing to know the
specific clock name.
Agents are either ACTIVE or PASSIVE. ACTIVE agents are agents that drive DUT signals. PASSIVE
agents never do that, either because they just monitor an interface within the DUT or because, according
to the protocol, no signals need to be driven. In addition to monitoring the activity of a corresponding
DUT agent, PASSIVE agents can also add checks, scoreboards, and coverage on top of what is
predefined in the eVC. Typically, these should be used in verifying the DUT but should not be part of the
active agent.
In addition to the ACTIVE / PASSIVE attribute, agents are considered proactive if they initiate
transactions, and reactive if they only respond to requests. These attributes are not reflected as fields or
field values in code.
Both ACTIVE and PASSIVE agents have a monitor. The monitor can be instantiated directly in the
agent.
A PASSIVE agent does not have a BFM or a sequence driver. Both are instantiated within the ACTIVE
subclass (or when clause) of the agent. For example:
unit vr_atm_agent_u {
active_passive: erm_active_passive_t ;
monitor: vr_atm_monitor_u is instance;
when ACTIVE vr_atm_agent_u {
bfm: vr_atm_bfm_u is instance;
seq_driver: vr_atm_sequence_driver is instance;
};
};
When an agent cannot drive signals (because the protocol does not allow it), it does not have an
active_passive field (because it is always PASSIVE).
Other standard fields include kind fields that allow specifying subtypes of agents and name/ID fields
that allow identification of each agent (as described in “Instance Names and IDs” on page 9-6).
Any agent in an eVC can be used as a PASSIVE agent. PASSIVE agents represent DUT devices that are
only monitored by the eVC. The PASSIVE agent collects information from the DUT and holds that
information as a reference for the verification environment. This enables orderly monitoring of the DUT
agent—collecting information and checking its behavior from a well defined place in the eVC.
Agent
Config:
active_passive
name ACTIVE
kind == TX
Sequence
Signal Map Driver
Passive seq
Mon
has_coverage
BFM
has_checker
DUT
All agents have a fixed basic architecture with optional additions as required by the specific
environment.
The interface of an agent consists of a configuration unit, a sequence driver, and the signal map
connecting it to the DUT. Agents should be designed to work as independently as possible, not relying
on a specific type of env, because independent agents are more easily adapted to unforeseen needs that
eVC users might have. Therefore, it is important that the agent configuration and signal map are internal
to the agent and are passed to the agent by the enclosing unit.
Note An agent can also have a pointer to its enclosing environment. Through the pointer, the agent can
obtain the configuration parameters. In this case, the agent is dependent on a specific environment and
can only be instantiated in that specific type of environment.
Some configuration fields like active_passive are used for subtyping. These fields must reside in the
agent or be propagated from the config struct to the agent. All other fields are defined in the config
struct. This struct can be constrained from the enclosing unit, thus eliminating the need for a parent
pointer and allowing instantiation of the agent in different environments. For the sake of efficiency,
config fields that are used frequently can be copied into a local field.
Signals connecting the eVC agent to the DUT are string type fields. They are defined either in the agent
itself (prefixed by “sig_”) or in a signal map unit within the agent. As the signals are shared by several
components of the agent such as the monitor and the BFM, a signal map unit is recommended to make
sharing of signals more convenient.
The sequence driver is a unit instantiated in the active agent. By default, it is connected to the BFM in
pull mode. (Push mode is not recommended.) Items generated in the sequence driver are sent to the
BFM whenever an item is available and the BFM is ready to accept a new item. At any given time, the
BFM and monitor provide the current DUT state to the sequence driver for generating sequence items.
The sequence driver must support both standalone operation (generation of items independent of
higher-level protocols) and layering of higher-level protocols (generation of items based on higher-level
protocols).
4.2.2.3 Monitor
The monitor is responsible for extracting signal information from the DUT and translating it into
meaningful events and status information. This information is available to other components of the
agent and to the test writer. The monitor should never rely on information collected by other components
such as the BFM.
The monitor is a unit that is always instantiated (regardless of subtypes). Its functionality should be
limited to the basic monitoring that is always required. Additional high-level functionality that might be
required should be implemented separately on top of the monitor. This includes protocol checkers,
scoreboards, and coverage, used typically, but not only, in the passive agent.
The events recognized by the monitor depend on the actual protocol. Typically, for the basic data item
the monitor provides an item_started and an item_ended event (for example, packet_started and
packet_ended). The monitor collects the item data from the signals and creates a current_item that has
the complete item data, ready to be used when the item_ended event occurs. In addition to the raw data,
the monitor should collect relevant timing information such as the duration of the transaction.
Global Monitor
Some eVCs require a system-level monitor in addition to the agent-level monitors. This is typically used
in broadcast protocols. In point-to-point protocols, agent monitors are usually sufficient.
4.2.2.4 BFM
BFMs do all of the signal driving from agents.
The BFM is a unit instantiated only in ACTIVE agents. Changing an ACTIVE agent to PASSIVE
prevents that agent from driving signals.
No generation is done in the BFM. The BFM receives a data item (from the sequence driver) and
performs all operations required to send the data item to the DUT according to the protocol rules. The
item should contain all necessary information to complete the transaction.
To perform its task correctly, the BFM must know the current state of the DUT. The BFM can sense the
DUT signals directly or use signal information extracted by the monitor.
Protocol errors should be caught and reported by the checker part of the monitor.
4.2.2.5 Checker
Typically, a checker is used to verify a DUT, but it can also be used to verify the correctness of the active
eVC agent.
The checker can be implemented either as a separate unit in the agent or in a has_checker subtype of the
monitor. By default, the has_checker flag should be TRUE in passive mode and FALSE in active mode.
The checker operates based on events and data collected by the monitor. If it is implemented as a
separate unit, it has a pointer to the monitor that is set by the agent when the checker is instantiated.
At a minimum, the checker should check the validity of the basic data item (for example, packet) and the
related timing requirements according to the protocol.
4.2.2.6 Coverage
Typically, coverage is used to verify a DUT. It can also be used to verify the eVC active agent’s
capabilities by covering sequence types.
Coverage can be implemented either as a separate unit in the agent or in a has_coverage subtype of the
monitor. By default, the has_coverage flag is TRUE in passive mode and FALSE in active mode. If an
end user wants to verify the eVC active agent’s capabilities, the has_coverage flag can be set to TRUE.
Coverage operates based on events and data collected by the monitor. If it is implemented as a separate
unit, it has a pointer to the monitor that is set by the agent when the coverage unit is instantiated.
• It is very important to separate the BFM from the monitors. When agents are PASSIVE, they should
be able to use their monitors without interfering with the DUT operation.
• There are many possible names for the main entities in the agent, and people tend to choose differently.
To eliminate unnecessary confusion, we propose the following names for these standard entities:
Agent Device
BFM Driver
Monitor Collector
xbus_env xserial_env
SD SD
SD SD SD
seq seq seq seq seq
M M B M B M B M B M B
xbus
At first glance, you can see the architecture of the XBus eVC is very similar to the XSerial eVC. There
is an env encapsulating configuration fields, signals, and several agents, and the agents are organized
internally in a similar way to the XSerial agents.
However, the XBus protocol is more complex than the XSerial, and this is reflected in the various types
of XBus agents. The agents here can play the roll of a bus master (initiating activity on the bus), a bus
slave (responding to bus master requests but not initiating activity), and an arbiter (coordinating between
the masters so that only one master initiates activity on the bus at a given time). The exact agents vary
for different buses, but this is a very typical arrangement.
The XBus eVC allows for a list of master agents and a list of slave agents. Thus the number of agents
instantiated can vary when the eVC is used in different situations. In the XSerial eVC, there are usually
exactly two agents, but one of them might be ignored.
There are other differences we can notice. The XBus has a bus monitor in addition to the agent monitors,
while the XSerial eVC has only agent monitors. The signals in the XBus are grouped under the env,
while in the XSerial eVC they are grouped in the agents.
From these differences, it is clear that a typical eVC architecture can have some variance and yet still be
quite similar.
xsoc_env
xserial_env
xbus_env xserial_env
Config: RX Agent TX Agent
Config: MASTER SLAVE ARBITER nameConfig: RX Agent TX Agent
name Agent Agent Agent ... name Config Config
has_... ... Config Config
Signal Signal
... Config Config Config Signal Map Signal Map
Sequence
Signal Map driverSD SD
SD SD SD
seq seq seq seq seq
M M B M B M B M B M B
In this case, the SoC verification environment could be the end user’s full environment. Still, we choose
to represent it as an eVC, because it highlights the fact that any verification environment today can turn
into a verification component in a bigger verification environment tomorrow.
This is the first example we see where one eVC is making use of other eVCs. The XSerial and XBus
eVCs are instantiated within the XSoC eVC. In fact, the XSoC eVC has no agents of its own, although
it probably will have some configuration that is not shown here simply because the diagram is crowded.
In this example, the DUT has two XSerial devices, and thus the XSoC eVC has a list of xserial_env
instances. Not shown in Figure 4-7 are things like the scoreboards that check the data end-to-end
between the two eVCs.
All in all, we can see that the XSoc eVC makes thorough use of the lower-level eVCs. By integrating
components, it creates a new and bigger verification component without adding much new code.
Layering is normally used in eVCs for protocols that naturally split into layers, for example, ATM over
Utopia (ATM and Utopia each being a layer), or Ethernet (as in Ethernet packets over XGMII over
XSBI).
Layered eVCs provide a flexible, scalable, and reusable approach to implementation of verification
solutions for multi-layer protocols. You can deliver a separate eVC for each layer or a combined eVC
with functionality for all layers.
Single-layer eVCs can be written without the need for prior knowledge of which other layer eVCs they
might be used with. The protocol for interconnection can be separately encapsulated in connector code,
which in turn can be delivered as a separate eRM package.
Any eRM-compatible eVC can be used as a lowest-layer eVC. Higher-layer eVCs require a
method-based API (but can optionally have an additional signal-based API).
See Also
• Verisity ATM eVC Overview (vr_atm_overview.pdf) in the erm_lib/vr_atm/docs directory
• Verisity XSerial eVC Overview (vr_xserial_overview.pdf) in the erm_lib/vr_xserial/docs directory
• Single-layer eVCs provide functionality relating to a single layer in a protocol, but they also provide
hooks to let other protocol layers be connected above or below as appropriate. Such eVCs enable a
plug-and-play approach to building multi-layer solutions.
• Multi-layer eVCs combine two or more protocol layers in a single eVC using a layered approach.
Such eVCs provide a single-package solution for a multi-layer protocol while letting the user get
control and visibility at each protocol layer.
The decision about which approach to take (single-layer or multi-layer) depends on the specific
protocol(s) in question. There may be both commercial and technical aspects to this decision.
In this chapter, we describe the single-layer approach in detail and provide some discussion of how the
same techniques can be applied to multi-layer eVCs.
Example code is drawn from the Golden eVCs for demonstrating how to layer ATM over XSerial:
• The code for the ATM eVC (an example of a higher-layer eVC) can be found in the erm_lib/vr_atm
directory.
• The code for the XSerial eVC (an example of a lowest-layer eVC) can be found in the
erm_lib/vr_xserial directory.
• Example code for the connection between the ATM and XSerial eVCs can be found at
erm_lib/vr_xserial/examples/layering_atm. Example code for the connection between the ATM
and XBus eVCs can be found at erm_lib/vr_xbus/examples/layering_atm.
For data being generated by a higher-layer eVC and then driven into the DUT via the lower-layer eVC,
the connection between the layers is achieved through the sequence interface. Lower-layer eVCs are
required to have a standard sequence interface implemented using Pull Mode. The connection between
the layers is then implemented as one or more special connector sequences.
For data being collected from the DUT by the lower-layer eVC and passed to the higher layer eVC, the
connection between the layers is achieved through the scoreboard hooks. Lower-layer eVCs are required
to have sufficient scoreboard hooks that the higher-layer eVC can extend to extract the data that it
requires.
Any standard, eRM-compatible eVC can be used as a lowest-layer eVC in a layered solution without
modification so long as its sequence drivers use Pull Mode.
In some cases, an eVC might need to be capable of acting either as a higher-layer eVC or as a
lowest-layer eVC. For example, an XGMII (Ethernet) eVC might be used to layer over an XSBI eVC to
generate and collect XGMII layer data while connected to an XSBI protocol DUT. However, the same
eVC might be connected directly to XGMII signals for a DUT that has an XGMII port. In such cases, we
recommend that the agent have method calls suitable for use in layering the eVC over lower-level eVCs.
The env can then have an optional signal-based interface that calls the methods in the agent as
appropriate.
Typically, two method calls are provided, one for passing data in each direction. Normally, these are
placed in the env unit of the higher-layer eVC. However, it is legal to place them either in the agent unit
or the BFM unit (for the higher-to-lower-layer direction) and in the monitor unit (for the
lower-to-higher-layer direction). When deciding where to locate these methods, bear in mind the likely
use model for the higher-layer eVC, and try to choose an interface that is intuitive for users.
Note These method calls are always called from the lower layer eVC. Hence, both eVCs are operated
in Pull Mode.
When designing the method interfaces, ensure that they are sufficiently flexible to support any possible
lower-level protocol. For example, although it might seem sensible for a method call for an ATM eVC
to work with data partitioned into bytes, this might not be suitably flexible when layered over a
lower-layer eVC that transfers data one bit at a time. Similarly, a lower-layer eVC that can transfer data
32 bits at a time should not have to call the ATM eVC’s method interface four times for each transfer. As
a general rule, method calls should pass a list of bits so that the lower-layer eVC can decide how much
data to request/return at a time.
The following code examples illustrate a typical method interface for an ATM eVC. Two methods are
provided, one for each direction of data flow:
The collect_rx_cell_data() method should be called each time some cell data is detected by a lower
layer on the Rx Path. The d parameter should contain the latest fragment of the bit list for the cell, and
first should be TRUE if this is the beginning of a cell. The d parameter should consist only of data from
a single cell. Any data that pushes the current cell over the 53-byte limit will be discarded.
1. Create the necessary pointer(s) to the upper layer in the lower layer.
2. Connect received traffic by extending the Rx monitor’s scoreboard hook in the lower layer to call
the appropriate method in the higher layer.
3. Create the connector sequence(s) for the lower layer sequence driver that takes appropriate data from
the higher layer.
The rest of this section examines each of these steps in more detail:
Note The addition of the pointer(s) must not compromise the standalone behavior of the lower layer.
Example
extend vr_xserial_tx_driver_u {
atm_evc : vr_atm_env_u;
};
extend vr_xserial_agent_u {
atm_evc : vr_atm_env_u;
keep soft atm_evc == NULL;
when ACTIVE has_tx_path vr_xserial_agent_u {
keep tx_driver.atm_evc == sn_pre_t(atm_evc);
};
};
Example
extend vr_xserial_agent_u {
rx_frame_completed(frame : vr_xserial_frame_s) is also {
if atm_evc != NULL {
if frame.payload is a ATM vr_xserial_frame_payload_s (d) {
atm_evc.collect_rx_cell_data(pack(packing.low,
d.data), d.first);
};
};
};
};
The underlying principle is to create a sequence that drives appropriate lower-layer data according to
data acquired from the higher layer. There can be multiple valid ways of doing this. For example, when
layering ATM over XSerial, the connector sequence might send a single XSerial frame containing a
fragment of an ATM cell, or the connector sequence might send multiple XSerial frames making up a
complete ATM cell. Often it makes sense to create a library of connector sequences that address
different testing scenarios.
In many cases, connector sequences embody protocol factors that are specific to the layering of the two
protocols. For example, neither the XSerial nor the ATM specifications specify how to layer ATM over
XSerial. In this case a separate specification is required to indicate how ATM cells should be transported
over an XSerial link. It is this method of transportation that is typically encoded by the connector
sequence.
An additional issue is that the higher-layer method that supplies data to the lower layer will, of necessity,
be a TCM. At best, this TCM will consume Specman tick time. At worst, it can consume real simulator
time. For example, a wait statement in a higher-layer sequence can cause a call to driver.get_next_item()
in the higher-layer BFM to stall. That, in turn, might cause the higher-layer interface TCM to consume
simulator time.
Constraints associated with a do action in the lower-layer sequence are not allowed to consume time. As
such, a mechanism is required to delay the do pending the return of data from the higher layer. This is
typically achieved using the connector sequence’s pre_do() TCM. This TCM gets called immediately
before each do action in the sequence and can be used to delay the do while data is collected from the
higher layer. Typically, the code in the pre_do() TCM calls the higher-layer TCM to obtain the
higher-layer data and stores this locally within the sequence. The do action can then constrain the
lower-layer data item according to this locally stored data.
The following code illustrates a typical connector sequence. This sequence sends a single ATM XSerial
frame containing ATM data if such data is available. If no ATM data is available, then the sequence
sends an IDLE frame.
extend ATM_FRAME_CONNECTOR vr_xserial_tx_sequence {
Independent eVCs
The most general case for layering is as follows:
Dependent eVCs
Sometimes the layer eVCs can be written knowing that layering between them will take place. In such
cases, it might be appropriate to build the layering code into one of the individual layer eVCs (typically
the higher-layer eVC).
Specific lower-layer behavior can be achieved by writing sequences that mix connector sequences with
other required behavior (such as error conditions).
For more coordinated testing, a virtual sequence driver can be used to regulate traffic injection at each
layer. For example, a virtual sequence could be written to drive continuous ATM cells over an XSerial
link while occasionally grabbing the XSerial sequence driver to override the default connector sequence
with one that introduces XSerial-specific error conditions.
Q: If the DUT has N ports of same protocol, should we have 1 env with N agents, or N envs each with
single instance agents? For example, if a DUT has two pairs of serial ports, which of the following
arrangements is better?
xserial_env xserial_env
or xserial_env
RX TX
RX TX
Agent RX TX
Agent Agent Agent Agent
• In case of serial interface, typically each pair of ports is independent and has 2 agents in it (RX/TX).
Therefore it is natural to have two envs (as shown on the right side of the above figure).
• In buses, typically there are many agents on a bus. Thus if the DUT has one bus, it is natural to have
a single env for all N agents (as shown on the left side of the above figure).
Q: If an env has a single agent, should env and agent be merged (that is, have only an env unit with no
agent in it)?
x_env
or x_env
X Agent
A: They should be kept separate (as shown on the left side of the above figure).
• It is okay to have a single agent in env. It will probably be more reusable this way.
• It is okay to have zero agents in an env. For example, SoC has envs in it but no agents.
Q: Must the Monitor and BFM be units under the env/agent? Might they be TCMs?
• For small environments, having them as TCMs might make sense, but for big environments it can
create problems. Therefore, for uniformity, they should be units.
4.7 Terminology
The following sections provide definitions of terminology. These terms are used extensively in this
book, and as many of these terms are used slightly differently by different people, be aware of the
specific connotations we give to each of them.
Data Item An e object that corresponds to a specific data structure in the eVC architecture.
Data items are implemented as structs.
Data item examples: packet, transaction, instruction, and so on.
Env The root unit for an independent verification environment, for a well defined
protocol, interface, specification, or HW block.
When combining HW blocks to HW systems, the envs can also be combined by
nesting block envs into the system env.
Each eVC defines an evc_env (child of any_env).
Multiple bus instances in the DUT imply multiple eVC instances (envs).
4.7.2 Sub-Entities
Monitor A passive entity that samples the DUT signals (but does not change their values
at any time).
Functionality: Extracts events, prints traces, collects transaction and data items,
does checking and coverage.
BFM An active entity that emulates an auxiliary device for the DUT.
Functionality: Samples and drives the DUT signals.
Always instantiated inside agents (a unit)
Monitor-BFM Relations
BFMs and Monitors are typically built independently (even if they look at the same signals). Therefore
each can work without the other.
Monitor-BFM Guidelines
• It is okay for eVC developers to duplicate or reuse some common code.
• Within an agent, monitors must not depend on the presence of a corresponding BFM.
• Within an agent, BFMs might depend on the presence of a corresponding monitor.
PASSIVE Looks at HW blocks internal to the DUT (often with no external interface).
Can also collect transactions from the DUT, but it does not drive signals.
BFM and sequence drivers are disabled.
Agent Examples
package-name_agent_[driven|monitor]_item-name
For example, vr_xbus_master_driven_burst is the burst generated and sent by the master.
vr_xbus_slave_monitor_burst is the burst reconstructed by the monitor in the slave. The types can be
inherited from a common type using like inheritance. If the types are closely related, when inheritance
might be more applicable.
Data item
DUT access DUT
Unit unit
Sequence Driver
Enclosing unit
Unit instantiation Color Monitor
Instantiated unit
BFM
DUT
Enclosing unit
Subtype Subtype
Sequence execution—
seq
Struct/unit pointer struct unit Sequential: seq
seq
Topic: seq
Field grouping field 1 Parallel: seq
seq
field 2
Event
Together with the evc_util package, Verisity ships three example packages called ex_atm, ex_c_bus, and
ex_soc. These example packages demonstrate in detail the various sequence features.
Note You can only run these example packages on top of Specman version 4.1 or higher. See the
PACKAGE_README.txt files of the example packages for a description of how to run them.
The sections of this chapter can be divided into the following four main parts:
For defining sequences, it is also necessary to define standard interfacing entities between the sequence
and the DUT. Therefore, the sequence solution deals with three main entities:
Item A struct that represents the main input to the DUT (for example, packet,
transaction, instruction). Typically, such items already exist in your
environment, and only very small modification is required to use them with
sequences.
Sequence A struct that represents a stream of items signifying a high-level scenario of
stimuli. This is done by generating items one after the other, according to
some specific rules. The sequence struct has a set of predefined fields and
methods. The sequence struct can also be extended by the user.
Sequence Driver A unit that serves as the mediator between the sequences and the
verification environment. The generated items are passed from the sequence
to the sequence driver and the sequence driver acts upon them one by one,
typically passing them to some kind of BFM (Bus Functional Model). Of
course, the sequence driver can be rather empty and, instead of driving items
into the DUT, simply place them on a list.
• A TCM does the actual driving of items into a specific DUT channel.
• The TCM resides in a BFM unit.
• For the purpose of driving data into the DUT, the sequence driver interacts only with the BFM.
The sequence driver and the BFM work as a pair, where the sequence driver serves as the interface
upwards towards the sequences so that the sequences can always see a standard interface to the DUT.
The BFM serves as the interface downwards to the DUT, letting you write sequences in any way you
find appropriate.
At first, it might seem unnecessary to separate the sequence driver and the BFM. The importance of this
separation becomes clear when implementing virtual sequences (see “Using Virtual Sequences” on page
5-25).
Figure 5-1 describes the general flow of data and control in sequences.
TX Agent Fields:
id
Sequence
kind == TX
Config: Item backpointer
... ...
Sequence Driver
seq
seq
Clock Event
seq
seq
seq
Signal Map
Mon BFM
DUT
The execution flow for generation of items and driving them into the DUT is as follows:
• The sequence driver launches the main TCM of a sequence (called body()), which in turn launches
the main TCM of any subsequences. (See “Defining the Behavior of Sequences” on page 5-12.)
• Sequences generate items on the fly (as part of the execution of their body() TCM).
• Each generated item is passed to the sequence driver, which in turn passes it to the BFM.
Note All of the above actions are done automatically by the sequence mechanism. The subsequence
and item actions are encapsulated in the new do action. (See “Activating Items and Subsequences” on
page 5-13.)
2. Define the sequence and its driver using the sequence statement.
4. Create your sequence library by implementing various scenarios using the sequence struct.
Note Throughout these sections, the examples used are from the eRM library.
See Also
• For more information on Step 1 through Step 3, see “Getting Started with Sequences” on page 5-5
• For more information on Step 4, see “Implementing Sequences” on page 5-11
• For more information on Step 5, see “Writing Tests Using Sequences” on page 5-20
For example:
struct ex_atm_cell like any_sequence_item {
kind: [A1, A2, A3, A4];
color: ex_atm_color;
..
};
Note If you have a pre-existing environment that does not use sequences, you must edit the basic item
to make it inherit as above.
Syntax
sequence sequence_name [using sequence_option,...];
Options
item = item_type The item to be used in the sequence. This item must be already
defined and inherits from any_sequence_item. The item struct is
extended by the sequence statement.
If the item option is not used, then this is assumed to be a virtual
sequence (see “Using Virtual Sequences” on page 5-25).
created_kind = kind_name The name of the associated kind enumerated type to be created
(default: sequence_name_kind).
created_driver = driver_name The name of the associated sequence driver to be created
(default: sequence_name_driver).
sequence_type = The name of the sequence struct your sequence inherits from
base_sequence_name (default: any_sequence).
For more information, see “Creating a Common Base for Your
Sequences and Sequence Drivers” on page 5-50.
sequence_driver_type = The name of the sequence driver unit your sequence driver
base_sequence_driver_name inherits from (default: any_sequence_driver).
For more information, see “Creating a Common Base for Your
Sequences and Sequence Drivers” on page 5-50.
This statement defines the soc_sequence struct, the soc_sequence_kind type, and the
soc_sequence_driver unit.
For more information about virtual sequences, see “Creating a Virtual Sequence” on page 5-26.
The rest of this section describes the entities that the sequence statement creates or extends:
Note The only way to define sequences is via the sequence statement; however, you can define a base
sequence by deriving it from any_sequence. For more information see “Creating a Common Base for
Your Sequences” on page 5-51.
For the full list of the sequence struct members, see “any_sequence Interface” on page 5-88.
Notes
• Some of the above methods apply only for BFM sequence drivers and not for virtual sequence drivers.
For more information about virtual sequences, see “Using Virtual Sequences” on page 5-25.
• The only way to define sequence drivers is via the sequence statement; however, you can define a
base sequence driver by deriving it from any_sequence_driver. For more information, see “Creating
a Common Base for Your Sequence Drivers” on page 5-51.
• For the full list of the sequence driver unit members, see “any_sequence_driver Interface” on page
5-89.
The main members that are added to the item struct are:
See Also
• For the full list of the item struct members, see “any_sequence_item Interface” on page 5-87
MAIN A sequence that loops n times creating the sequence field (randomly unless
constrained). The driver contains an instance of the MAIN sequence, which is
started automatically upon run().
RANDOM Same as MAIN but used inside other sequences.
SIMPLE A sequence that contains a single item.
Note Verisity recommends that all enumerated values be stated in uppercase letters. In addition, all
user-defined sequence kinds should be defined as uppercase names. For example:
extend ex_atm_sequence_kind: [LEGAL_CELLS];
extend LEGAL_CELLS ex_atm_sequence {
body() @driver.clock is only { ... };
};
extend MAIN ex_atm_sequence {
body() @driver.clock is only { ... };
};
See Also
• “Predefined Sequence Kinds” on page 5-93
4. Transfer the item from the driver to the BFM by adding a TCM that explicitly requests items from
the driver and calls the appropriate BFM’s TCM.
5. (Optional) Add useful fields and methods to the base sequence type.
Hookup Example
Assume an ex_atm_cell item that is defined as follows:
struct ex_atm_cell like any_sequence_item {
...
};
Have a BFM that knows how to drive a cell into the DUT:
unit ex_atm_bfm {
event a_clock is rise('atm_clk') @sim; // ATM main clock
drive_cell(cell: ex_atm_cell) @a_clock is { // drives cells to the DUT
...
};
};
Now define the ATM sequence and sequence driver, and hook the sequence driver into the BFM:
// Define ex_atm_sequence, ex_atm_sequence_kind, and ex_atm_driver
sequence ex_atm_sequence using item=ex_atm_cell,
created_driver=ex_atm_driver;
// 4. Pull item from driver, process it, then inform using item_done
extend ex_atm_bfm {
execute_items() @clock is {
var seq_item: ex_atm_cell;
while TRUE {
seq_item = driver.get_next_item();
drive_cell(seq_item);
emit driver.item_done;
};
};
run() is also {
start execute_items();
};
};
Step 5 is optional:
// 5. Extend the base ex_atm_sequence type
extend ex_atm_sequence {
!cell: ex_atm_cell;
};
At this point, your environment is already capable of generating (by default) random sequences.
• The sequence driver generates the MAIN sequence and starts its body() method upon run().
• The launched MAIN sequence creates count sequences of any kind, randomly selected from the
currently loaded ATM sequences. (count is a field in the predefined MAIN sequence.) Initially, this
is only the SIMPLE sequence, so you will have a random stream of ATM cells. See also “MAIN
Sequence” on page 5-94.
• The execute_items() TCM of the BFM pulls the items created by the sequences and drives them into
the DUT using the drive_cell() TCM.
• After each item is driven to the DUT, the event driver.item_done is emitted to let the sequence
complete the do action and inform the driver that the item was processed.
Note If your verification environment contains several ex_atm_unit instances, each of them will have
a sequence driver and hence a MAIN sequence and a random stream.
See Also
• “BFM-Driver Interaction Mode” on page 5-44
The body() TCM defines the life cycle of the sequence and, as such, defines the duration of the
sequences. Before body() is initiated, the predefined event started occurs. When body() is finished, the
predefined event ended occurs.
You cannot call or start body() directly. Instead, use the following:
Note The do action can be used only within a sequence TCM. For details about the additional effects
of the do action, see “Activating Items and Subsequences” on page 5-13.
Except for the aforementioned restriction, body() is just a normal TCM. You can use ordinary
procedural code in it, and you can extend it using is first, is also, or is only.
When starting the body() TCM using the start_sequence(), there are two hooks, pre_body() and
post_body() TCMs, that can be used as follows:
Note The MAIN sequence is started automatically, using the start_sequence() method. For more
information about the start_sequence() method, see “start_sequence()” on page 5-76.
Syntax
do field_name [keeping {constraint;...}]
Parameters
field_name Must be a field in the current struct. The field must have an exclamation mark (!)
in front of it and must be either a basic item or a sequence.
Description
The do action performs the following steps:
For more information, see Figure 5-12 on page 5-99 and Figure 5-11 on
page 5-98.
Notes
• When do-ing an item, you must emit the event driver.item_done to let the sequence complete the do
action and inform the driver that the item was processed. (Typically, this is done when the transmission
of the item via the BFM is done.) Without emitting this event, the sequence cannot continue, and the
driver cannot drive more items.
• The do action can only be activated inside sequences.
• For items, Step 1 (waiting for the sequence driver to be ready) is performed before Step 2 (generation)
to ensure that generation is done as close as possible to the actual driving. In this way, if the constraints
depend on the current status of the DUT/environment, that status will be as accurate as possible.
• BFM sequences cannot do sequences created by other sequence statements.
• The sequence driver decides when the item is ready by managing a FIFO that also considers any
grab/ungrab actions done by the various sequences and the value of is_relevant() of the sequences.
If no grab is done, and is_relevant() returns TRUE for all sequences, the order of doing the items is
determined by the order of the do actions in the various sequences that refer to the sequence driver,
regardless of their depth or origin. Keep in mind that sequences and items can also be done in parallel,
using the all of and first of actions. (See “grab()” on page 5-78 and “is_relevant()” on page 5-82.)
• do is a time-consuming action encapsulating a set of actions that can be considered as an atomic
activation of an item/sequence. However, if for some reason you want to perform any of these steps
separately from the do, you can easily do so.
Example
extend FOO ex_atm_sequence {
// Parameters
i: int;
b: bool;
// Items/subsequences
!cell: ex_atm_cell;
!seq: bar ex_atm_sequence;
For example:
extend MAIN my_sequence {
!seq: GREEN my_sequence;
body() @driver.clock is {
gen seq keeping { .driver == driver; };
For example:
seq.start();
For example:
sync (@seq.ended or @seq.stopped);
4. Call the sequence’s stop() method when you want to stop the run.
Example
extend MAIN my_sequence {
!seq: GREEN my_sequence;
body() @driver.clock is {
gen seq keeping { .driver == driver; };
seq.start();
sync (@seq.ended or @seq.stopped);
};
};
extend GREEN my_sequence {
body() @driver.clock is {
for i from 0 to 10 do {
do cell keeping {.color == GREEN; };
if driver.need_more_green == FALSE {
stop();
};
};
};
};
In this way, you can later control the parameters using constraints from the outside without knowing the
actual implementation of body().
};
The parameter of this sequence is count, which is the number of random sequences that will be created
by the sequence. You can control the behavior of all RANDOM sequences in the environment by
constraining the count:
extend RANDOM ex_atm_sequence {
keep count in [10..20];
};
Note Such a constraint can also be applied locally to a RANDOM sequence field in another sequence
or even in a specific do.
Using Methods
In the sequence struct, you can define methods that encapsulate a typical do action. An example is a
read/write interface.
For example, assume that a bus-based design with the basic item c_bus_op is defined as follows:
struct c_bus_op like any_sequence_item {
kind: [READ, WRITE, OTHER];
address: int;
color: [RED, GREEN, WHITE];
when WRITE c_bus_op {
data: int; // Sent data
};
when READ c_bus_op {
!data: int; // Returned data
};
};
To do that, define methods in the corresponding sequence that implement these operations:
extend ex_c_bus_sequence {
!write_op: WRITE c_bus_op;
!read_op: READ c_bus_op;
// Do a c_bus write
write(address: int, data: int) @driver.clock is {
do write_op keeping {.address == address; .data == data};
};
// Do a c_bus read
// NOTE: The read operation returns a value to the sequence. This
// requires additional infrastructure in the send_to_bfm() method to
// ensure that the data on the operation will be valid after the do
// action.
read(address: int): int @driver.clock is {
do read_op keeping {.address == address};
return read_op.data;
};
};
This lets you use the convenient procedural interface while still keeping the ability to constrain other
fields (such as c_bus_op.color) from the outside.
For more information about a read/write interface, see “DUT-Independent Read/Write Interface” on page
5-41.
Using Macros
Sometimes, you might need the greater syntactical flexibility that only macros can afford. In that case,
Verisity recommends creating action macros that implement typical do actions. For example:
define cell'action "cell <len'num> <kind'name>[ <x'num>]" as {
do cell keeping {
.len == <len'num>;
.kind == <kind'name>;
.x == <x'num|0>;
};
};
Verisity recommends defining the sequence library in a separate file or files that can be loaded on top of
the environment. This lets you include or exclude some of the sequence kinds upon demand of specific
tests.
Note The term “subtype” applies when creating a new type using when inheritance.
1. Extend the sequence kind type with the desired new kind.
For example:
extend ex_atm_sequence_kind: [SHORT_LONG];
2. Extend the new sequence subtype of the kind with any of the following:
• New parameters
• New implementation of body()
• Any other struct members such as constraints, methods, and so on
For example:
extend SHORT_LONG ex_atm_sequence {
// new parameter
short_count: int [1..8]; // number of short cells in sequence
// New implementation of body()
body() @driver.clock is {
for i from 1 to short_count do {
do cell keeping {.len <= 2};
};
do cell keeping {.len == 5};
};
};
Note Verisity recommends encapsulating any meaningful scenario as a new sequence kind.
Verisity recommends using sequences in tests to create a sequence library by defining many subtypes of
sequences based on the kind field of the sequence. (See “Creating a Sequence Library” on page 5-19.)
Then create tests that use these subtypes by parameterizing them, enhancing them, or using them as is
with some weights.
In some simple cases you can avoid defining new sequence kinds altogether by directly redefining the
body() method of the MAIN sequence.
Alternatively, you can define a specific new sequence kind directly in the test and then use it.
This section demonstrates all of the above options. This section includes:
• “Writing the Simplest Test: Redefining MAIN Sequence body()” on page 5-20
• “Writing a Typical Test: Using the Sequence Library” on page 5-21
• “Writing a Dedicated Test: Creating a New Sequence Kind” on page 5-23
• “Writing a Unit-Related Test: Using Unit ID” on page 5-23
Note The eRM library contains example directories with many sequence definitions and tests that you
can study and run.
This is sufficient to create a test, because the MAIN sequence is started automatically as part of the
infrastructure of sequences and so there is no need to handle that in the test.
Example
extend MAIN ex_atm_sequence {
body() @driver.clock is only {
do cell;
do cell keeping {.color == GREEN};
};
};
Example
extend MAIN ex_atm_sequence {
keep soft sequence.kind == select {
30: ALTERNATING_COLOR;
60: SHORT_LONG;
10: FIXED_LEN;
};
};
The way to modify an existing sequence is to extend the sequence subtype in the test itself.
Example
// Modify the behavior of FIXED_LEN sequence - wait additional 3 cycles
// after it ends
extend FIXED_LEN ex_atm_sequence {
body() @driver.clock is also {
wait [3] * cycle;
};
};
This approach can be used in conjunction with a specific unit. In that case, you can add a flag that
determines the behavior and then constrain the flag under the specific unit. For example, to apply the
above (waiting 3 cycles) only to sequences under unit ATM_0, extend the target_sequence within the e
file describing the current test or sequence.
// Add control to the behavior of 'fixed_len' sequence.
// Add option to wait additional 3 cycles.
// Add a unit-related constraint to activate the additional behavior.
extend FIXED_LEN ex_atm_sequence {
use_extra_waits: bool;
// Set default behavior.
keep soft use_extra_waits == FALSE;
// Activate additional behavior for ATM_0.
keep in_unit(ATM_0 ex_atm_driver) => use_extra_waits == TRUE;
};
To define a sequence kind in a test, use the same methodology recommended in “Creating a Sequence
Library” on page 5-19.
Note If you redefine a sequence in several tests, consider adding that sequence to the sequence library
and using it as mentioned in “Writing a Typical Test: Using the Sequence Library” on page 5-21.
These ID fields can be enumerated types (for example, ex_atm_name) or numbers (for example,
agent_num). In effect, these fields correspond to the role the field is playing. For example, you can add
to your environment a code similar to this:
type ex_atm_env_name: [NORTH, SOUTH];
extend ex_atm_env {
name: ex_atm_env_name;
};
unit comm_env {
north_atm: NORTH ex_atm_env is instance;
south_atm: SOUTH ex_atm_env is instance;
ethernet_ports: list of ethernet_port is instance;
keep for each in ethernet_ports {.ind == index};
};
These ID fields are convenient for specifying constraints for sequences and also for constraints on the
unit itself. For example:
extend NORTH ex_atm_env {
keep foo == 4;
};
Once such IDs exist, you can use methods like those mentioned in “Propagating Parameters in the
Sequence Tree” on page 5-33 and “Migrating Unit Attributes to the Sequences” on page 5-33 to apply the
IDs to the sequences themselves.
Note The name “NORTH ex_atm_env” specifies a role of ex_atm_env, sometimes mistakenly called
an instance.
Suppose you want to write a test in which only one ATM env will run the multi_write sequence (with an
address of 20). The rest will run random. You could achieve that as follows:
extend MAIN ex_atm_sequence {
keep in_unit(NORTH ex_atm_env) =>
soft sequence is a MULTI_WRITE ex_atm_sequence (s) and
s.address == 20;
};
For example, you could define an SoC containing four comm_envs as follows:
unit SoC {
comm_envs[4]: list of comm_env;
keep for each in comm_env {.index == index};
};
You now have four NORTH ex_atm_envs. If you load your previous test on top of this environment, it
will run a multi_write sequence on each one of them. If, instead, you want to have all but the first be of
type xx, you can do that as follows:
extend MAIN ex_atm_sequence {
keep in_unit(NORTH ex_atm_env) and in_unit(comm_env) (C) and
C.index != 0 => soft sequence is an XX ex_atm_sequence;
};
Sequence definition files Define a sequence for a specific item (packet, transaction, and
so on) in any environment
DUT-specific sequence Extends the sequence for use with a specific DUT and hooks
hook file the sequence to the environment
Sequence libraries Define various sequence subtypes that implement specific
scenarios, either generic or DUT-specific
Test files Import relevant sequence libraries and use them
Note The erm_lib directory contains several full environments (see the LIBRARY_README.txt file).
However, outside the area of sequences, it is not a good example. For one thing, it defines the whole ATM
environment in one file (ex_atm_env.e) to simplify the exposition.
See Also
• For more information on eVC file organization, see Chapter 3 “eVC File Organization”
Virtual sequences, unlike BFM sequences, are not tightly connected to a specific sequence type or item.
Virtual sequences can do sequences of other types (but not items). As a result, you can use virtual
sequences to:
A virtual sequence driver is not connected to a specific BFM. Therefore, it lacks the logic and
functionality of a BFM driver. For example, a virtual sequence driver does not schedule items—it only
drives sequences. As much of the driver functionality is aimed at controlling and manipulating the
scheduling of items, any method that controls this functionality cannot be called for a virtual sequence.
For example, you cannot grab/ungrab a virtual sequence driver, because grabbing manipulates the
scheduling of items. For a full list of driver interface methods that cannot be used for virtual drivers, see
Table 5-4 on page 5-89.
See also ex_soc_1_sequences.e in the ex_soc/e directory, or just load ex_soc_1_test.e (in the
ex_soc/examples directory) and run it.
For example:
sequence comm_sequence; // Note no "item=" option
2. Add fields to the virtual sequence driver that point to the subdrivers.
For example:
extend comm_sequence_driver {
atm: ex_atm_master_sequence_driver;
ethernet: ethernet_sequence_driver;
For example:
get_sub_drivers(): list of any_sequence_driver is {
return {atm; ethernet};
};
};
Note Although this method is needed by the sequence mechanism only when applying stop() on
virtual sequences, you might find the method useful for applying procedural actions on your
subdrivers. For example:
for each (d) in get_sub_drivers() {
....
};
4. Define the sequence driver in the appropriate unit in your environment (probably the lowest unit
enclosing all of the component drivers).
For example:
extend comm_subsystem_unit {
driver: comm_sequence_driver is instance;
// Constrain the subdrivers.
keep driver.atm == atm1_unit.driver;
keep driver.ethernet == ethernet_router_unit.driver;
};
The sequence driver will launch under its MAIN comm_sequence.
For example:
extend comm_sequence_driver {
event clock is only @sys.any;
};
Notes
• A virtual sequence cannot do items directly, but only sequences. To activate single items from a
virtual sequence, use the SIMPLE sequence. You can pass any desired parameters to the item by
defining the parameters as fields in the SIMPLE sequence and propagating them by constraining the
item.
• For the full list of driver interface methods that cannot be used for virtual sequences, see “Sequence
Interface” on page 5-86.
This is enforced via a soft constraint. However, when a virtual sequence is do-ing a BFM sequence, it
must pass the appropriate subdriver to the BFM sequence.
• Business as usual:
You want the virtual sequence driver and the original sequence drivers to work at the same time,
using the built-in capability of the original sequence drivers.
This is the default behavior. There is no need to do anything to achieve this.
• Disable the subdrivers:
This is most simply achieved as follows (taken from ex_soc_1_test.e):
extend MAIN ex_atm_sequence {
keep count == 0;
};
SD
Virtual Sequence
Mon BFM
SD
Memory DMA
Mon BFM Controller Controller
CPU Bus
SD SD SD SD
Mon BFM Mon BFM Mon BFM Mon BFM
1. Ensure that each of the agents has its own sequence (and sequence driver).
2. Define a new (virtual) sequence (and sequence driver) using the sequence statement and omitting
the item parameter.
3. Add the existing sequence drivers as fields of the new sequence driver.
4. Pass the existing sequence drivers using constraints to the BFM sequences done by the virtual
sequence.
extend comm_subsystem_unit {
driver: comm_sequence_driver is instance;
comm_subsystem_unit
Virtual Driver
do eth_config keeping {
.driver=eth_driver; eth_config
};
seq
do atm_sequence keeping { atm_sequence
.driver=atm_driver;
};
atm_env eth_env
BFM BFM
5.7.5 Transactions
The term “transaction” includes both items and sequences. In an SoC context, there can be DMA
transactions, MPEG transactions, and so on.
Such transactions normally consist of writing to several registers to initialize the DMA or MPEG device
and possibly sending an MPEG input packet to the MPEG input channel, either in parallel or after
writing to all of the registers.
It is easiest to package the transaction as a virtual sequence. Then you can have a higher-level sequence
go as follows:
do sequence keeping {.kind == DMA_TRANS};
do sequence keeping {.kind == MPEG_TRANS};
Parameters
Note “it” already refers to the subtype. There is no need for casting to access the subtype attributes.
Example 1
extend MAIN my_sequence {
seq: my_sequence;
body() @driver.clock is {
do RED seq keeping {
it.red_level == 10 // Access red_level without casting
};
do GREEN seq keeping {
.green_level == 18;
};
do ATOMIC GREEN seq keeping {
it.green_level == 12;
it.atomic_level == PARTIAL;
};
do YELLOW seq ;
};
};
Example 2
extend MAIN my_sequence {
!seq: GREEN my_sequence;
body() @driver.clock is {
// seq will be generated to LONG GREEN my_sequence (not GREEN LONG -
// order does matter).
do LONG seq keeping {
it.length > 10; // length is a field of LONG
};
// seq will be generated to ATOMIC GREEN my_sequence
do ATOMIC seq keeping {
it.green_level == 12;
it.atomic_level == PARTIAL;
};
};
};
For example:
keep parent_sequence != NULL => soft my_param ==
parent_sequence.as_a(ex_atm_sequence).my_param;
3. Add an additional constraint for the root sequence in the root-sequence parent.
Example
type ex_atm_port_id: [ATM_0, ATM_1];
extend ex_atm_sequence {
port_id: ex_atm_port_id; // Field to be propagated
keep parent_sequence != NULL => // The propagating constraint
soft port_id == parent_sequence.as_a(ex_atm_sequence).port_id;
};
extend ex_atm_driver {
port_id: ex_atm_port_id;
keep sequence.port_id == port_id; // Constrain sequence root in driver
};
Example
extend ex_atm_sequence {
// 1. Define the unit name in the sequence.
name: ex_atm_name;
// 2. Migrate the value from the enclosing unit.
keep name == get_enclosing_unit(ex_atm_agent).name;
};
This is a useful shorthand. Nevertheless, you might still need to reference the IDs of the enclosing units
if you want a more complete hierarchical identification.
2. Either generate the field/variable using the gen action or instantiate it using the new action.
3. Constrain the field in the do action to be equal to the additional field/variable (defined in Step 1).
For example:
extend FOO ex_atm_sequence {
// Items/subsequences
!cell: ex_atm_cell;
gen preprepared_cell;
...
do cell keeping {it == preprepared_cell};
};
};
To return immediately (non-blocking do), implement the method that processes the item to be
non-time-consuming and return immediately.
Suppose you have a data item that contains a list with items that you want to constrain. In this case, you
cannot use the it variable to refer to the list items, because it already refers to the data item itself. Instead,
you must specify a new name for the list items. In the following example, the name “item” is used for
this purpose.
extend NEW my_sequence {
body @driver.clock is only {
do burst keeping {
.addr <= 1000;
.type in [READ, WRITE];
for each (data_item) in .data { // Specify name for list item
data_item > 10 and data_item < 90;
};
};
};
};
body() @driver.clock is {
for each (seq) in sl_seq_l {
it.start_sequence();
};
};
};
Note The number of sequences that PARALLEL_N activates can be controlled by a higher-level
sequence.
You can implement these preliminary stages of the test with sequences. For example:
extend MAIN ex_atm_sequence {
body() @driver.clock is only {
do initialization_sequence;
...
};
};
Another way to perform initializations before the test is by extending the pre_body() method to delay
the execution of body(). For example:
extend MAIN ex_atm_sequence {
pre_body() @sys.any is {
// Perform configuration
configure_dut();
// Wait until initialization is done
sync true(driver.initialization_done);
};
};
a. Wait for the interrupt event to occur (that is, serves as the interrupt handler).
3. Start the interrupt sequence in the run() method of the sequence driver.
Example
(Taken from ex_interrupt_abort_test.e in the ex_atm/examples directory.)
// 1. Define an interrupt sequence.
extend ex_atm_sequence_kind: [INTERRUPT_ABORT];
extend INTERRUPT_ABORT ex_atm_sequence {
// Upon interrupt, grab driver, terminate all its main activity, and
// send a configuration sequence
body() @driver.clock is {
sync @driver.interrupt; // Wait for the interrupt event.
grab(driver); // Grab the driver.
driver.sequence.stop(); // Stop the MAIN sequence activity.
do cell keeping {.color == RED}; // Execute the interrupt scenario.
do cell keeping {.color == GREEN};
do cell keeping {.color == BLUE};
ungrab(driver); // Ungrab the driver.
};
};
extend ex_atm_driver {
// 2. Instantiate the interrupt sequence under the sequence driver
iseq: INTERRUPT_ABORT ex_atm_sequence;
keep iseq.driver == me;
All Specman structs have the predefined method rerun() for reactivating the struct and re-executing the
run() method.
• Call quit() upon reset. Then, at the end of the reset, call rerun().
• Call rerun() upon reset. Then use the reset-qualified clock for the struct’s TCM.
For sequence drivers, the following methods support re-execution of sequences:
driver.quit() Calls to quit() for every started sequence under the driver
driver.rerun() 1. Calls to quit()
4. Re-executes the run() method, which by default starts the MAIN sequence
Notes
• When calling to rerun(), the driver continues driving items until the end of the cycle. If the driver
was in the middle of do-ing an item at the end of the cycle, then that item might be lost.
• When you have a virtual sequence that controls multiple BFM sequence drivers, rerunning any of
the BFM sequence drivers does not terminate the virtual sequence. If the virtual sequence was in the
middle of do-ing a BFM sequence that was do-ing an item at the end of the cycle, then that item
might be lost but the BFM sequence continues.
The following runtime data is reset upon rerun, at the beginning of the next cycle:
All do item actions waiting This includes do actions initiated by a higher-level virtual sequence
to be performed that continues to run. In such case, those do item actions are skipped,
and the sequence continues to run.
grab() method If the driver was grabbed by a sequence before the reset, then the grab
will be canceled along with the entire queue of sequences waiting to
grab the driver. Therefore, a higher-level virtual sequence that
grabbed the driver before the reset must regrab it if needed.
Sequences that were waiting to grab the driver (that is, were blocked
in the grab() method) are released without grabbing the driver.
The list of previously sent You cannot use last() to retrieve items that were done before the
items rerun. For more information on last(), see “last()” on page 5-83.
Any additional actions needed at quit() time can be added to the quit() method of the driver using is
also. For example:
extend ex_atm_sequence_driver {
quit() is also {
out("quitting driver", me);
};
};
Any additional actions needed at rerun() time can be added to the rerun() method of the driver using is
also. For example:
extend ex_atm_sequence_driver {
rerun is also {
out("rerunning driver", me);
};
};
Sometimes, you might want to regenerate some of the driver’s data upon rerun() (before re-executing
the driver’s run()). You can do that by extending the method driver.regenerate_data(). For example:
extend ex_atm_sequence_driver {
regenerate_data() is also {
gen sequence;
};
};
Note The rerun() method performs the following operations:
• Quits the struct (which causes the termination of all TCMs and TEs).
• Executes the run() method.
• Re-initiates all TEs.
A typical example is a bus-based configuration test that can be executed over multiple buses, for
example, a PCI bus as well as an ATM bus.
1. Define a virtual read/write sequence that uses a generic low-level sequence driver to execute the
low-level read/write transactions.
Note The last portion of the step (2c) might be done already as part of the specific
environment. See “Enhancing the User Interface” on page 5-17.
Note Each of these steps should reside in a separate file (to allow reuse).
Example
(This is from ex_cbus_rw_test.e in the ex_c_bus/examples directory.)
result = low_driver.read(address);
};
};
};
};
Notes
• The test itself has no notion of the actual agent that eventually executes the read/write transactions.
• A similar approach can be used to develop additional generic actions such as reset.
• The use of list of bit and packing in the read/write interface of the sequence driver is essential to
prevent dependency on a specific agent or format and a specific bit width.
The scheduling algorithm works on a first-come-first-served basis. You can change the algorithm using
grab() and is_relevant().
If a sequence is grabbing the driver, the driver will choose the first do action that satisfies the following
conditions:
To control scheduling:
• Use is_relevant() to specify a condition for performing a do action.
or
• Grab the driver.
For example, a divide-by-zero machine instruction sequence might look like this:
extend DVZ inst_sequence {
op1_reg: register;
op2_reg: register;
body() @driver.clock is {
do SET_REG inst_sequence keeping {.reg == op1_reg};
...Do any random sequence not writing to op1_reg
do SET_REG inst_sequence keeping {.reg == op2_reg; .value == 0};
...Do any random sequence not writing to op1_reg or op2_reg
do instr keeping {
.op == divide;
.op1 == op1_reg;
.op2 == op2_reg
};
...Release the locking on op1_reg and op2_reg
};
};
The question is what to put in the lines starting with the ellipsis “...”. grab() and ungrab() will not work
here. They are useful to grab sequence drivers, not registers.
Verisity recommends using either Boolean flags or lockers inside the sequence driver (or inside the unit
where the sequence driver resides). Have one flag or locker for every resource that you want to lock (for
example, a register that you do not want people to write to).
Sometimes you might want the sequences to be the initiators so that you do not need to request an item.
Instead, when the driver is ready to do an item, there is an implicit call to driver.send_to_bfm(). This
style of interaction is called PUSH_MODE.
2. Implement the driver.send_to_bfm() TCM so that it calls the BFM TCM that drives the item into
the DUT.
PULL_MODE PUSH_MODE
extend ex_atm_bfm { extend ex_atm_driver {
execute_items() @clock is { // Set interaction mode
var seq_item: ex_atm_cell; keep bfm_interaction_mode ==
while TRUE { PUSH_MODE;
seq_item = // Transfer the item from the
driver.get_next_item(); // driver to the BFM:
drive_cell(seq_item); // Add a call to the BFM's TCM in
emit driver.item_done; // the sequence driver's
}; // send_to_bfm()
}; send_to_bfm(seq_item: ex_atm_cell)
@clock is only {
run() is also { bfm.drive_cell(seq_item);
start execute_items(); };
}; };
};
Notes
• In PUSH_MODE, driver.item_done is automatically emitted when driver.send_to_bfm() returns.
• PUSH_MODE is implemented using PULL_MODE in the following way:
send_loop() @clock is {
while TRUE {
var seq_item := get_next_item();
send_to_bfm(seq_item);
emit item_done;
};
};
• The BFM interaction mode is a static property of the environment. It must not be modified during
simulation.
For more information about the difference between PUSH_MODE and PULL_MODE, compare Figure
5-11 on page 5-98 to Figure 5-12 on page 5-99.
When implementing pipelined scenarios with sequences some typical problems arise:
• How to launch a new data item while the current data item is still being processed
• How to ensure that the fields of the data item are stable before sampling them
2. Emit the item_done event when a trigger event in the BFM indicates that a new data item should be
sent to the DUT.
This releases the current do action and lets the sequence perform the next do action.
For example:
extend my_sequence {
!trans1 : READ transfer;
!trans2 : WRITE transfer;
body()@driver.clock is {
-- List a series of pipelined do actions
do trans1;
do trans2;
};
};
extend my_bfm {
bfm_loop()@clock is {
while TRUE {
trans = driver.get_next_item();
start put_on_bus(trans);
If sampling of data items is required, you need a mechanism for detecting when the data-item fields are
updated and ready for sampling. In the above example, the sequence can start to do the WRITE burst
before the fields of the READ burst are fully updated.
1. Add a Boolean field to the data item that is set to TRUE when all fields are up to date.
2. Delay sampling of fields with a wait or sync action that is satisfied when the field is TRUE.
For example:
extend transfer {
-- Add Boolean field
!finished : bool;
};
extend my_sequence {
!trans1 : READ transfer;
body()@driver.clock is {
do trans1;
-- Delay sampling
sync true(trans1.finished);
sample(trans1);
};
};
When sampling of a pipelined data item’s fields is required immediately after the item is completely
processed, then the above approach must be refined. The sampling of each item must occur in a thread
distinct from that of the do of the next item. You can achieve this in several ways.
get_data()@driver.clock is {
-- Delay sampling
wait true(finished);
do_something_with(data);
};
};
extend my_sequence {
!trans1 : READ transfer;
!trans2 : WRITE transfer;
body()@driver.clock is {
var num_of_items : uint;
all of {
-- Create separate thread for each do-sample pair
{ // First thread
do trans1;
num_of_items = 1;
trans1.get_data();
};
{ // Second thread
sync true(num_of_items==1);
do trans2;
trans2.get_data();
};
};
};
extend my_bfm {
bfm_loop()@clock is {
while TRUE {
trans = driver.get_next_item();
start put_on_bus(trans);
get_data()@driver.clock is {
-- Delay sampling
wait true(finished);
do_something_with(data);
};
};
extend my_sequence {
!trans1 : READ transfer;
!trans2 : WRITE transfer;
body()@driver.clock is {
message(NONE,"running ",kind," burst");
do trans1;
start trans1.get_data(); // Sample in separate thread
do trans2;
start trans2.get_data(); // Sample in separate thread
};
};
extend my_bfm {
bfm_loop()@clock is {
while TRUE {
trans = driver.get_next_item();
start put_on_bus(trans);
Although atm_sequence, ethernet_sequence, and comm_sequence are different sequence families, they
can have a common logic and functionality. Therefore, you might want to define a common base for
your sequences and a common base for your drivers. Some times when this could be useful are:
• When using layered sequence drivers that have a common functionality (for example, in the way
they extract data from the higher-level driver)
• When you want each sequence to print a standard header or footer when it is started or ended
• When you want to associate different sequences or drivers that use different items in one logic family,
(for example, all AHB sequences)
2. Implement the common logic for your sequences in your base_sequence struct.
3. Add the option sequence_type=base_sequence to the sequence statement for each sequence struct
that you want to inherit from base_sequence.
For example:
sequence my_seq using item=my_item, sequence_type=base_sequence;
Specifying sequence_type causes newly created sequence structs to inherit from base_sequence
rather than any_sequence.
Notes
• The base_sequence struct can be derived from another base_sequence struct, not only from
any_sequence.
• The base_sequence struct only establishes a common logic. You cannot instantiate or generate an
instance of this type.
2. Implement the common logic for your sequence drivers in your base_sequence_driver unit.
For example:
sequence my_seq using item=my_item,
sequence_driver_type=base_sequence_driver;
Specifying sequence_driver_type causes newly created sequence drivers to inherit from
base_sequence_driver rather than any_sequence_driver.
Notes
• The base_sequence_driver unit can be derived from another base_sequence_driver unit, not only
from any_sequence_driver.
• The base_sequence_driver only establishes a common logic. You cannot instantiate or generate an
instance of this type.
This TCM tries to return the current item being done by the sequences. However, unlike
get_next_item(), if no such item exists, it returns NULL in the same cycle that it is called. This lets the
user create a default item instead of the item that was supposed to be created by the sequences, thereby
adhering to the DUT rules.
Note try_next_item() does not just look for an existing candidate for a do but also allows sequences
to execute their body() TCMs and then look for a possible do action. This is done by calling the
wait_for_sequences() TCM (see “Solving Possible Race Conditions” on page 5-54).
1. Replace the call to driver.get_next_item() (as demonstrated in Step 4 of the hookup scheme in
“Hooking the Sequence Driver to the Environment” on page 5-9) with a call to the try_next_item()
TCM.
Example
extend ex_atm_bfm {
pull_send_loop() @clock is only {
var seq_item: ex_atm_cell;
while TRUE {
seq_item = driver.try_next_item();
if seq_item == NULL {
seq_item = new;
};
drive_cell(seq_item);
emit driver.item_done;
};
};
};
See Also
• Figure 5-13 on page 5-100
• Figure 5-12 on page 5-99
To enable an intracycle delay (a delay within the same cycle) of sequence activity:
• Use the delay_clock() TCM.
any_sequence_driver.delay_clock() @sys.any;
After an intracycle delay, the delay_clock() TCM emits the driver’s clock just once.
1. Remove any connection to the driver clock (see the hookup scheme in “Hooking the Sequence Driver
to the Environment” on page 5-9).
For example:
extend ex_atm_bfm {
delay_driver_clock() @a_clock is {
while TRUE {
// Emit the driver.clock after some delay
driver.delay_clock();
wait cycle;
};
};
run() is also {
start delay_driver_clock();
};
};
Note This TCM replaces the regular clock connection, so make sure that the clock is not also
emitted.
A typical case is when one thread produces some data that another thread consumes. If the consumer is
evaluated before the producer, it could happen that the consumer will be called first and will miss the
producer, even though both occurred in the same cycle.
Solving such cases can be done by explicitly performing a context switch in the consumer that lets the
producer be evaluated first.
This TCM is already used in the sequence mechanism in the call to try_next_item().
try_next_item() calls wait_for_sequences() to allow evaluation of the sequences before looking
for existing do actions.
You can also extend or override this TCM if the existing implementation does not satisfy the needs
of your environment. For example:
extend ex_atm_driver {
wait_for_sequences() @sys.any is only {
for i from 1 to 100 {
sync [0];
};
};
};
Note Overriding wait_for_sequences() also affects the behavior of try_next_item().
Note By default, auto_quit() currently returns FALSE. In future, the default will change to TRUE.
Sequence layering and virtual sequences are the two main ways in which sequence drivers can be
composed to create a bigger whole.
In the erm_lib directory, there are two example packages that demonstrate layering:
ex_blayers This eRM package (basic layers) demonstrates a simple layering configuration.
It shows a low-layer packet sequence driver that can either work alone or pull
information from a high-layer frame sequence driver. (For details, see the
PACKAGE_README.txt of the package.)
ex_layers This eRM package demonstrates the use of multiple high- and low-layer
sequence drivers as well as virtual sequence drivers, all interacting in the
layering scheme. It also demonstrates late binding of the various layers
(emulating a situation where different people wrote separate eVCs that are later
bound together by yet another person). (For details, see the
PACKAGE_README.txt of the package.)
For example, assume that there are two sequence drivers. The low-layer sequence driver drives packets
that are defined as:
struct packet like any_sequence_item {
data[10]: list of byte;
... -- Other fields
};
In one case, you just want to send packets with random data. In another case, you want the data to come
from a higher-layer data protocol. The higher-layer protocol in the example drives frames. So the
high-level sequence driver is defined as:
sequence frame_sequence using item = frame;
A frame would then have various fields that must be packed together and sent in the data lists of n
consecutive packets.
“Layering Inside One Sequence Driver” Applies for simple cases only
“Layering of Several Sequence Drivers” Applies for all layering needs
The FRAME_SENDER sequence generates a single frame and sends it in chunks, in one or more
packets, until the data of the frame is exhausted. For example:
extend FRAME_SENDER packet_sequence {
frame: frame;
!packet: packet;
body() @driver.clock is {
var full_data: list of byte = pack_the_frame(); // Pack the frame
The FRAME_SENDER sequence can then be used by other sequences. For example, you could define a
SUPER_FRAME_SENDER packet_sequence that takes a super_frame (whatever that is), chops it into
frames, and executes in a loop as follows:
do FRAME_SENDER sequence keeping {
.frame == get_next_frame_in_super_frame();
};
Layering inside one sequence driver is easy to write and understand. However, it only works well in
simple cases. For complex cases, you need a more general approach.
DUT
Multi-Layer seq
Architecture seq
seq
Frame SD
DUT
Packet BFM
Single-Layer
seq
Architecture seq
seq
Packet SD
The more general approach uses multiple sequence drivers (see Figure 5-4 on page 5-59).
Taking the frame and packet example, there would be a low-layer packet_sequence and a high-layer
frame_sequence (complete with their sequence drivers). The packet_sequence would pull data from the
frame_sequence_driver (or from its BFM).
Each sequence driver might even be encapsulated in an eVC so that layering could be done by
connecting the eVCs.
See Also
• “Using Layered Sequence Drivers” on page 5-64
You could also have multiple kinds of layer1 and layer2 packets (as in Figure 5-5 on page 5-61). In
different configurations you might want to layer any kind of layer2 packet over any kind of layer1
packet.
The rest of this section describes some possible variations and complications, depending on the
particular protocol or on the desired test-writing flexibility.
Layer 1a heade
header kind
kind Payload (32 bytes)(32 bytes)
Payload CRC
CRC
Many to One
Layer 2a address Payload (31 bytes)
2A
Layer 1b header kind Payload (64 bytes) CRC
Many to Many
add len Payload (20 bytes) add len Payload (20 bytes) add len Payload (18 bytes)
2B
hea kind
header kind Payload (32 bytes)
Payload (32 bytes) CRC
CRC header kind Payload (32 bytes) CRC
The conversion mechanism might need to cope with any of the following situations:
One-to-one One high-layer packet must be converted into one low-layer packet.
One-to-many One big high-layer packet must be broken into many low-layer packets.
Many-to-one Many high-layer packets must be combined into one big low-layer packet (as in
Sonet).
Many-to-many Multiple higher-layer packets must be taken in and converted into multiple
lower-layer packets. For example, high-layer packets are 10 bytes long, and
low-layer packets are 3 to 35 bytes long. In this case, there could be remainders.
For example, in one configuration, you might have only layer1 packets. In another configuration, layer1
packets would be dictated by layer2 packets. You might also want to decouple the layers further, for
example, so that layer2 packets could drive either layer1 packets or layer1 cells (at another interface) or
both.
The general picture here is boxes with connecting pipes. At pre-run generation, you can connect the
pipes according to the required configuration or topology.
Sometimes, you might want to decide on the mix of input from multiple sources at runtime. For
example, you might want to have one low-layer sequence driver send n packets that come from one
high-layer sequence driver and then m packets from another high-layer sequence driver.
In other configurations, the low-layer sequences pace the operation. When a low-layer do is executed,
the corresponding high-layer packet should appear in zero time, much like with reactive sequences.
Finally, there is a case where things are driven into the DUT according to the timing of the low-layer
sequences, but the high-layer sequences are not reacting in zero time. Rather, if there is no data available
from the high-layer sequences, then some default value (for example, a zero filler) is used instead.
Often, however, both layers influence what reaches the DUT. For example, the high layer might
influence the data in the payload while the low layer influences other attributes of the items reaching the
DUT. As such, the choice of sequences for both layers is meaningful.
Serial number handling Assume that each packet has serial numbers, and serial numbers should
be given using some global algorithm that takes into account all
high-layer requests.
Retransmit handling Assume that this is a two-way protocol. The other side can send back
ACK and NACK (acknowledge and not acknowledge) packets, and the
high-layer protocol must retransmit some packets accordingly. At the
same time, you might already be outside the high-layer request that
created the original packet for which a NACK was received.
Complex blocking issues Assume that a given low-layer sequence driver is sending packets from
one high-layer source that drives a high-layer transaction to one target
(slave). Assume also that the target cannot receive packets for other
high-layer transactions; however, other targets can. Thus, the given
low-layer sequence driver can process multiple high-layer transactions
at the same time, as long as you do the necessary queueing so that any
subsequent high-layer transactions to the same slave wait for their turn
in the correct queue.
One thing common to all of these examples is the need for some global information (the counter in the
first example and the queue in the last example) and some corresponding global control.
In the example configuration shown in Figure 5-7 on page 5-64, a low-layer sequence driver (L1B) gets
input from multiple high-layer sequence drivers (two instances of L2A) as well as from a virtual
sequence driver.
L2B SD
seq
seq
seq
L2A SD L2A SD L2A SD
seq seq seq
seq seq seq
seq L2B BFM seq seq
L1A L1B SD
seq seq
seq seq
seq seq
DUT
Assume that at the low layer you have a packet_sequence (and a packet_sequence_driver), and above it
you have a frame_sequence (and a frame_sequence_driver). Packets can either be layered (that is, their
payload contains data from frames) or independent (that is, their payload is random). (See Figure 5-4 on
page 5-59.)
• Lowest-layer sequence drivers send information to a real BFM (which typically toggles actual DUT
signals).
• Higher-layer sequence drivers only send information to lower-layer sequence drivers. They may or
may not have their own BFMs, depending on whether complex transformations are needed from the
higher layer to the lower layer.
• Lower-layer drivers point to the higher-layer driver(s) from which information must be pulled (using
get_next_item() / try_next_item() or some connector method). The actual pointer configuration is
normally decided during pre-run generation.
The pulled information (either the higher-layer data or some transformation thereof) is put in a field
of the sequence and is then used to constrain various fields in the lower-layer item(s).
Following is an example of a connector packet sequence that pulls a single frame from the frame
sequence driver and then sends it as packets:
extend FRAME_SENDER packet_sequence {
!frame: frame;
!packet: packet;
body() @driver.clock is {
-- Get the frame from the frame_driver
frame = frame_driver.get_next_item();
emit frame_driver.item_done;
var full_data: list of byte = pack_the_frame();
• You can get information from the higher layer by calling frame_driver.get_next_item() directly and
getting a frame (as was done in the single-item connector example above):
frame = frame_driver.get_next_item();
• You can call a method of the higher-layer BFM, not a method of the higher-layer sequence driver.
For example, you might want the BFM to do some transformations on the higher-layer data, returning
a list of bytes somehow extracted from it. For example:
raw_data = frame_bfm.get_data();
Note The BFM is called here in PULL_MODE. It would probably use get_next_item() to get the
data from the higher-layer sequence driver, also in PULL_MODE. (See Figure 5-8 on page 5-66.)
• You can call some intermediate connector method in the lower-layer sequence driver, which will
later be extended (see “Late Binding” on page 5-68). For example:
raw_data = driver.get_payload();
Frame SD
seq
seq
seq To achieve complex data
transformation, we need a
BFM or a third-party
Frame BFM
transformer unit.
Packet SD
seq
seq
seq This sequence pulls
from the Frame BFM.
Packet BFM
DUT
To allow for single-item granularity, you can use a single-item connector sequence, as demonstrated in
“Overview of the Layering Solution” on page 5-64. The sequence takes a single high-layer item (frame in
the example) and produces one or more low-layer items (packets in the example).
To do that, you can extend the basic, low-layer sequence (rather than a specific CONNECTOR
sequence) and pull the information from above using the pre_do() method. This lets you avoid changing
the original body() method (which was customized for each sequence in the library).
extend packet {
keep in_sequence(packet_sequence) (p) and p.use_layering =>
data == p.raw_data;
};
Loading the above code and constraining use_layering to TRUE makes that sequence (and sequences
underneath it) use layering:
extend MAIN packet_sequence {
keep use_layering == TRUE;
};
Note This allows conditional layering. If that is not needed, you can remove the use_layering flag and
the logic around it.
Quite often, there is no way to mix layered and independent data items without breaking the protocol
and causing the DUT to go into an error state.
For example, the protocol might involve complex retransmit and blocking rules (see “Complex
Inter-Packet Dependencies” on page 5-63). The logic for these rules could either be embedded in the
BFM of the high-layer sequence driver (in which case the low-layer sequence is relatively free from
restrictions) or embedded in the low-layer sequence (in which case it becomes protocol-aware and has
very little freedom).
A protocol-aware sequence is typically a multiple data-item sequence (quite often lasting throughout the
test). Generally, it pulls the full high-layer item (frame in the example) as is from the layer above, adding
it to some global list (in the sequence itself or in the sequence driver). It then starts to create low-level
data items as needed. As required (for example, upon a NACK), it would consult the global list and other
global fields and do the appropriate thing (for example, retransmit some packets).
While this sounds like one, monolithic sequence, it might still have some degree of freedom (perhaps
freedom in timing or various ways to respond to a NACK). These should be encoded as generatable
fields in the sequence (and its subsequences), which could be constrained from outside.
One Person
Sometimes, the same party two layers and the connection between them are written together. This
typically occurs when the two layers always go together.
Two Persons
In some cases, the lower layer was written by a person with no knowledge of layering. Thereafter, the
higher layer is written by another person specifically for that lower layer. In this case (as in the previous
one), this strong coupling makes it possible to customize the BFM of the higher layer as needed to match
the low layer.
Three Persons
In this case:
• Person A writes the lower-layer part (item, sequence, sequence driver, BFM).
• Person B writes the higher-layer part (item, sequence, sequence driver).
A and B do not know about each other and might even ignore the need for layering.
• Person C connects the two by writing the connector sequence(s) and the rest of the connecting code.
Four Persons
This more general case is the one implemented in the ex_layers package. It goes as follows:
This more flexible scheme allows for the case where each lower-layer sequence driver has multiple
connector-unit references that are only wired at configuration time. Thus, a particular sequence (written
by person C or by the test writer) could take raw data from one connector unit and then some more data
from a second connector unit without caring whether the two connector units are of the same type or not.
Example
1. Define a generic list of connector units and the empty connector methods (get_payload(),
get_kind()), which initially just issue an error.
Note At this point this is not associated with any specific higher layer.
extend ex_layer1a_driver_u {
2. Define a sequence kind that pulls in a payload and a kind from a (yet unspecified) higher-level
sequence driver (or BFM) and stuffs it into a packet.
-- Before do-ing items, grab the required data from the higher-layer
-- protocol by calling empty connector methods defined in the driver
-- (defined in Part 1).
pre_do(is_item : bool) @sys.any is also {
if is_item {
raw_data = driver.get_payload(connector);
packet_kind = driver.get_kind(connector);
};
}; // pre_do()
3. In a separate file, specify how to connect layer1a to a specific higher-level driver (layer2a), by
extending get_payload().
extend ex_layer1a_driver_u {
get_payload(connector : any_unit) : list of byte @sys.any is first {
if connector is a ex_layer2a_driver_u (p) {
var packet := p.try_next_item();
if packet != NULL { // Has a real packet
emit p.item_done;
return pack(packing.low, packet);
} else { // No real packet. Therefore, return a filler
result.resize(32);
return result;
};
};
}; -- get_payload()
};
trace sequence on page 5-71 For runtime tracing of the creation of sequences and items
wave sequence on page 5-74 For runtime tracing of the items sent by the sequence driver in
the waveform viewer
show sequence on page 5-74 For postmortem analysis of specific sequence drivers
These three commands display information on sequences and items using the nice_string() method. For
information on the default and customized output of nice_string(), see “Customizing Display of Trace
and Debug Output” on page 5-75.
Syntax
trace seq[uence] [on | log_only | off]
Description
The trace sequence command turns On/Log Only/Off the trace flags inside the sequence drivers. When
the trace flag is On or Log Only, this causes each subsequence and item to be added to a trace list within
the corresponding sequence driver, to be used1 by show sequence.
When the trace is On, the information is displayed on the screen immediately.
1.
You can get the trace list of the driver with the following methods:
Also, there is a method, any_sequence.post_trace(), which gets called for every sequence just after a
trace message about it gets printed. This is useful for breakpoints.
LOW • When starting and ending sequences that were started using start_sequence()
• When rerunning a sequence driver
• When executing the default body of the MAIN sequence
MEDIUM • When do-ing sequences (at the beginning and end)
• When stopping or quitting sequences (using stop() or quit())
HIGH • When do-ing sequence items (at the beginning and end)
By default, trace sequence messages are printed to screen. You can also print the messages to file.
2. Configure the new logger to accept messages with the TRACE_SEQUENCE tag and the appropriate
verbosity.
3. (Optional if you do not want trace sequence messages printed to screen also) Remove the
TRACE_SEQUENCE tag from the list of tags accepted by sys.logger.
Note You must do this step in the run phase and not at pre-run.
Example
extend sys {
};
};
See Also
• Chapter 6 “Messaging”
Syntax
wave seq[uence] [num]
Description
The wave sequence command adds the specified BFM sequence driver (default: all of them) to the
waveform viewer. The num is according to the numbering of the show sequence command. For each
sequence driver, the following 6 lines are added to the waveform:
Syntax
show seq[uence] [num]
Description
The show sequence command shows all sequence drivers.
If you specify a num, it shows the trace list of the sequence driver with the index num
(driver.get_index()==num).
The trace list output is shown in a window. The format looks as follows:
Sequences and items in ex_atm_driver-@1 =
0. drvr 0 0: MAIN ex_atm_sequence-@2
1. drvr 0 1: PARALLEL_3 ex_atm_sequence-@3
2. drvr 0 2: SHORT_LONG ex_atm_sequence-@4
3. drvr 0 2: SHORT_LONG ex_atm_sequence-@5
4. drvr 0 2: SHORT_LONG ex_atm_sequence-@6
5. drvr 0 3: ex_atm_cell-@7 (A1 GREEN 2)
6. drvr 0 3: ex_atm_cell-@8 (A1 BLUE 2)
7. drvr 0 3: ex_atm_cell-@9 (A1 GREEN 1)
8. drvr 0 3: ex_atm_cell-@10 (A3 RED 1)
9. drvr 0 3: ex_atm_cell-@11 (A4 GREEN 5)
10. drvr 0 3: ex_atm_cell-@12 (A4 RED 5)
11. drvr 0 3: ex_atm_cell-@13 (A1 GREEN 1)
12. drvr 0 3: ex_atm_cell-@14 (A4 RED 5)
Verisity recommends initially using trace seq to see all output directly on the screen. Then, as output
becomes too much, use trace seq log_only with show seq to see only the desired sequence drivers.
For items, nice_string() returns the instance name (for example, “atm_cell-@2”).
You can customize the output of nice_string() by extending or overriding the method.
5.13.1 start_sequence()
Purpose
This method of any_sequence is used to initiate the run of a sequence.
Syntax
[sequence-exp.]start_sequence()
Parameters
None
Description
start_sequence() is a non-time-consuming method that starts the body() TCM of the sequence. Use this
method instead of starting the body() TCM directly. When doing so, a new sequence tree is executed.
Notes
• The start_sequence() of the MAIN sequence is called automatically by the driver.
• Before calling the start_sequence() of all sequences other than the MAIN sequence, you must
generate the sequence and connect the driver to the sequence using either a constraint or procedural
code.
• The parent_sequence field of a started sequence is NULL. A started sequence has no parent sequence,
because it serves as the root of a new sequence tree.
See Also
• Figure 5-9 on page 5-96
5.13.2 stop()
Purpose
This method of any_sequence is used to terminate the run of a sequence.
Syntax
[sequence-exp.]stop()
Parameters
None
Description
stop() is a non-time-consuming method that terminates the execution of body() (if such exists) by
killing its thread. This might become useful if, for example, you want to terminate a sequence in the
middle upon interrupt and execute an alternative sequence instead.
Notes
• Stopping a sequence also stops its containing tree. To stop only the sequence’s subsequence tree,
make sure that the sequence is started (not done) by its parent sequence.
• Stopping is done on a cycle boundary, so some non-time-consuming actions in the stopped sequence
might still be executed after the sequence itself was stopped.
• When stopping a sequence, the event stopped is emitted both for the stopped sequence and its root.
• When a sequence is stopped, it and all of its subsequences are released from the grab.
• For correct behavior of stop() for virtual sequences, make sure that the method get_sub_drivers()
of the virtual driver is implemented correctly and returns all BFM drivers to which the sequence and
its subsequences might send items. This is also important when stopping a BFM sequence that was
done by a virtual sequence. For more information about virtual sequences, see “Using Virtual
Sequences” on page 5-25.
5.13.3 grab()
Purpose
This method of any_sequence lets sequences have an exclusive control over a sequence driver.
Syntax
[sequence-exp.]grab(driver) @sys.any
Parameters
Description
grab() is a blocking TCM that grants its calling sequence an exclusive access to driver. A sequence that
wants exclusivity can call grab(driver). This grants exclusive access to that sequence and its
subsequences from the moment grab() returns (perhaps, after a slight delay) until the sequence calls a
corresponding ungrab(driver). During this grabbing period, the sequence driver belongs to the
sequence. The driver drives only the items that were done by the grabbing sequence or its subsequences.
When a sequence driver belongs to a sequence, only that sequence can send items to the sequence driver.
All other sequences are blocked from sending (that is, their items remain blocked).
Notes
• Virtual drivers cannot be grabbed, because they do not schedule items.
• Sequence drivers cannot be grabbed while they execute a do action on a sequence item. The grab is
granted immediately after the completion of the do action.
• Exclusive control means that the sequence and all its subsequences can use the sequence driver. All
other calls are blocked until ungrab().
• When grabbing a sequence driver, make sure that the is_relevant() method of the sequence that grabs
the driver returns TRUE. This averts a situation in which the driver cannot send items.
• grab() is hierarchical. If a sequence s1 grabs the sequence driver and then a sequence s2 enclosed
by s1 also grabs the sequence driver, there will be no delay as long as s2 has the sequence driver
within s1. So, for example, s3 (the brother of s2) cannot access the sequence driver. Only when s2
ungrabs does s3 finally get access, but everything outside s1 is still blocked.
• grab() is somewhat similar to lock(), but it is different in the following respects:
• grab() is hierarchical.
• grab() interacts with how items are sent.
• Rerunning a driver cancels the grab. If a sequence grabbed the driver before driver.rerun() was
called, the sequence must regrab the driver. If a sequence was blocked while waiting to get the grab,
then it is released without grabbing the driver. For more information, see “Resetting and Rerunning
Sequences” on page 5-39.
See Also
• grab_test.e in the ex_atm/examples directory (an example of grabbing and ungrabbing)
• ungrab() on page 5-80
5.13.4 ungrab()
Purpose
This method of any_sequence stops sequences from having an exclusive control over a sequence driver.
Syntax
[sequence-exp.]ungrab(driver)
Parameters
Description
ungrab() is a non-time-consuming method that releases the driver from the exclusive control of the
sequence that was previously granted by calling grab().
Notes
• A virtual driver cannot be ungrabbed, because it does not schedule items.
• Calling ungrab() when a sequence did not grab the driver results in error.
See Also
• grab_test.e in the ex_atm/examples directory (an example of grabbing and ungrabbing)
• grab() on page 5-78
5.13.5 is_blocked()
Purpose
This method is valid for BFM sequences only. It indicates whether a sequence is blocked from sending
items. It happens when another sequence that is not an ancestor has grabbed the sequence driver.
Syntax
[sequence-exp.]is_blocked(): bool
Parameters
None
Description
is_blocked() is a non-time-consuming method that returns TRUE if the sequence is blocked by another
sequence that grabbed the sequence driver.
See Also
• grab() on page 5-78
5.13.6 is_grabbed()
Purpose
This method is valid for BFM sequence drivers only. It indicates the grab status of the sequence driver.
Syntax
[driver-exp.]is_grabbed(): bool
Parameters
None
Description
is_grabbed() is a non-time-consuming method that returns TRUE if the sequence driver is grabbed by a
sequence.
See Also
• grab() on page 5-78
5.13.7 current_grabber()
Purpose
This method is valid for BFM sequence drivers only. It indicates which sequence (if any) has exclusive
control over a sequence driver.
Syntax
[driver-exp.]current_grabber(): any_sequence
Parameters
None
Description
current_grabber() is a non-time-consuming method that returns the sequence that currently has
exclusive control over the sequence driver. It returns NULL if no sequence currently has exclusive
control over the sequence driver.
See Also
• grab() on page 5-78
5.13.8 is_relevant()
Purpose
This method is valid for BFM sequences only. It indicates whether the sequence is currently relevant for
do-ing items.
Syntax
[sequence-exp.]is_relevant(): bool
Parameters
None
Description
is_relevant() is a non-time-consuming method that returns TRUE if the sequence is currently allowed to
do items. By default, this method returns TRUE. You can implement it to respond to the changing
conditions of the simulation. It is particularly useful for sequences used by reactive agents.
5.13.9 last()
Purpose
This method is valid for BFM sequence drivers only. It enables access to previously sent items in the
sequence driver.
Syntax
[driver-exp.]last(index): item
Parameters
index The index in the sequence history. 0 is the currently sent item, 1 is the
previous item, and so on.
Return Value
item The item that was created index items ago by the sequence. The return value
is NULL if such item does not exist.
Description
last() is a non-time-consuming method that lets you directly access items in the history of the sequence:
• last(0) returns the item currently being driven in (or the last item that was driven in, if there is no
item currently being driven in).
• last(1) returns the item before last(0), and so on.
The length of the history is determined by the field num_of_last_items in the sequence driver, which can
be constrained by the user. The default is 1.
Notes
• last(i) results in an error if i >= num_of_last_items.
• The maximum value for num_of_last_items is 1024.
• num_of_last_items is a static property of the BFM driver. Therefore, it cannot be changed during the
run.
Example 1
You could create a sequence that alternates colors from red to green as follows:
extend packet {
keep color == (driver.last(0) is a GREEN packet ? RED : GREEN);
};
Example 2
You could configure the buffer to store the last 10 items (instead of the default 1 item) as follows:
extend packet_seq_driver {
keep num_of_last_items == 10
};
Notes
• The is a construct is a convenient way to also avoid checking for non-NULL.
• For efficiency, in the sequence driver you might want to set Boolean flags that summarize the last()
information. In that case, the constraint can refer to the Boolean flags rather than last().
• You can also use last()-based constraints in sequences (not just items).
• The last() buffer is cleared upon driver.rerun().
5.14.1 in_sequence()
This pseudo-routine can be used inside sequences and items.
Syntax
[exp.]in_sequence(sequence_type) [(name)]
Parameters
sequence_type Any type or subtype of a sequence (that is, it inherits from any_sequence).
Description
The in_sequence() routine returns TRUE if exp is an item or a sequence that was created using do inside
a sequence of type sequence_type (at any depth). If name is specified, it assigns the found sequence to
name.
If exp is a BFM sequence driver sd, then in_sequence() returns TRUE if sd.last(0) returns TRUE.
in_sequence() can be used in constraints to determine whether an item is inside a specific sequence
type. This allows enforcing constraints on every item in the sequence tree under a specific sequence.
Therefore, this kind of constraint is called a tree constraint. It is useful when you want to say something
like:
“Every ATM cell under the FOO sequence should be green, regardless of how far down it is in the
hierarchy.”
Example
extend ex_atm_cell {
keep in_sequence(FOO ex_atm_sequence) => color == GREEN;
// Implements the condition above
Of course, the in_sequence() pseudo-routine can also be used in normal procedural code. For example:
if in_sequence(BAR ex_atm_sequence) (B) then {
print B.len1;
};
5.14.2 in_unit()
This pseudo-routine can be used anywhere.
Syntax
[exp.]in_unit(unit_type) [(name)]
Parameters
Description
in_unit() returns TRUE if you are inside such a unit (at any depth). If name is specified, it assigns the
found unit to name.
Note
• in_unit(foo) is equal to: try_enclosing_unit(foo) != NULL. The motivation behind this routine is
to make it shorter and similar to in_sequence() as well as to enable tree constraints.
See Also
• “Writing Tests Using Sequences” on page 5-20
The following tables contain the full list of methods and fields for “any_sequence_item Interface”,
“any_sequence Interface”, and “any_sequence_driver Interface”.
get_depth(): int Depth from the sequence driver, valid from pre-generation.
driver: driver_name Driver for the item, soft-constrained to be its parent sequence’s
driver.
nice_string(): string is empty A short string representing the item for tracing. It is used by
trace sequence, wave sequence, and show sequence (see
“Tracing and Debugging Sequences” on page 5-71).
The default implementation returns the value of to_string().
Notes
• The information in this section also applies to sequences.
• The driver field is not defined in any_sequence_item. For this field to be defined, you must use the
item in a sequence statement.
• The get_driver() method is declared as undefined in any_sequence_item. For this method to be
defined, you must use the item in a sequence statement.
• Never use is only on the pre_generate() or post_generate() of items or sequences. The
parent_sequence and driver of fields are assigned in the pre_generate() of any_sequence_item.
get_depth(): int Depth from the sequence driver, valid from pre-generation.
!parent_sequence: Backpointer to the sequence in which this sequence was created. Assigned
any_sequence; automatically in pre_generate() of the sequence if such a parent exists.
grab(driver: Grab the sequence driver for exclusive access and returns when you have
any_sequence_driver) exclusive access.
@sys.any is undefined
is_relevant(): bool Apply a condition for performing a do item action, so that the do action
will not be scheduled until is_relevant() returns TRUE.
driver: driver_name Driver for the sequence, soft-constrained to be its parent sequence’s
driver.
nice_string(): string is A short string representing the sequence for tracing. It is used by trace
empty sequence, wave sequence, and show sequence (see “Tracing and
Debugging Sequences” on page 5-71).
The default implementation returns the kind followed by the instance
name (for example, “RED atm_sequence-@2”).
body() @driver.clock is Main method called by do of parent sequence after it generates the current
empty sequence.
kind: kind_name The kind field that determines which sequence it is (within its when
family).
mid_do(s: Hook method called in the middle of do, just after s is generated and
any_sequence_item) before it is executed by calling its body() TCM.
is empty;
post_body() @sys.any Hook method called after body() when sequence is started using the
is empty; start_sequence() method.
post_do(s: Hook method called at end of do, just after the execution of s.body().
any_sequence_item)
is empty;
post_trace() Gets called for every sequence just after a trace message about it gets
printed. (Useful for breakpoints.)
pre_body() @sys.any Hook method called before body() when sequence is started using the
is empty; start_sequence() method.
pre_do(is_item: bool) Hook TCM called at start of a do performed by the sequence. ‘is_item’
@sys.any is empty; specifies whether you are in a context of do-ing an item or a sequence.
Note Some of the methods in Table 5-3 above are inherited from the any_sequence_item interface
shown in Table 5-2 on page 5-87.
BFM
Struct Member Description Only
Read Only Member (can only be accessed)
current_grabber(): any_sequence Indicates which sequence (if any) has exclusive Yes
control over a sequence driver.
BFM
Struct Member Description Only
get_current_item(): Returns the item currently being sent. (NULL if the Yes
any_sequence_item BFM is currently idle.)
get_item_trace_list(): list of Returns a list of the items handled (sent) by the Yes
any_sequence_item sequence driver (the item sublist of the trace log).
Note The list is populated only if the trace
sequence command was activated in On or Log
Only mode.
get_trace_list(): list of Returns a list of all sequences and items handled by Yes
any_sequence_item the sequence driver (the full trace log).
Note The list is populated only if the trace
sequence command was activated in On or Log
Only mode.
is_grabbed(): bool Indicates the grab status of the sequence driver. Yes
BFM
Struct Member Description Only
bfm_interaction_mode: Specifies the way the driver and the BFM interact Yes
bfm_interaction_mode_t with each other. Possible options are
PULL_MODE (the default) and PUSH_MODE. It
can be constrained.
See also “BFM-Driver Interaction Mode” on page
5-44.
num_of_last_items: int Used for setting the length of the history of Yes
previously sent items. Default is 1.
BFM
Struct Member Description Only
delay_clock() @sys.any This TCM can be used to emit the driver’s clock No
with some intercycle delay to let the BFM export
its state before activation of sequences in a specific
cycle.
BFM
Struct Member Description Only
Notes
• Some of the driver members are relevant only for BFM drivers. See the last column in the table above.
• Never use is only on the pre_generate() or post_generate() of drivers. The list of previously sent
items of the driver is initialized in the post_generate() of the driver.
• Never use is only on the run() or rerun() of drivers. Some important initializations are performed
in those methods.
start_sequence()
- Parent sequence is NULL.
- Sequence is already generated.
- Driver is not NULL.
Figure 5-9 describes the flow when starting a sequence using the start_sequence() method.
For more information on the start_sequence() method, see “start_sequence()” on page 5-76.
End of do subsequence
For more information on the do action, see “Activating Items and Subsequences” on page 5-13.
Figure 5-11 describes the flow when do-ing an item when driver.bfm_interaction_mode is set to
PUSH_MODE.
For more information on the do action, see “Activating Items and Subsequences” on page 5-13.
For more information on driver.bfm_interaction_mode, see “Hooking the Sequence Driver to the
Environment” on page 5-9.
Figure 5-12 describes the flow when do-ing an item when driver.bfm_interaction_mode is set to
PULL_MODE and driver.get_next_item() is used to receive items from the driver.
For more information on the do action, see “Activating Items and Subsequences” on page 5-13.
For more information on driver.bfm_interaction_mode, see “Hooking the Sequence Driver to the
Environment” on page 5-9.
No Yes
try_next_item()
returns with NULL Continue as in
Create a default item
get_next_item()
Send item to the DUT flow.
try_next_item()
returns an item.
Notes
• This flow occurs when driver.bfm_interaction_mode == PULL_MODE
• try_next_item() might take more than a cycle, in case a do is chosen and pre_do() takes more than a cycle
Figure 5-13 describes the flow when do-ing an item when driver.bfm_interaction_mode is set to
PULL_MODE and driver.try_next_item() is used to receive items from the driver.
For more information on the do action, see “Activating Items and Subsequences” on page 5-13.
For more information on driver.bfm_interaction_mode, see “Hooking the Sequence Driver to the
Environment” on page 5-9.
For more information on driver.try_next_item(), see “Applying Default Behavior When No Item Is Done”
on page 5-52 and Table 5-4 on page 5-89.
Note Currently the default severity of this deprecation is IGNORE. You will not get any warning or
error message unless you change the severity level.
For more information on the deprecation process, see “The Deprecation Process for Syntactic Changes”
in the e Language Reference.
• The name of the MAIN sequence field under a sequence driver changes from “sequence” to
“main_sequence”.
• Under the MAIN sequence, the name of the field “sequence” changes to “sub_sequence”.
• Under the RANDOM sequence, the name of the field “sequence” changes to “sub_sequence”.
Note Currently the default severity of this deprecation is IGNORE. You will not get any warning or
error message unless you change the severity level.
This enables the new names for the “sequence” field in the driver and its associated sequences.
If your code uses the old name for the MAIN sequence field under a sequence driver and you have set
the severity level to WARNING, you will receive a message like the following:
*** Warning: DEPR_SEQUENCE_FIELD_KEYWORD The keyword 'sequence' is used
as an identifier. Replace 'sequence' of my_sequence_driver with
'main_sequence' after constraining
my_sequence_driver.using_new_sequence_naming to TRUE.
For more information, search help for 'DEPR_SEQUENCE_FIELD_KEYWORD'
If your code uses the old sequence field name under MAIN and you have set the severity level to
WARNING, you will receive a message like the following:
*** Warning: DEPR_SEQUENCE_FIELD_KEYWORD The keyword 'sequence' is used
as an identifier. Replace 'sequence' of MAIN'kind my_sequence with
'sub_sequence' after constraining
my_sequence_driver.using_new_sequence_naming to TRUE.
For more information, search help for 'DEPR_SEQUENCE_FIELD_KEYWORD'
If your code uses the old sequence field name under RANDOM and you have set the severity level to
WARNING, you will receive a message like the following:
*** Warning: DEPR_SEQUENCE_FIELD_KEYWORD The keyword 'sequence' is used
as an identifier. Replace 'sequence' of RANDOM'kind my_sequence with
'sub_sequence' after constraining
my_sequence_driver.using_new_sequence_naming to TRUE
For more information, search help for 'DEPR_SEQUENCE_FIELD_KEYWORD'
For more information on the deprecation process, see “The Deprecation Process for Syntactic Changes”
in the e Language Reference.
Note Currently the default severity of this deprecation is IGNORE. You will not get any warning or
error message unless you change the severity level.
For more information on the deprecation process, see “The Deprecation Process for Syntactic Changes”
in the e Language Reference.
Description
You cannot use the same item type in two different sequence statements in the same environment.
Workaround
Create two (or more) subtypes and use the subtypes in the sequences (a different subtype for each
sequence type).
Example
You could create two different sequences for ATM cells as follows:
For example:
type cell_seq_kind: [A, B];
2. Add the type to the item for which you want to create sequences.
For example:
extend ex_atm_cell {
seq_kind: cell_seq_kind;
};
3. Use the sequence statement to create two different sequences for the two different subtypes.
For example:
sequence atm_A using item=A ex_atm_cell;
sequence atm_B using item=B ex_atm_cell;
Example
sequence my_seq using item=my_item;
// Create children of my_item only after the sequence statement
struct my_new_item like my_item {...}
6.1 Introduction
The messaging feature is a centralized and flexible mechanism to print out useful text messages to the
screen or to log files. It lets the code developer easily insert formatted and colored messages into the
code. It provides the end user with powerful and flexible controls to selectively enable or disable groups
of messages.
One of the most important aspects of messages is that they give end users a standard and unified
interface. For ease of use, those who receive ready-to-use eVCs must be able to debug or trace activity
inside the code. This is even more important when the user has multiple eVCs and will need all printouts
from all eVCs to have similar formatting and a standard centralized mechanism to control and filter the
messages. The messaging feature provides all of these capabilities.
Messages are different from plain out() and outf() printouts. They have a standard-format prefix that can
be turned on or off by the user.
Messages are also different from dut_error() printouts. They do not signify failure, and they do not
increment error or warning counters.
Uniformity
Many Specman users have invented their own company-wide or project-wide feature for writing
message output. This is often a macro on top of the out() action.
However, a uniform way to write message output is needed so that output appears in a standard format
(for example, starting with the current time). There must also be a standard way to configure message
output at various verbosity levels.
The need for uniformity is especially great in the context of eVCs, because an integrator using several
eVCs might want to make changes to all of the output (for example, disable all messages or enable all
messages).
In addition to writing to the screen, users should also be able to divert the output of specific message
actions to one or more files. This avoids the need to write a separate file logging facility.
Furthermore, there should be a way to compile a specified subset of the message actions so that they
incur no overhead at all.
The message loggers are instantiated by the programmer in the unit hierarchy, and then configured by
the end user via constraints (though they can also be configured via commands or procedurally).
Note There is a predefined message logger, sys.logger. Therefore, defining new loggers is not strictly
necessary. We recommend doing so for finer control.
Users can use the interactive show message commands to see all loggers and message actions.
Users can use the interactive set message commands to change the configuration of loggers (for
example, to request more or less output).
Examples
• Add some message actions, and put a logger in the unit:
unit my_dsp {
foo() is {
...
message(LOW, "Starting transmission");
-- LOW verbosity means this is an important message that will
-- be shown even when verbosity is set to 'LOW'
...
message(MEDIUM, "Sending packet ", pkt);
-- MEDIUM verbosity means this is a less important message
...
};
Note We recommend not referencing or accessing HDL signals and external ports from message
loggers.
Example
Prior to Specman 4.1.1, message loggers were structs and not units. Therefore, the following code ran
without error in earlier releases:
unit A {
logger: message_logger; // Incorrect
};
Beginning in Specman 4.1.1, message loggers are units and not structs. Therefore, you must declare
message loggers with the “is instance” clause required by units:
unit A {
logger: message_logger is instance; // Correct
};
For more information on the deprecation process, see “The Deprecation Process for Syntactic Changes”
in the e Language Reference.
• “Basic Messaging” on page 6-6: This is a QuickStart that might satisfy all of your needs. Even if you
plan to use some of the more advanced aspects of the messaging feature, we still recommend reading
this section before going on to the rest of the chapter.
• Everything after “Basic Messaging”: The rest of the chapter provides detailed explanation of the
various aspects of the messaging feature. Even if the “Basic Messaging” section seems to satisfy all
of your needs, you might want to glance at “Summary of Messaging Commands” on page 6-23 to see
all available commands (everything you can do to message loggers). We also encourage everyone to
read “Recommended Methodology for the Message Action in eRM eVCs” on page 6-43.
Examples
• short
[12030] AHB_0 M1: Bus 3 is done with reset
Notes
• The color of the short name (“AHB_0” in the example above) can be customized per eVC.
• The color of the time (“[12030]” in the example above) alternates between BLACK and GRAY
as the time changes.
• long
[12030] AHB_0 M3 (HIGH) at line 32 in @ex_bus_watch in ex_bus_unit-@4:
Bus 3 is done with reset
• none
Bus 3 is done with reset
Syntax
message([tag], verbosity, exp, …) [action-block]
Parameters
tag Used to direct the output (for example, to a file). Default is NORMAL
6.2.2.1 message_logger
message_logger is a unit whose job is to manage the output from message actions by:
6.2.2.2 sys.logger
sys.logger is predefined. It starts with LOW verbosity, NORMAL tag, and sending messages to the
screen. You can change these settings using constraints or commands.
For example, in Figure 6-1, the second message action has a verbosity of FULL, but the logger is set to
verbosity LOW. As a result, only the first message action is printed to screen.
sys.logger is predefined:
sys
logger (NORMAL (LOW) => screen) This message action will show on the screen.
foo()@clock is {
message (LOW, "Reset is done");
message (FULL, "Packet", packet_num, "completed:" …);
};
This message action will not (because sys.logger only
accepts LOW verbosity messages)
To enable that second message as well, you must set the verbosity of the logger to FULL via the
following command:
set message FULL
The default tag for a message action (if none was specified) is NORMAL.
By default, sys.logger recognizes messages with NORMAL tag. The default for all other loggers is to
ignore all tags. They have an empty tag list “{}”. Therefore, to activate a logger other than sys.logger,
you must set its tag list using commands or constraints.
The default destination for all loggers is to send messages to the screen only.
vr_xbus.elog
sys
logger (NORMAL (LOW) => screen)
vr_xbus_env_u
logger (NORMAL (FULL) => screen) This message:
file_logger (VR_XBUS_FILE (HIGH) => vr_xbus.elog) • Goes to file_logger (accepts the tag)
• Does not go to sys.logger (accepts
foo()@clock is { only NORMAL tag)
message (LOW, "Reset…");
message (FULL, "Packet…");
message (VR_XBUS_FILE, MEDIUM, "RESET: …");
};
1. Each logger has a list of destinations, a list of tags it recognizes, and a list of enabled messages.
2. Messages are handled by each logger on the way to sys that recognizes the message tag and has a
destination that closer loggers did not have.
3. Messages can be sent to multiple destinations but they only have one chance to go to any particular
destination.
• If a message is enabled in a handling logger, it is formatted and sent to all destinations of the
logger (except any destinations of a closer handling logger).
• If a message is not enabled in a handling logger, it is not sent to any of the logger’s destinations
(and it will never be sent to any of the logger’s destinations). (See Figure 6-3.)
vr_xbus_env
logger (NORMAL (LOW) => screen) This message will not be
shown. It is disabled for
file_logger (VR_XBUS_FILE (HIGH) => vr_xbus.elog) vr_xbus_env.logger and will
not continue to sys.logger
foo()@clock is { because of the above rules
message (LOW, "Reset…");
message (FULL, "Packet…");
message (VR_XBUS_FILE, MEDIUM, "RESET: …");
};
Figure 6-4 below shows an example of multiple eVCs with loggers. In the figure, the verbosity level of
each logger is shown in parentheses. The vr_xbus instance on the left is set to FULL verbosity. The
vr_xbus instance on the right is set to MEDIUM verbosity. (The logger for that instance is set to disabled
(transparent); but because sys.logger is set to MEDIUM verbosity, you still get messages from that
instance up to MEDIUM verbosity.) In the example, the file loggers of the two vr_xbus instances are
directed to two different files (left_xbus.elog and right_xbus.elog).
Because this
logger ignores all
left_xbus.elog tags, screen right_xbus.elog
messages from
this unit continue
to sys.logger
sys
logger (NORMAL (MEDIUM) => screen)
This message
soc_env_u logger (no tags=> screen) action is being
filtered
vr_xbus_env_u vr_xbus_env_u
logger (NORMAL (FULL) => screen) logger (no tags=> screen)
file_logger (VR_XBUS_FILE (HIGH) => left_xbus.elog) file_logger (VR_XBUS_FILE (HIGH) => right_xbus.elog)
foo()@clock is { foo()@clock is {
message (LOW, "Reset…"); message (LOW, "Reset…");
message (FULL, "Packet…"); message (FULL, "Packet…");
message (VR_XBUS_FILE, MEDIUM, message (VR_XBUS_FILE, MEDIUM,
"RESET: …"); "RESET: …");
}; };
• Verbosity NONE (that is, ignoring all input). The one exception is sys.logger, which defaults to
verbosity LOW.
• Writing to the screen
• Not writing to any file
• Empty tag list {}
These parameters (and many others) can be changed using:
Examples
• Add a screen logger.
extend vr_xbus_env {
// Use message_logger defaults: verbosity NONE, to_screen,
// Empty tag list {}
logger: message_logger is instance;
};
Note By default, each logger uses verbosity NONE, and its tag list is empty. This passes the
control to sys.logger until the logger is explicitly enabled.
extend vr_xbus_env {
file_logger: message_logger is instance;
keep soft file_logger.verbosity == LOW;
// Have all actions from all instances of eVC go to vr_xbus.elog
keep soft file_logger.to_file == "vr_xbus.elog";
keep soft file_logger.to_screen == FALSE;
keep soft file_logger.tags == {VR_XBUS_FILE};
-- Some writers might want to use NORMAL tag (same as screen)
};
Specifying the Short Name and Short Name Color in Message Output
eVC writers can specify the short name and short name color in message output per eVC. For example,
they might want the short name output to appear as follows:
Tips
• eVC writers might want more than one file logger. For example, they might want a data_file_logger
and a packet_file_logger, each showing information at different abstraction levels. To do this, use
separate tags for each file logger.
• eVC writers might want to have some hidden message actions that are enabled only to debug some
algorithm within the eVC. To do this, use special tags (for example, VR_XBUS_CRC).
• Split file output per eVC instance (if wanted). For example:
extend vr_xbus_env {
keep soft file_logger.to_file == append(name,".elog");
};
• To get more information during the run. This is done via commands. For example:
// Get more messages from all units
set message HIGH
• To make some messages stand out. For example, you could color all reset messages purple as follows:
set message –style=PURPLE "...Reset..."
• To change output format. For example, you can set the output format to long as follows:
set message –format=long
• If there are no handling loggers for the action, then the action is skipped. (For details on how Specman
computes the list of handling loggers for a message action, see “How Loggers Handle Messages” on
page 6-21.)
• If there are handling loggers for the action, the action creates a message (consisting of a list of string
plus related information) and sends it to all of the handling loggers. Those loggers then format the
message and send it to the screen or to files.
• For message(), the first string in the message is created by appending all of the expressions like out()
does. For messagef(), the first string is created using the format-exp, as in outf().
messagef(), like outf(), does not automatically add a \n (carriage return) after the message. Hence,
the users should normally end the format-exp with a \n. For example:
messagef(HIGH, "Packet number %d seen\n", i);
messagef() also allows things like:
messagef(HIGH, "And the winner is: ") {
if winner != NULL then {
out(winner.name);
} else {
out("Nobody");
};
};
However, if the whole messagef() output (including the optional action block) does not end with a
\n, then an extra \n is added.
• Finally, if an action block exists, it gets executed. It will probably contain further output-producing
actions, calls to reporting methods, and so on. The output of all of those is added, as a list of string,
to the message. As the message action can be enabled or disabled, code in an action block should not
modify the flow of the simulation in any way. Time-consuming operations in an action block are
strictly disallowed.
print the_packet;
};
-- Output this message and print the packet, at verbosity HIGH.
Note Users who are not satisfied with the way the three predefined formats look have full
programmatic control over formatting (by extending the format_message() method of the message
logger).
Example
[12030] AHB_0 M1: Bus 3 is done with reset
Note If short-name-path has not been set, then the default output appears as path-@instance_number.
Note The source and the struct-instance will both be blue hyperlinks in Specview.
Example
[12300] AHB_0 M1 (HIGH) at line 12 in @vr_ahb_send in vr_ahb_bfm-@77:
Bus 3 is done with reset
Example
Bus 3 is done with reset
The set message action refers to verbosity. For example, “set message -verbosity=MEDIUM” enables
all message actions whose verbosity is MEDIUM or lower.
Table 6-1 below shows the recommended usage of verbosity. Keep in mind that each level can assume
that all lower levels are also printing (and hence there is no need to repeat them).
NONE Critical messages that users will “WARNING: Running in reduced mode”
always want to see. (This level
cannot be disabled.)
LOW Messages that happen once per run or “Master M3 was instantiated”
once per reset.
“Device D6 got out of reset”
MEDIUM Short messages that happen once per “Packet-@36 was sent to port 7”
data item or sequence.
“A write request to pci bus 2 with
address=0xf2223, data=0x48883”
6.3.6 message_tag
Both message() and messagef() have an optional first parameter of type message_tag.
If you do not specify a tag, (that is, if the first parameter of message() is a legal value for verbosity), then
the value NORMAL is prepended. Hence:
message(MEDIUM, "Packet done: ", packet);
Example
Following is an example of specifying a message tag in the message action:
message(VR_XBUS_PACKET, MEDIUM, "Packet ", num, " sent: ", data);
-- This message action only goes to loggers that look for this
-- particular tag "VR_XBUS_PACKET".
Message tags are used for associating specific message actions with a message logger (see “Message
Loggers” on page 6-20).
We expect that most message actions will not specify a tag at all. (In other words, they will use the
default NORMAL tag.) Other tags will be used only in special cases such as:
Message loggers are defined programmatically and are attached as fields to various units in the unit
hierarchy. For example:
extend vr_xbus_env {
logger: message_logger is instance;
};
For each message logger you can specify the following things (via methods, commands, or constraints):
• Which subset of the message actions the logger will look at.
By default, each logger uses verbosity NONE, and its tag list is empty. This passes the control to
sys.logger until the logger is explicitly enabled.
• Which subset of the unit instances the logger will look at.
By default, this is the tree starting at the unit the logger is attached to. For example, each
vr_xbus_env.logger looks at the unit subtree under the corresponding vr_xbus_env.
• Which destinations (files or screen) to send output to.
• What format to use.
Typically, loggers are generated at elaboration time before the simulation run begins. You can create a
logger instance at run time, but it must be created as a field and not as a variable.
By default (before any file is loaded) there is just one message logger, called sys.logger. However, we
recommend that each eVC (and, optionally, each agent within the eVC) define one message logger for
writing information to the screen and zero or more message loggers for writing information to files.
Note We recommend not referencing or accessing HDL signals and external ports from message
loggers.
1. Whenever a message action is executed, Specman gathers the list of handling loggers. To compute
that list, Specman :
a. Determines the origin unit where the message action is executed, via get_unit().
Note get_unit() is a method of any_struct. message() and messagef() can be used anywhere
in your code.
These are normally all loggers instantiated in units between the origin unit and sys that recognize
the tag of the message except those loggers explicitly programmed to ignore the origin unit (for
example, by using the set message -units command).
c. Sorts the list of handling loggers on the basis of closest to the message action first and selects
the closest one for each destination.
a. Calls logger.accept_message().
If that method (which is extensible by the user) returns FALSE, then the message is ignored and
will not be processed by this logger any more.
b. Calls logger.format_message() to take the raw message and create a final list of string from it.
c. Sends the created list of string to each of the destinations (file or screen) associated with the
logger.
Example
Suppose only the NORMAL tag exists, and we have:
And then:
set message -logger=sys.xbus_1.agent_5.logger LOW
-- Assume this logger is configured to send to screen (the default)
• sys.xbus_1.agent_5.logger handles this message for the screen. However, as that logger has only
LOW verbosity messages enabled, the message is not sent to screen.
• sys.xbus_1.file_logger handles this message for the xbus_1.elog file. As that logger has HIGH
messages enabled, this message is sent to the file.
• Commands
• Methods of the logger
• Constraints
Commands and methods have almost equivalent power. (In fact, the commands are directly
implemented via the methods.) Configuring via constraints is less flexible, but it should be flexible
enough for most users. If more flexibility is required, you can use the procedural interface to fine-tune
the configuration after generation.
Constraints are used during pre-run generation to set the fields of the logger. Then, during
post_generate(), the logger fields are used to configure the logger.
This section first gives a brief look at all of the messaging commands and then goes into detail on each
of them. This section includes:
Notes
• The filter parameter in some of the commands in Table 6-2 and Table 6-3 specifies a subset of the
message actions to operate on. For example, a common filter is -verbosity as in
set message -verbosity=HIGH, which enables all message actions with a verbosity level of NONE
to HIGH.
• For -add and -replace, the meaning of -verbosity is from NONE to the verbosity specified. For
-remove, the meaning of -verbosity is from the verbosity specified to FULL.
• If no -verbosity is specified, the verbosity of -add and -replace is FULL. If no -verbosity is specified,
the verbosity of -remove is NONE.
Command Effect
set message [-logger=exp|all] -units=exp [on|off] Specify which unit instances are watched by
the logger.
set message [-logger=exp|all] -format=name Specify the format for the logger.
set message [-logger=exp|all] Specify the file flush_frequency for the logger.
-flush_frequency=num
set message [-logger=exp|all] -file=file [on|off] Add or remove files to be written to by the
logger.
set message [-logger=exp|all] -screen [on|off] Turn on and off writing to screen by the logger.
set message -style=style-name [filter] Change all the actions matching the filter to
show the message in specified style. (By
default, all messages are printed as BLACK.)
Command Effect
set message [-logger=exp|all] -ignore[_tags] Force the logger to ignore the specified tags.
[=tags|=all]
Command Effect
show message -logger=exp|all [-full] Show medium information for the specified
logger or all loggers (if -logger=all is specified).
show message -actions [filter] Show all message actions, each with its
associated loggers.
show message -units [=exp] Show all units, each with its associated loggers.
show message -actions -ignore [filter] Show the list of actions (matching filter) whose
tag is ignored by all loggers.
All options add the tag specified in the filter to the list of tags recognized by the specified logger. To
ignore a tag, use set message -ignore_tags (see below).
-replace Enables the actions specified in filter, and disables all others.
In most cases you want to specify directly (and not incrementally) what each
logger will do. Therefore, -replace is the default. For example:
set message LOW
The set message command has an optional filter parameter, specifying which of the currently loaded or
compiled message actions will be affected.
[-verbosity=]verbosity Matches the message actions whose verbosity is between LOW and
verbosity (when using set message -add or set message -replace) or
between verbosity and FULL (when using set message -remove).
Note Instead of -verbosity you can omit the switch name and just specify
the value. For example, “set message FULL” is the same as “set message
-verbosity=FULL”.
-tags=tags-list Matches the message actions whose tags are those specified by the list.
Note If the -tags option is not used, then only the message actions whose
tag is NORMAL are affected.
"string" Matches the message actions whose concatenated string is string. This is
the same string that would be shown using show message.
The string shown would be: “Bus … is done with reset”. And, for example,
you could match it with:
show message "... reset"
Note In the following example, the string would also be “Bus … is done
with reset”:
messagef(LOW, "Bus %d is done with reset\n", bus_num);
Note The "string" and @module parameters are the same as in the set check command. For more
information, see “set checks” on page 12-1 of the Specman Command Reference.
Example
set message -add @vr_ahb*
-- Add all actions in modules matching vr_ahb* to system.logger
set message -remove -verbosity=HIGH @vr_ahb*
-- Remove all message actions with HIGH verbosity in specified modules
set message -add -logger=sys.dsp.logger @vr_dsp*
-- Add to the specified logger all message actions in specified modules
set message -add -logger=sys.packet_logger -tags={VR_XBUS_PACKET}
-- Add to the specified logger all message actions whose message tag is
-- VR_XBUS_PACKET
For example, the following command removes all units under (and including) the audio_subsystem from
the set of units watched by sys.logger.
set message -units=sys.audio_subsystem off
As detailed in the description of loggers, a message action gets processed by a logger only if that logger
looks at both the executing message action and the unit in which the message action executed (as
determined by get_unit()).
Initially, each logger looks at its native unit tree, that is, the unit tree under the unit to which the logger
is attached. It is an error to specify -unit=some_unit if some_unit is not included in the native unit tree
for the logger.
Note u_exp can be either a unit or a list of units (in which case, the subtrees under each of the units are
turned on or off).
The set message -units command is useful if you want to see less output from one of the many instances
of, say, an eVC. For example, suppose you have:
uarts[10]: list of uart
The default extension for file is “.elog”. Thus the following command will write to foo.elog:
set message -logger=sys.main_file_logger -file=foo
Each logger can write to any number of files, in addition to optionally writing to the screen (see “set
message [-logger=exp|all] -screen [on|off]” on page 6-30).
Users often want to log information into separate files (for example, one file per eVC type or one file per
eVC instance, one file altogether, and so on). We recommend using the message action for that purpose
as well. Some points to keep in mind are:
• We recommend using a message_tag to mark message actions meant for going to loggers that will
write to files. For example, if you would normally want to write to two files (one for packet
information and one for byte information), we suggest defining two tags as in the following example:
extend message_tag: [VR_XBUS_PACKET, VR_XBUS_BYTE];
Then (using a command, a constraint, or a method) you can have only message actions using
VR_XBUS_PACKET go to the packet logger, and so on. With commands, this will look as follows:
set message -logger=sys.packet_logger -tags={VR_XBUS_PACKET}
set message -logger=sys.packet_logger -file=packet_log.elog
• When you use the set message -file=file command, Specman first checks if that file is already open
(via some other set message -file command). If the file is not open, Specman opens it (and writes a
message to the screen indicating that). If the file is already open, Specman simply marks that logger
as also going to that file.
• When you use set message -file=file off, Specman will stop writing to that file. When there are no
more loggers writing to a file, then Specman closes the file (and writes a message to the screen
indicating that).
• Specman flushes the buffer of each file after every n writes to that file, where n starts out as 10 but
can be modified using the set message -flush_frequency command (see “set message
[-logger=exp|all] -flush[_frequency]=num” on page 6-30).
• Every file gets flushed whenever you get to the Specman prompt.
• All files are flushed and closed just before Specman exits. There is no auto-closing at end of test.
Loggers start out with the short format. For details on how these formats influence the output, see
“Output Appearance” on page 6-18.
Note Users can extend message_format and can also format the output programmatically by extending
the method logger.format_message(). For more information, see “Messaging Procedural Interface” on
page 6-32.
The message output files are flushed out in any case when quitting Specman and any time you reach the
Specman prompt. However, if you are looking at the output files using, for example, the “tail -f” UNIX
command, you might want to flush out files frequently.
Using set message -flush=1 will cause flushing after each write (but will, of course, have some
performance implications).
Note Using the set message -flush_frequency command also flushes the files immediately (that is,
when the command is issued).
Notes
• Initially all messages are printed as BLACK.
• This command is global (to all loggers).
• This command only influences the actual message (that is, the information after the colon). You must
use unit.short_name_style() to influence the style of the short_name.
Notes
• If no tags are specified, then NORMAL tag is assumed.
• If “=all” is specified, all tags are ignored, which effectively disables the logger.
• The difference between -ignore_tags and -remove is that -remove eliminates the unique destinations
of a logger for messages with the specified tags, whereas -ignore_tags eliminates the impact of the
logger’s destinations on messages with the specified tags. When -ignore_tags is used, the end result
depends on the remaining loggers on the way to sys. Verbosity might be increased or decreased.
show message
This command shows a short summary of all loggers. The output looks like the following:
0. sys.logger - NORMAL (HIGH) ,TRACE_SEQUENCE (HIGH) ,TRACE_OBJECTION
(MEDIUM) - 35 actions
1. sys.ex_soc_env.atms[0].logger - 0 actions
2. sys.ex_soc_env.atms[0].file_logger - NORMAL (HIGH) - 24 actions
3. sys.ex_soc_env.atms[1].logger - 0 actions
4. sys.ex_soc_env.atms[1].file_logger - NORMAL (HIGH) - 24 actions
5. sys.ex_soc_env.atms[2].logger - 0 actions
6. sys.ex_soc_env.atms[2].file_logger - NORMAL (HIGH) - 24 actions
7. sys.ex_soc_env.c_bus_env.logger - 0 actions
8. sys.ex_soc_env.c_bus_env.file_logger - 0 actions
9. sys.ex_soc_env.logger - 0 actions
If -full is specified, then all information for the specified loggers is displayed. Otherwise, MEDIUM
information is displayed.
For each unit, it shows which loggers are looking at it (closest to the unit first).
• Sets the unit tree under root to be either on or off for the message logger.
• Corresponding command: “set message [-logger=l_exp] -units=u_exp [on|off]”
set_format(format: message_format)
set_style(verbosity: int, tags: list of message_tag, modules: string, text: string, style: vt_style)
• Sets the specified actions to the requested style (for example, GREEN)
• Corresponding command: “set message -style=style-name [filter]”
ignore_tags(tags:list of message_tag)
show_units(root: any_unit)
Note These methods can use the various query methods (see “Query Methods for Getting Message
Information” on page 6-35) to get information about the current message.
extend message_logger {
get_action_style(): vt_style
Return the vt_style for the current message action (for example, GREEN)
get_format(): message_format
get_message_action_id(): int
get_time(): string
get_verbosity(): message_verbosity
source_location(): string
Return the source location where the message occurred, for example, “At line 12 in @foo”
source_method_name(): string
Return the name of the method where the message occurred, for example, “foo()”
source_struct(): any_struct
source_struct_name(): string
Return the name of the struct type where the message occurred, for example, “packet”
The following example shows the use of query methods for formatting messages:
<'
extend message_logger {
case format {
long: {
s = append(time, short_name_path," (",verbosity_str,") " ,
source_location(), " in ",current_struct,
":\n",msg);
};
short: {
s = append(time, short_name_path,
(short_name_path == NULL ? " " : ": "), msg);
};
none: {
s = append(msg);
};
};
if (msg_list.size() > 1) {
result = {s;msg_list[1..]};
} else {
result = {s};
};
};
};
'>
At post-generation, the logger becomes attached to the unit (specifically, the unit computed by
logger.get_unit()).
In addition, each logger has several constrainable fields (see “Constrainable Fields and Their Default
Values” on page 6-38).
The post_generate() method of a logger uses the generated values of the fields to configure the logger
via the procedural interface described in “Messaging Procedural Interface” on page 6-32.
// The message tags for selecting the actions for this logger
tags: list of message_tag;
keep soft tags == {};
// The modules wildcard for selecting the actions for the logger
modules: string;
keep soft modules == "*";
extend message_logger {
set_base_unit(get_unit());
-- Set filtering
set_actions(verbosity,tags,modules,string_pattern,replace);
-- Set destinations
if to_file != "" then {
set_file(to_file, on);
};
if to_screen {
set_screen(on);
};
By default, all loggers have all tags ignored and a verbosity of NONE:
extend message_logger {
keep soft tags == {};
keep soft verbosity == NONE;
};
Tags are empty by default, because by default we want all messages to be controlled by sys.logger.
If you want one specific logger to be HIGH (and not ignored), use:
extend sys {
keep soft xbus_1.logger.verbosity == HIGH;
keep soft xbus_1.logger.tags == {MY_TAG};
};
If you constrain verbosity to any value above NONE and yet leave the tag list empty, Specman
automatically constrains the tag list to {NORMAL}.
6.8.1 Motivation
The short_name_path() feature provides units with a short name-path, to be used, for example, in the
short format of messages.
Example:
[123000] AHB_0 MASTER_1: Received first part of transaction-@33
6.8.2 Syntax
short_name_path(): string
In the example below, “AHB_0 Master_1” is the result of the message action calling
short_name_path().
[123000] AHB_0 MASTER_1: Received first part of transaction-@33
You can change this method to return the desired short-name for the unit. For example:
extend vr_ahb_env {
evc_name: vr_ahb_name; // e.g. AHB_0 etc.
extend vr_usb_agent {
index: int; // The index in some big list
1. Collects all of the non-empty short_name() strings along the path from sys to the unit.
3. If the result is not empty, returns it. Otherwise, returns something like “vr_xbus_env-@55”.
Notes
• If the eVC and the agent have short_name() defined as non-empty, but the BFM unit within the
agent does not define short_name(), then the short_name_path() for the agent and the BFM will
be the same.
This is acceptable. You do not have to invent short names for all of the leaves of the unit tree. It is
okay for messages coming from the agent and the BFM to all start with something like
“(20000) AHB_0 M1:”. In that case, presumably it either does not matter where exactly the
message is coming from, or it will become clear by the text of the message anyway.
• Suppose you have an SoC that contains AHB_0, AHB_1, and AHB_2. If you then construct a new
DUT that contains two instances of that SoC, then it is advisable to define a non-empty short_name()
for the whole SoC to maintain uniqueness of short-name paths.
In this case, the message will start with something like
“[123000] NORTH_RTR AHB_0 MASTER_1: …”.
• Beyond pre-generation, short_name_path() caches the result for performance.
For example, if you have Specman , you could get some red text by writing:
out("I see red: ", vt.text_style(RED, "Some red text"));
You can use vt.text_style() to colorize any part of your message. For example:
message(LOW, vt.text_style(ORANGE, "Reset"), " is done");
You can use any style you want from the list. We recommend avoiding BLUE (used for hyperlinks) and
both RED and DARK_ORANGE (used for errors).
Note These colors apply only to text going to screen. Text going to file is scrubbed of color
information.
6.9.2 any_unit.short_name_style()
In addition to short_name(), each unit has a method called short_name_style().
short_name_style(): vt_style
The default is BLACK. If you change vt_style to another color, then the message will contain the short
name in that color.
then the word “MASTER_1” in the message output below will also appear green (along with the
short-name path):
[123000] AHB_0 MASTER_1: Received first part of transaction-@33
Note The same style will also appear in the eVC banner that gets written after initial generation.
We recommend that developers not constrain the tags (accepting all defaults) so that sys.logger can
provide centralized control. To get more information about a particular instance, users can write
something like:
extend XBUS_1 vr_xbus_env {
keep soft logger.verbosity == HIGH;
};
Note Always use soft constraints to allow additional changes later.
As configured by the eVC developer, we recommend that all of these loggers go to the file
evc_name.elog and start out with LOW verbosity.
End users might choose to have HIGH verbosity for the file logger and LOW verbosity for the screen, or
vice versa. In that case, they could write:
extend vr_xbus_env {
keep soft file_logger.verbosity == HIGH;
keep soft logger.verbosity == LOW;
};
As defined above, the two streams go into the same file. However, end users (or integrators) can decide
to split them any way they like. For example, if there are three instances of the vr_xbus_env,
distinguished by their name (instance_id) field (which would be XBUS_1, XBUS_2, and XBUS_3),
then the end user might want six files, one for each (name, stream) combination. This can be done as
follows:
extend vr_xbus_env {
keep soft data_file_logger.to_file == append(name, "_data.elog");
keep soft packet_file_logger.to_file == append(name, "_packet.elog");
};
Short Names
We recommend that each eVC and each agent have a non-empty short_name(). Typically, this would be
the name (instance_id) field of the eVC or agent. For example:
extend vr_xbus_env {
short_name(): string is {
return append(name);
};
};
This ensures that the full short-name path printed with each message will look something like:
[10340] AHB_1 M2: Reset is done
Notes
• Going lower than the agent level (for example, defining a non-empty short name for the BFM) is
generally not needed. The context is usually clear from the message.
Styles (Colors)
For each eVC, providers should select a color that will be used when writing the short name of that eVC.
To see all available styles, use vt.show_styles().
For example:
extend vr_ahb_env {
short_name_style(): vt_style is {
return GREEN;
};
};
• They might decide to change some of the styles to make each eVC unique.
• They might also decide to have the styles go by eVC instance, rather than by eVC type, using
something like:
return (name == AHB_1 ? GREEN : name == AHB_2 ? CYAN : PURPLE);
• They might decide to color each agent separately. That is, they could add a (non-BLACK) style to
M1, M2, and so on. Generally we discourage this, because so many colors might cause a rainbow
effect.
However, occasionally, end users or integrators might want to change the message text color. For
example, they might want to color some rare messages so that they will be easier to spot. They can do
that as follows:
set message -style=CYAN "Reset ..."
For example, the logic for matching scoreboard items might be very complex and unsure. So the
developer could insert message actions like the following:
message(VR_XBUS_SCOREBOARD, HIGH, "Looking for match for ", the_packet) {
print the_packet;
};
This tag would be disabled by default, but it could be enabled when needed.
These messages use the TRACE_SEQUENCE tag, which is enabled by default in sys.logger.
Using the set message commands, you can write these messages to files, filter them by verbosity, and so
on.
See Also
• “trace sequence” on page 5-71
6.10.7 at_message_verbosity()
There is a global method at_message_verbosity(). Its syntax is:
The method returns TRUE if you are currently at the specified verbosity (or higher). It might be used as
follows:
extend packet {
do_print() is also {
if at_message_verbosity(HIGH) then {
out("Some more details about this packet:");
out("...");
};
};
};
If you are currently executing a message, at_message_verbosity() uses the verbosity of the current
message logger. Otherwise, it uses the verbosity of sys.logger.
Received packet-88
The exception to this is when you want to insert a message before some lengthy operation begins. In that
case you should use the present continuous tense. For example:
message(MEDIUM, "Sending ", packet, " to BFM");
send_to_bfm(packet);
While you can configure loggers in various ways; some standard recommendations follow.
6.10.10 sys.logger
We recommend that end users normally use sys.logger.
By default, only LOW verbosity messages go to screen. If end users just want messages at some
verbosity, they can use sys.logger. For example, they might issue the following command:
set message -verbosity=MEDIUM
Of course, they could achieve the same thing via a constraint as follows:
extend sys {keep soft logger.verbosity == MEDIUM};
End users should use the per-eVC and agent loggers only when they need more flexibility (for example,
higher verbosity for one eVC out of many).
Note Initially only sys.logger exists. The other loggers get created during the gen phase. Therefore,
until sys is fully generated, only sys.logger should be used.
• If you are running with a simulator, you can issue set message commands after the test command.
For example:
test
set message -logger=sys.soc1.logger -verbosity=FULL
... // Issue simulator command to run the test here
• Often, you might want to enable messaging only after a specific method has been reached. For
example:
break foo.bar
test
set message -verbosity=FULL
continue
• When running in standalone mode (without a simulator), there is no natural stop before the actual
run. Hence, you can set a breakpoint on sys.run. For example:
break sys.run
test
set message -verbosity=FULL
continue
• Commands that relate only to sys.logger can be issued even before initial generation. These
commands will also carry over after generation. For example, issuing the following command causes
the entire test to run in HIGH verbosity:
set message -verbosity=HIGH
test
While it is possible to define “unit my_logger like message_logger {…}” and instantiate my_logger, in
general we recommend using the original message_logger.
It is acceptable to extend message_logger and change, for example, the format_message() method.
The show message -actions command can be used to see what message actions exist in the current
verification environment. This lets you inspect the verification environment and see what it can do.
The show message -actions command also lets you find a specific message action and put a breakpoint
on it. For example, if you want to find the message action that printed “Channel 3 sent a packet” and you
want to stop in the debugger just before it is emitted, you can do the following:
For more details, see Chapter 12 “Specman Time Scale” in the Specman Elite Integrator’s Guide.
In the compliance check tables, the column that reads RQ/RC/ST indicates the nature of the check:
• RQ = Required
• RC = Recommended
• ST = Statistical
The various compliance rules can be relaxed somewhat for internal packages not intended for general
reuse. Nevertheless, to ensure general plug-and-play and reuse, some compliance checks must be
enforced, even for such internal packages. Those compliance checks are tagged as “eRM-Ready”. The
eRM-Ready checks are marked in the “ER” column of the following tables.
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
ARSD* ACTIVE agents with BFM sequence RQ 3 Active agents that contain
drivers— other agents are not required to
Do all ACTIVE agents have a BFM have a BFM sequence driver
sequence driver? in them.
See “A Look at Agents” on
page 4-6.
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
DCEX Examples— RQ
Does the documentation give sufficient
examples to cover the most likely user
scenarios?
RQ/
RC/ Comments/
Index Check ST ER Information
DCRS Reset— RQ
Does the documentation explain
whether the package manages multiple
resets during the test?
RQ/
RC/ Comments/
Index Check ST ER Information
DCSD SD documentation— RQ
Are all sequence-driver test interfaces
sufficiently documented?
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
RQ/
RC/ Comments/
Index Check ST ER Information
Code Structure
Coding Style
Test Interface
Note The toolbar at the top of each eRM Utility window lets you navigate to the other windows.
The chapter explains how to use the eRM Utility. It contains the following sections:
The eRM Packages Loaded window (see Figure 8-2) opens with a list of all loaded eRM packages. Each
package in the list is a hyperlink to the eRM Package window for that package (see “The eRM Package
Window” on page 8-5).
The eRM Packages on Disk window (see Figure 8-3) opens with a list of all packages on disk, by library,
whether loaded or not. Each package in the list is a hyperlink to the eRM Package window for that
package (see “The eRM Package Window” on page 8-5).
The eRM Package window shows the details about a package (title, version, and so on).
Clicking the directory hyperlink lets you examine the package files.
For loaded packages, there is an eDoc button that takes you to the eDoc window. This shows
auto-extracted documentation for the eRM package. (For more information, see “Using the eDoc Tool”
on page 8-21.)
For non-loaded packages, there is an option to activate the @demo.ecom of that package if it exists and
to activate the demo.sh of that package (in another window) if it exists.
Note Avoid running more than one @demo.ecom in the same Specview window. If you want to run
another demo in the same window, do a restart before activating the new demo script.
The Env Tree window (see Figure 8-5) opens with a hierarchical list of all instantiated envs. Clicking an
env hyperlink opens that env in the Specman Data Browser. Clicking the Visualize button next to an env
opens the Env window for that env (see “The Env Window” on page 8-8).
The Unit Tree window (see Figure 8-6) opens with a hierarchical list of all units under sys. Clicking a
unit hyperlink opens that unit in the Specman Data Browser. Clicking the Visualize button next to an
env unit opens the Env window for that env (see “The Env Window” on page 8-8).
Notes
• If you view the Env Tree or Unit Tree window before generation, there are no instantiated envs.
Therefore, you see only a Generate button. Clicking the Generate button generates the environment
and refreshes the window.
• If a unit has short_name() and short_name_style() defined for it, then the name and color are used
for that unit. (For information on setting the name and color, see “The short_name_path() of a Unit”
on page 6-40 and “Colors for Message Actions” on page 6-42.)
This window shows the basic information of the env (package name, title, HDL path, and so on),
followed by a list of the drivers in the env (for stripe-chart visualization), and finally followed by the unit
tree of the env. The eVC provider can customize the content of this window (see “Customizing
Visualization” on page 8-11).
From the Env window, you can go to the eDoc Report window by clicking the eDoc button. The eDoc
Report window shows auto-extracted documentation for that env. For more information on eDoc
reports, see “Using the eDoc Tool” on page 8-21.
From the Env window, you can display stripe charts for one or more sequence drivers by selecting the
desired drivers in the Stripe Chart list and clicking the Show button. For more information on stripe
charts, see “The Stripe Chart Window” on page 8-9.
This window is opened via the Show button in the Env window. It shows a stripe chart for one or more
input or output streams (usually sequence drivers). It displays each sequence and sequence item, sorted
by start time. The indentation shows the sequence hierarchy.
The hyperlinks in the stripe charts lead to the source code and the data browser for additional
information related to the sequence. The “tree” hyperlink displays the selected object in a tree view,
from which you can browse all subsequences and parent sequences.
For information on customizing the display of stripe charts, see “Customizing Visualization” on page
8-11.
The tree view starts with a single stripe. The tree hyperlinks (at the left end) let you expand all
subsequences (by clicking the plus “+”) or parent sequences (by clicking the caret “^”).
1. Run vr_xbus/demo.sh.
2. Rather than doing the normal load, in the Specview window, enter:
@vt_util/examples/vr_xbus_visualization
3. From the User menu in Specview, choose “eRM Utility” to see the customized window of the env.
5. Go back to the vr_xbus Env window, and browse through the (now updated) stripe charts.
You can see how the customization was done by examining vt_util/examples/vr_xbus_visualization.e.
The HTML template used is vt_util/examples/vr_xbus_template.html.
Note By default, sequence stripes are disabled to reduce overhead. In other words the eRM Utility does
trace sequence off, and therefore the sequence stripes are empty. To enable sequence information in
stripe charts, issue the trace sequence log_only command.
This section explains how to customize the Env and the Stripe Chart windows. It contains:
};
};
s.add_box("size",size,CYAN);
};
};
Return
Method Description Parameters Value
Methods of any_sequence_item
Return
Method Description Parameters Value
Message Loggers Shows basic information for each message Figure 8-11 on page
logger with hyperlinks to each message logger’s 8-17
individual Message Logger window.
Message Logger Shows detailed information for a specific Figure 8-12 on page
message logger and lets you make on-the-fly 8-18
changes to that logger’s settings for recognized
message tags.
Logger Destinations Lists all available message destinations, showing Figure 8-13 on page
for each its associated loggers. The logger 8-19
hyperlinks open the relevant Message Logger
window.
Messages Lists all messages with details on verbosity, tag, Figure 8-14 on page
logger, and package. The message hyperlinks 8-20
display the source code for that message.
See Also
• Chapter 6 “Messaging”
The Message Loggers window (Figure 8-11) lists all message loggers. Clicking a logger hyperlink takes
you to a Message Logger window for the specified logger (Figure 8-12) where you can make on-the-fly
changes to the settings for recognized message tags and verbosity.
The Message Loggers window (Figure 8-11 on page 8-17) opens in a new window.
Tip Clicking the Loggers button in an eRM Utility messaging window also opens the Message
Loggers window (Figure 8-11).
In the Message Logger window for a particular logger (Figure 8-12), you can change the verbosity for a
message tag, remove tags from the message logger, and change the logger’s message format.
The eRM Utility also has a Logger Destinations window that lists all possible logger destinations with
their associated loggers. The logger hyperlinks display the corresponding logger window (see Figure
8-12).
This Messages window (Figure 8-14) lists all messages with details on verbosity, tag, logger, and
package. The message hyperlinks display the source code for that message.
eDoc reports consist of tables of fields, methods, and events for all units, structs, and sequences at the
specified level of encapsulation. In addition, sequences also have tables of their subtypes.
At the beginning of each report is a summary of all structs. At the end of each report is a table of all
user-defined types.
The eDoc window (Figure 8-15) lets you select the package or individual files and automatically
generate an eDoc report (see “Generating eDoc Reports” on page 8-23) from the source code.
The information for the eDoc report (Figure 8-16) is collected from the code of a loaded package or
module. For each unit, struct, and sequence, eDoc can collect all fields, events, and methods with their
in-line documentation.
Comments above the code line or at the end of the line are collected into the description field for the
item. All information is concatenated into a single string that fits in a single table cell. HTML formatting
can be used for HTML output, for example, <br> for new line.
eDoc generates a report and opens it in an eDoc Report window (Figure 8-16).
The Structs table in the eDoc report contains hyperlinks to the tables relating to the selected struct
(fields, methods, and events). Struct members have hyperlinks to the source code where they are
defined.
Purpose
Generate an eDoc report
Category
Command
Syntax
show doc [-l[oad[_order]] -net[scape]] [-public] [-protected] [-package] [-private]
package_name|@modules
Syntax Examples
show doc vr_xbus
This displays an HTML report for the vr_xbus package in the VT window. You get the same result when
you click the eDoc button in the VT window for the vr_xbus package.
show doc -net -public -protected @vr_ahb*
This displays an HTML report in Netscape for all public and protected fields, events, and methods of
modules with names beginning with “vr_ahb”.
Parameters
-public Show all fields, methods, and events with encapsulation “public”. (If no
encapsulation option is specified, then this is the default. If any encapsulation
option is specified, then public is displayed only when specified.)
-protected Show all fields, methods, and events with encapsulation “protected”.
-package Show all fields, methods, and events with encapsulation “package”.
-private Show all fields, methods, and events with encapsulation “private”.
Description
You can display eDoc HTML reports in either the VT window (the default) or in a Netscape browser
window with a sort order that is either alphabetical or chronological (based in respect to loading). You
can specify the level of encapsulation for the fields, methods, and events to be reported. Reports can be
generated for packages or modules. Wildcards are accepted.
In 4.x, a new predefined method rerun() (see “rerun()” on page 9-4) is defined for units and structs in e.
It manages termination and restart of TCMs and temporals during reset.
• Ideally, eVC-1 drives some HW signals that eVC-2 is sensitive to. Thus reset is sensed by eVC-2.
• If reset is initiated by another e component and you want to do it all in e, do that by calling
rerun(), not by emitting an event.
• rerun() is less prone to races, because it schedules the quit() to be done as the last thing of the
specman tick, and the run() to be done as the first thing of the next Specman cycle.
Note Specman tick refers to the current call to specman_tick(). The next Specman cycle is a
call to specman_tick that has a new sys.time.
9.1.3 rerun()
Syntax
[instance-exp.]rerun()
Syntax Example
master.rerun();
Description
The rerun() method:
• Deactivates a struct/unit instance at the end of tick by calling quit(). This kills all TCMs, events, and
expects of the instance.
• Reactivates the instance at the beginning of the next tick by calling its run() method. This activates
the events and expects of the instance.
Note
• rerun() of an instance does not call the rerun() of its children recursively. The same is true for quit().
rerun() is also {
-- If my_sub_structs is a list of structs under xyz_agent...
for each (s) in my_sub_structs {
s.rerun();
};
};
};
Users can of course achieve this with the absolute path from sys to the instance. For example:
extend sys {
keep my_environment.my_evc_instance_a.masters[3].speed == 5;
};
However, this is not a particularly user-friendly interface. Giving each instance a logical name or ID
results in a far more powerful interface. The logical name or ID can be used in constraints within the
struct or unit to achieve differentiated behavior between different instances.
There are two methods of providing this functionality: Enumerated Logical Names and Scalar IDs.
Where practical, we recommend the Enumerated Logical Names approach.
Each time users want to create a new instance of the struct or unit, they must extend the enumerated type
to create a new logical name and then create an instance of the struct or unit that has its name field
constrained to the new logical name. For example:
extend sys {
my_evc_a : my_evc_env_u is instance;
keep my_evc_a.name == INSTANCE_A;
};
With this approach, users can then subtype the struct or unit using the logical name to apply constraints
to a specific instance. For example:
extend INSTANCE_A my_evc_env_u {
keep speed == 5;
};
Note It is possible to have multiple instances of a struct or unit with the same logical instance name.
Constraints applied to the subtype of the struct or unit will then apply to all instances with that logical
instance name. This lets users create a group of instances that have identical behavior.
extend my_evc_env_u {
agents : list of my_evc_agent_u is instance;
keep for each (agent) in agents {
agent.id == index;
};
};
Note Use of a scalar ID means that the struct or unit cannot be subtyped. However, the ID can still be
used to control constraints in extensions of the base type. For example:
extend my_evc_agent_u {
keep id == 5 => port_name == “PORT_A”;
};
We recommend giving all major units in an eVC a logical name or ID field (for example the top level
env unit, the agent unit, and so on).
Moreover, it often helps for data structs to have fields that indicate the logical name or ID of the unit by
which they were generated. For example, a packet struct might have a field indicating the logical name
of the agent that generated it. This lets users constrain fields within the data struct according to the agent
that generates it.
vr_xbus_types.e
type vr_xbus_agent_name_t : [];
vr_xbus_agent.e
unit vr_xbus_agent_u {
name : vr_xbus_agent_name_t;
};
vr_xbus_env.e
unit vr_xbus_env_u like any_env {
name : vr_xbus_bus_name_t;
agent_names : list of vr_xbus_agent_name_t;
keep soft agent_names.size() == 0;
agents : list of vr_xbus_agent_u is instance;
keep agents.size() == agent_names.size();
keep for each (agent) in agents {
agent.name == agent_names[index];
};
};
vr_xbus_trans.e
struct vr_xbus_trans_s like any_sequence_item {
agent_name : vr_xbus_agent_name_t;
};
vr_xbus_sequence.e
sequence vr_xbus_sequence using
item = vr_xbus_trans_s,
created_driver = vr_xbus_driver_u;
extend vr_xbus_driver_u {
agent_name : vr_xbus_agent_name_t;
};
extend vr_xbus_sequence {
agent_name : vr_xbus_agent_name_t;
keep agent_name == driver.agent_name;
// For "do trans"
!trans: vr_xbus_trans_s;
keep trans.agent_name == agent_name;
};
extend vr_xbus_agent_u {
vr_xbus_coverage.e
extend vr_xbus_trans_s {
event trans_done;
cover trans_done is {
item name : vr_xbus_agent_name_t = agent_name
};
vr_xbus_config.e
extend vr_xbus_bus_name_t : [NORTH_XBUS, SOUTH_XBUS];
extend vr_xbus_agent_name_t : [AGENT_N_A, AGENT_N_B,
AGENT_S_A];
extend sys {
xbus_north : NORTH_XBUS vr_xbus_env_u is instance;
xbus_south : SOUTH_XBUS vr_xbus_env_u is instance;
};
extend NORTH_XBUS vr_xbus_env_u {
keep agent_names == {AGENT_N_A; AGENT_N_B};
};
extend SOUT_XBUS vr_xbus_env_u {
keep agent_names == {AGENT_S_A};
};
extend AGENT_N_A vr_xbus_agent_u {
keep min_addr == 0x0000; keep max_addr == 0x7fff;
};
extend AGENT_N_B vr_xbus_agent_u {
keep min_addr == 0x8000; keep max_addr == 0xffff;
};
extend AGENT_A_A vr_xbus_agent_u {
keep min_addr == 0x0000; keep max_addr == 0xffff;
};
Test_1.e
extend AGENT_N_A MAIN vr_xbus_master_sequence {
body() @driver.clock is only {
write(1, 0x0, 0xff);
};
};
extend AGENT_N_B MAIN vr_xbus_master_sequence {
body() @driver.clock is only {
write(1, 0x8000, 0xff);
};
};
extend AGENT_S_A MAIN vr_xbus_master_sequence {
keep sequence.kind.reset_soft();
keep soft sequence.kind == select {
90 : SEQUENCE_A;
10 : SEQUENCE_B;
};
};
};
};
In some cases, users will not be able to subtype the eVC agent to extend a method. We need to provide
parallel mechanism that does not require subtyping.
// eVC hook
extend vr_xbus_agent_u {
current_transfer : vr_xbus_trans_s;
event transfer_complete;
};
// User code e.g.
event transfer_sent is
@sys.my_evc.my_agent.transfer_complete;
on transfer_sent {
sys.my_scrbd.add_item(sys.my_evc.my_agent.current_transfer);
};
This mechanism is incorporated as a beta feature in the evc_util of erm_lib. The golden eVCs
demonstrate its use.
Note The current version of this spec is similar in spirit but different in some details from earlier
versions.
• It must allow waiting until the proposed status is propagated all the way to sys or (if required) until
the status is propagated to some intermediate units.
• The proposed status might be applicable only to some eVCs and not to others.
• Status coordination can be easily debugged.
When a consensus to stop is propagated all the way to sys, the stop_run() method is called
automatically. The stop_run() method can also be called directly at any time (for example, for
debugging purposes).
objection_kind
The predefined enum type objection_kind contains all topics for which the objection mechanism is
needed.
It initially contains the single value TEST_DONE, but it can be extended by the user.
• raise_objection(kind: objection_kind)
• drop_objection(kind: objection_kind)
A unit calls these methods when it wants to raise or drop an objection to objection_kind.
For more information on the methods of any_unit, see “Methods of any_unit” on page 9-18.
Debugging Aids
For each of the units, the show objection command shows the current objection counter and objection
total. (For more information on the objection counter and objection total, see “Objection Counter and
Objection Total” on page 9-18.
The trace objection command turns tracing of the objection mechanism on and off. When on, each
objection raised or dropped prints a message.
For more information on debugging aids, see “Debugging Aids” on page 9-21.
End-Of-Test Solution
The end-of-test solution is built on top of the general objection mechanism.
You can extend objection_kind to add other kinds. For example, you can add soft reset as follows:
extend objection_kind: [SOFT_RESET_DONE];
• Objection counter: The current number of objections in the unit itself, regardless of its subunits. If
there is no objection, the counter is 0.
• Objection total: The total number of objections in the tree that includes both the unit itself and all of
its subunits.
All of the methods of the API (all of them under any_unit) are tightly related to these two pieces of
information. For details on the methods of any_unit, see “Methods of any_unit” on page 9-18.
Method Description
Method Description
get_objection_counter(kind: Returns the objection counter for the unit, that is, the
objection_kind): int current number of objections to objection_kind in the unit
itself, regardless of its subunits.
get_objection_total(kind: Returns the objection total of the unit, that is, the total
objection_kind): int number of objections to objection_kind in the tree that
includes both the unit itself and all of its subunits.
get_objection_list(kind: Returns the list of units directly under the unit (not
objection_kind): list of any_unit including it) for which the objection total is not zero.
Example
Counter : 1
Unit A
Total : 8
Counter : 5
Unit B
Total : 5
Counter : 0
Unit C
Total : 0
Counter : 0
Unit D
Total : 2
Counter : 1
Unit E
Total : 1
Counter : 0
Unit F
Total : 0
Counter : 1
Unit G
Total : 1
In the example shown in Figure 9-3, calling get_objection_list() on Unit A returns a list consisting of
Unit B and Unit D. This method is useful if your logic depends on knowing which of the immediate
descendents is responsible for rejecting a proposed status such as stopping the run.
Note In this example, for Unit A, get_objection_list() returns a list of two units, whereas
get_objection_total() returns a count of 8.
Notes
• As rerun() calls quit(), it has the same effect as quit() on the objection mechanism. rerun() also
calls run(), restarting all initially started TCMs. Therefore, if the unit called raise_objection() in
some started TCM (or in run()), it will do it again after rerun(). For this reason, we recommend
raising and dropping objections only during run() or later.
• It could happen that there is no objection to end of test during reset because of the effect of the quit()
method mentioned above. This will only happen if all sequences are reset. In that case, to avoid
stopping the simulation, the unit responsible for reset must raise an objection and drop the objection
only after the sequences restart.
• show objection
Syntax:
show objection [-kind=objection_kind]
This command shows the current objection counter and total for each of the units, for
objection_kind (default: TEST_DONE).
Note If you issue this command while in the middle of the propagation process (for example, after
hitting a breakpoint on some_unit.all_objections_dropped()), you will notice that the counters of
all units between some_unit and sys are not yet decremented. This is consistent with the algorithm
described above.
• trace objection
Syntax:
trace objection [on|off]
This command turns the tracing of the objection mechanism (for all objection kinds) On and Off.
When On, the following things are reported (via the messaging mechanism):
• Raising and dropping objections (at MEDIUM verbosity)
• Calling all_objections_dropped() (at LOW verbosity)
• Stopping the run when sys.all_objections_done(TEST_DONE) is called (at NONE verbosity)
When you want to produce this report, you must call your user-defined method. For example, you could
produce this report on DUT error as follows.
extend dut_error_struct {
write() is also {
sys.my_obj_report(TEST_DONE);
};
};
Note This report cannot be activated from stop_run(). The reason is that stop_run() calls quit(),
which drops all objections (as explained in “Behavior upon quit() and rerun()” on page 9-20).
Also, the following code is already included with the objection logic:
extend sys {
all_objections_dropped(kind: objection_kind) is {
if kind == TEST_DONE then {
start evc_util_check_objection_and_stop_run();
};
};
};
When the last objection is dropped, Specman waits until the end of the Specman tick and then stops the
run.
Notes
• If no objection is ever raised, then the objection mechanism assumes that there is another process for
stopping the test.
• You can always call stop_run() directly and stop the test immediately. This bypasses the whole
objection mechanism.
You might also want to allow for drain time after the MAIN sequence is done. You could add 20 cycles
to the example above as follows:
extend MAIN vr_xbus_sequence {
pre_body() @sys.any is first {
driver.raise_objection(TEST_DONE);
};
post_body() @sys.any is also {
wait [20] * cycle @driver.clock;
driver.drop_objection(TEST_DONE);
};
};
For example, to allow for drain time, assume that “bridge_unit” knows it must wait 40 cycles after all its
children are ready to stop. You can achieve this as follows:
extend bridge_unit {
!stopper_started: bool;
stopper() @clk is {
wait [NUM_OF_DRAIN_CYLCLES];
drop_objection(TEST_DONE);
};
};
If you want to disable that early stop, you have several options. Perhaps the simplest is to change the
watchdog method in the verification environment from:
watchdog() @someclock is {
wait [1000];
stop_run();
};
to:
watchdog() @someclock is {
sys.raise_objection(TEST_DONE);
wait [1000];
sys.drop_objection(TEST_DONE);
};
Alternatively, you could simply disable the automatic stopping mechanism in sys:
extend sys {
all_objections_dropped(kind: objection_kind) is first {
if kind == TEST_DONE then {
return;
};
};
};
End users cannot read or edit code in an encrypted file, but they can load the file like any non-encrypted
file. If you use the -visible flag of sn_encrypt.sh, you can see the fields in the Data Browser or a
waveform viewer. However, the debugger cannot read encrypted code, and so error messages do not
display the content of the line.
There are currently four Golden eVCs under development. They are described in the following sections:
For more information on the XBus eVC, see the Verisity XBus eVC User Guide (vr_xbus_evc.pdf) in the
erm_lib/vr_xbus/docs directory.
For more information on the XSerial eVC, see the Verisity XSerial eVC Overview
(vr_xserial_overview.pdf) in the erm_lib/vr_xserial/docs directory.
For more information on the XSoC eVC, see the Verisity XSoC eVC Overview (vr_xsoc_overview.pdf)
in the erm_lib/vr_xsoc/docs directory.
The eVC can perform verification at either the environment level or the system level.
Note The ATM eVC is intended primarily as an example of layering eVCs. The ATM eVC package
has two examples: one layering the ATM eVC over the XBus eVC and the other layering the ATM eVC
over the XSerial eVC.
For more information on the ATM eVC, see the Verisity ATM eVC Overview (vr_atm_overview.pdf) in
the erm_lib/vr_atm/docs directory.
See Also
• “Layering eVCs” on page 4-15
Test writers can add any constraints they want. The environment tends to be safer for users.
They do not have to read the documentation to Working in the limited zone defined by the
understand what can be controlled and how. developer, there is less chance that users will
write constraints that break the environment.
One of the primary tasks of an eVC developer is to determine the appropriate level of flexibility in the
environment. After determining the appropriate level of flexibility, the developer must configure the
eVC accordingly.
Environment size In small environments, the risk you take when creating a very flexible
environment is minimal. The code is short, and the number of users is small.
The bigger the environment, the bigger the risk of flexibility. Can you really
anticipate all constraints users might write? If the generation breaks, will it be
easy to debug the environment?
Our recommendations:
eVC purpose If the eVC is for local use, you can take the risk of it breaking from time to
time to get the advantages of a flexible environment.
On the other hand, if the eVC is for commercial use, debugging at customer
sites is much more complicated. You should minimize the likelihood of users
adding constraints that break the environment.
Field role In general, we recommend having stricter rules for architecture units and
looser rules for data items. For example, you might want to let eVC users
control agent units, but not the monitor unit parameters, which would be
propagated from the agent to which it belongs.
Note According to the above recommendations, you might expect the Golden eVCs to be very flexible.
However, they have been built in a more rigid fashion because their purpose is really to demonstrate
commercial eVCs.
If users try to constrain fields that are the output of constraints, they will get a contradiction error from
the generator.
For example, in the XSerial eVC (see “XSerial eVC” on page 10-2), clock signals are constrained in the
agent (vr_xserial_agent_u) and propagated to its monitors.
keep tx_monitor.sig_clock == value(sig_tx_clock);
Adding constraints to the monitor's sig_clock results in an error message from the generator. Users
would then understand the developer’s intent from the message and the code.
A 7-6
architecture diagrams, legend for
active agents 4-25 brief 4-5
agents full 4-26
components 4-8 architecture diagrams, legend, definition 4-26
configuration 4-9 architecture, eVC, basic 4-1
data item naming convention 4-25 at_message_verbosity
definition 4-23 recommended methodology 6-48
examples 4-25 ATM eVC 10-2
in depth 4-6 auto_quit() 5-55
internals 4-8
monitor 4-10
overview 4-7
B
sequence drivers 4-9 BFM 4-10
signals 4-9 BFM, definition 4-23
types of 4-25 BFM-driver interaction mode 5-44
all_objections_dropped() 9-19 BFM-monitor guidelines 4-25
any_env unit 2-23 BFM-monitor relations 4-24
connecting to package 2-29 BFMs 4-5
example 2-23 body() TCM 5-12
methods, user-visible 2-24
any_sequence, interface 5-87 C
any_sequence_driver, interface 5-89
any_sequence_item, interface 5-86 checker 4-11
any_unit checking
methods 9-18 eVC standardization, developer comments
methods, main 9-16 7-9
any_unit.short_name_style() 6-43 clocks 4-6
architecture clusters, file 3-7
eVC standardization, developer comments color
do item flow, pull mode, using get_next_item() eVC architecture, typical 4-1
5-99 eVC example
do item flow, pull mode, using try_next_item() combining eVCs 4-14
5-100 complex 4-12
do item flow, push mode 5-98 simple 4-2
do subsequence flow 5-97 eVC standardization 7-1
documentation checking 7-9
eVC standardization, developer comments coverage 7-10
7-13 developer comments
drop_objection() 9-18 architecture 7-6
DUT, definition 4-23 documentation 7-13
DUT, example 4-2 end of test 7-17
general deliverables 7-15
E messaging 7-12
monitor 7-13
e directory 2-8 name space 7-2
eDoc 8-21 other possible sections 7-17
eDoc window 8-22 packaging 7-2
output 8-23 reset 7-6
report 8-23 sequences 7-10
report input 8-23 visualization 7-16
using 8-21 reset 7-7
eDoc window 8-22 evc/docs/ 3-3
encryption 9-25 evc/e/ 3-2
end of test 9-15, 9-22 evc/examples/ 3-3
eVC standardization, developer comments evc_util package 2-4
7-17 eVCs 1-3
handling defined 1-3
complex 9-24 plug-and-play 1-5
examples 9-23 reuse requirements 1-5
requirements 9-16 VEs, versus 1-4
via sequences 9-23 eVCs, golden 10-1
simple 9-23 ATM eVC 10-2
introduction 9-15 XBus eVC 10-1
solution 9-17 XSerial eVC 10-2
end-of-test XSoc eVC 10-2
basic 9-23 events 4-6
end-to-end scoreboards 9-13
enumerated logical names 9-7
env, definition 4-23
F
eRM compliance checks 7-1 FAQs, modeling 4-22
eRM release library 1-2 file names, package-relative, using 2-10
erm_lib 1-2 files
eVC architecture, basic 4-1 cyclic dependencies 3-8
e Reuse Methodology Index-3
Index